[automerger skipped] Clear mod-union tables for immune spaces in PreZygoteFork am: 0992003fec -s ours
am skip reason: Merged-In I2bd0286da765f414bc22cf55af3efb84c55ce6c2 with SHA-1 157084ac62 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/art/+/21167853
Change-Id: I07a8a72170695cc5bb117a04e53a35e7bc02f716
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.clang-format b/.clang-format
index 0d6a160..ba0bff3 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,3 +1,4 @@
+# Please keep in sync with .clang-format-java-2 where applicable.
---
BasedOnStyle: Google
---
@@ -22,3 +23,24 @@
FixNamespaceComments: true
PointerAlignment: Left
TabWidth: 2
+
+---
+
+Language: Java
+
+ColumnLimit: 100
+CommentPragmas: (NOLINT|CHECK[^ ]*):.*
+ContinuationIndentWidth: 8
+IndentWidth: 4
+JavaImportGroups:
+- android
+- androidx
+- com.android
+- dalvik
+- libcore
+- com
+- junit
+- net
+- org
+- java
+- javax
diff --git a/.clang-format-java-2 b/.clang-format-java-2
new file mode 100644
index 0000000..956bdcd
--- /dev/null
+++ b/.clang-format-java-2
@@ -0,0 +1,48 @@
+# Variant of .clang-format but with 2 space indent for Java files, for use in
+# subdirectories with that style.
+# Please keep in sync with .clang-format where applicable.
+---
+BasedOnStyle: Google
+---
+
+Language: Cpp
+
+AlignConsecutiveMacros: AcrossComments
+AllowShortBlocksOnASingleLine: Empty
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AttributeMacros: ['__', 'NO_RETURN']
+BinPackArguments: false
+BinPackParameters: false
+BreakConstructorInitializers: BeforeColon
+BreakBeforeTernaryOperators: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+FixNamespaceComments: true
+PointerAlignment: Left
+TabWidth: 2
+
+---
+
+Language: Java
+
+ColumnLimit: 100
+CommentPragmas: (NOLINT|CHECK[^ ]*):.*
+ContinuationIndentWidth: 4
+IndentWidth: 2
+JavaImportGroups:
+- android
+- androidx
+- com.android
+- dalvik
+- libcore
+- com
+- junit
+- net
+- org
+- java
+- javax
diff --git a/Android.mk b/Android.mk
index 73292aa..d560276 100644
--- a/Android.mk
+++ b/Android.mk
@@ -380,76 +380,6 @@
art_apex_manifest_file :=
-#######################
-# Fake packages for ART
-
-# The art-runtime package depends on the core ART libraries and binaries. It exists so we can
-# manipulate the set of things shipped, e.g., add debug versions and so on.
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := art-runtime
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-
-# Reference the libraries and binaries in the appropriate APEX module, because
-# they don't have platform variants. However if
-# ART_MODULE_BUILD_FROM_SOURCE isn't true then the APEX
-# modules are disabled, so Soong won't apply the APEX mutators to them, and
-# then they are available with their plain names.
-ifeq (true,$(ART_MODULE_BUILD_FROM_SOURCE))
- art_module_lib = $(1).com.android.art
- art_module_debug_lib = $(1).com.android.art.debug
-else
- art_module_lib = $(1)
- art_module_debug_lib = $(1)
-endif
-
-# Base requirements.
-LOCAL_REQUIRED_MODULES := \
- $(call art_module_lib,dalvikvm) \
- $(call art_module_lib,dex2oat) \
- $(call art_module_lib,dexoptanalyzer) \
- $(call art_module_lib,libart) \
- $(call art_module_lib,libart-compiler) \
- $(call art_module_lib,libopenjdkjvm) \
- $(call art_module_lib,libopenjdkjvmti) \
- $(call art_module_lib,odrefresh) \
- $(call art_module_lib,profman) \
- $(call art_module_lib,libadbconnection) \
- $(call art_module_lib,libperfetto_hprof) \
-
-# Potentially add in debug variants:
-#
-# * We will never add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = false.
-# * We will always add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = true.
-# * Otherwise, we will add them by default to eng builds.
-art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)
-ifneq (false,$(art_target_include_debug_build))
-ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
- art_target_include_debug_build := true
-endif
-ifeq (true,$(art_target_include_debug_build))
-LOCAL_REQUIRED_MODULES += \
- $(call art_module_debug_lib,dex2oatd) \
- $(call art_module_debug_lib,dexoptanalyzerd) \
- $(call art_module_debug_lib,libartd) \
- $(call art_module_debug_lib,libartd-compiler) \
- $(call art_module_debug_lib,libopenjdkd) \
- $(call art_module_debug_lib,libopenjdkjvmd) \
- $(call art_module_debug_lib,libopenjdkjvmtid) \
- $(call art_module_debug_lib,profmand) \
- $(call art_module_debug_lib,libadbconnectiond) \
- $(call art_module_debug_lib,libperfetto_hprofd) \
-
-endif
-endif
-
-art_module_lib :=
-art_module_debug_lib :=
-
-include $(BUILD_PHONY_PACKAGE)
-
####################################################################################################
# Fake packages to ensure generation of libopenjdkd when one builds with mm/mmm/mmma.
#
@@ -521,7 +451,6 @@
lib/libart-disassembler.so \
lib/libartpalette.so \
lib/libart.so \
- lib/libbacktrace.so \
lib/libdexfile.so \
lib/libdt_fd_forward.so \
lib/libdt_socket.so \
@@ -542,7 +471,6 @@
lib/libprofile.so \
lib/libsigchain.so \
lib/libunwindstack.so \
- lib/libziparchive.so \
lib64/libadbconnection.so \
lib64/libandroidio.so \
lib64/libartbase.so \
@@ -551,7 +479,6 @@
lib64/libart-disassembler.so \
lib64/libartpalette.so \
lib64/libart.so \
- lib64/libbacktrace.so \
lib64/libdexfile.so \
lib64/libdt_fd_forward.so \
lib64/libdt_socket.so \
@@ -572,7 +499,6 @@
lib64/libprofile.so \
lib64/libsigchain.so \
lib64/libunwindstack.so \
- lib64/libziparchive.so \
PRIVATE_RUNTIME_APEX_DEPENDENCY_FILES := \
bin/linker \
@@ -652,6 +578,13 @@
#
# TODO(b/129332183): Remove this when Golem has full support for the
# ART APEX.
+#
+# TODO(b/129332183): This approach is flawed: We mix DSOs from prebuilt APEXes
+# and prebuilts/runtime/mainline/platform/impl with source built ones, and both
+# may depend on the same DSOs, and some of them don't have stable ABIs.
+# libbase.so in particular is such a problematic dependency. When those
+# dependencies eventually don't work anymore we don't have much choice but to
+# update all prebuilts.
.PHONY: standalone-apex-files
standalone-apex-files: deapexer \
$(RELEASE_ART_APEX) \
@@ -667,16 +600,11 @@
# Also, platform libraries are installed in prebuilts, so copy them over.
$(call extract-from-apex,$(RUNTIME_APEX),\
$(PRIVATE_RUNTIME_APEX_DEPENDENCY_FILES)) && \
- for libdir in $(TARGET_OUT)/lib $(TARGET_OUT)/lib64; do \
- if [ -d $$libdir/bionic ]; then \
- mv -f $$libdir/bionic/*.so $$libdir; \
- fi || exit 1; \
- done && \
- for libdir in $(TARGET_OUT)/lib $(TARGET_OUT)/lib64; do \
- if [ -d $$libdir ]; then \
- cp prebuilts/runtime/mainline/platform/impl/$(TARGET_ARCH)/*.so $$libdir; \
- fi || exit 1; \
- done
+ libdir=$(TARGET_OUT)/lib$$(expr $(TARGET_ARCH) : '.*\(64\)' || :) && \
+ if [ -d $$libdir/bionic ]; then \
+ mv -f $$libdir/bionic/*.so $$libdir; \
+ fi && \
+ cp prebuilts/runtime/mainline/platform/impl/$(TARGET_ARCH)/*.so $$libdir
$(call extract-from-apex,$(CONSCRYPT_APEX),\
$(PRIVATE_CONSCRYPT_APEX_DEPENDENCY_LIBS))
$(call extract-from-apex,$(I18N_APEX),\
@@ -709,6 +637,7 @@
$(TARGET_OUT_EXECUTABLES)/dex2oat_wrapper \
$(ART_TARGET_PLATFORM_DEPENDENCIES) \
$(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
+ $(TARGET_OUT_SHARED_LIBRARIES)/libgolemtiagent.so \
$(PRODUCT_OUT)/apex/art_boot_images/javalib/$(TARGET_ARCH)/boot.art \
standalone-apex-files
# remove debug libraries from public.libraries.txt because golem builds
@@ -726,22 +655,21 @@
ART_HOST_SHARED_LIBRARY_BENCHMARK := $(ART_HOST_OUT_SHARED_LIBRARIES)/libartbenchmark.so
build-art-host-golem: build-art-host \
$(ART_HOST_SHARED_LIBRARY_BENCHMARK) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libgolemtiagent.so \
$(HOST_OUT_EXECUTABLES)/dex2oat_wrapper
########################################################################
-# Phony target for building what go/lem requires for syncing /system to target.
-.PHONY: build-art-unbundled-golem
-art_apex_jars := $(foreach pair,$(ART_APEX_JARS), $(call word-colon,2,$(pair)))
-build-art-unbundled-golem: art-runtime linker oatdump $(art_apex_jars) conscrypt crash_dump
-
-########################################################################
# Rules for building all dependencies for tests.
.PHONY: build-art-host-gtests build-art-host-run-tests build-art-host-tests
build-art-host-gtests: build-art-host $(ART_TEST_HOST_GTEST_DEPENDENCIES)
-build-art-host-run-tests: build-art-host $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES)
+build-art-host-run-tests: build-art-host \
+ $(TEST_ART_RUN_TEST_DEPENDENCIES) \
+ $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) \
+ art-run-test-host-data \
+ art-run-test-jvm-data
build-art-host-tests: build-art-host-gtests build-art-host-run-tests
@@ -749,7 +677,10 @@
build-art-target-gtests: build-art-target $(ART_TEST_TARGET_GTEST_DEPENDENCIES)
-build-art-target-run-tests: build-art-target $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES)
+build-art-target-run-tests: build-art-target \
+ $(TEST_ART_RUN_TEST_DEPENDENCIES) \
+ $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES) \
+ art-run-test-target-data
build-art-target-tests: build-art-target-gtests build-art-target-run-tests
@@ -885,7 +816,7 @@
define create_public_sdk_dex
public_sdk_$(1)_stub := $$(call get_public_sdk_stub_dex,$(1))
$$(public_sdk_$(1)_stub): PRIVATE_MIN_SDK_VERSION := $(1)
-$$(public_sdk_$(1)_stub): $$(call resolve-prebuilt-sdk-jar-path,$(1)) $$(DX) $$(ZIP2ZIP)
+$$(public_sdk_$(1)_stub): $$(call resolve-prebuilt-sdk-jar-path,$(1)) $$(D8) $$(ZIP2ZIP)
$$(transform-classes.jar-to-dex)
$$(call declare-1p-target,$$(public_sdk_$(1)_stub),art)
diff --git a/OWNERS b/OWNERS
index 06c7d08..cfaa94b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,16 +4,18 @@
jiakaiz@google.com
lokeshgidra@google.com
mast@google.com
-miguelaranda@google.com
-mingaleev@google.com
mythria@google.com
ngeoffray@google.com
+rpl@google.com
+solanes@google.com
+vmarko@google.com
+
+# Core Libraries.
+miguelaranda@google.com
+mingaleev@google.com
nikitai@google.com
oth@google.com
prb@google.com
-rpl@google.com
skvadrik@google.com
-solanes@google.com
sorinbasca@google.com
vichang@google.com
-vmarko@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index b8ee3c5..c5b3c9c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,11 +7,53 @@
# so we don't need the custom runtests script and this check.
check_libnativebridge_test_field = libnativebridge/tests/preupload_check_test_tag.sh ${PREUPLOAD_COMMIT_MESSAGE} ${PREUPLOAD_FILES}
+check_expectation_jsons = tools/check_presubmit_json_expectations.sh ${REPO_ROOT} ${PREUPLOAD_FILES}
+
[Builtin Hooks]
-cpplint = true
bpfmt = true
+clang_format = true
+cpplint = true
gofmt = true
[Builtin Hooks Options]
+# Enable clang-format in all directories except test/, because there are many
+# test Java files with 2 space indent which won't be handled well if they
+# change. Unfortunately there is no way to exclude a directory for a builtin
+# hook.
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file
+ adbconnection/
+ artd/
+ benchmark/
+ build/
+ cmdline/
+ compiler/
+ dalvikvm/
+ dex2oat/
+ dexdump/
+ dexlayout/
+ dexlist/
+ dexoptanalyzer/
+ disassembler/
+ dt_fd_forward/
+ imgdiag/
+ libartbase/
+ libartpalette/
+ libartservice/
+ libarttools/
+ libdexfile/
+ libelffile/
+ libnativebridge/
+ libnativeloader/
+ libprofile/
+ oatdump/
+ odrefresh/
+ openjdkjvm/
+ openjdkjvmti/
+ perfetto_hprof/
+ profman/
+ runtime/
+ sigchainlib/
+ simulator/
+ tools/
# Cpplint prints nothing unless there were errors.
cpplint = --quiet ${PREUPLOAD_FILES}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index ec814ef..d01c739 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -71,6 +71,9 @@
"name": "art-run-test-022-interface[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-024-illegal-access[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-025-access-controller[com.google.android.art.apex]"
},
{
@@ -86,6 +89,9 @@
"name": "art-run-test-029-assert[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-032-concrete-sub[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-033-class-init-deadlock[com.google.android.art.apex]"
},
{
@@ -107,6 +113,9 @@
"name": "art-run-test-041-narrowing[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-042-new-instance[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-043-privates[com.google.android.art.apex]"
},
{
@@ -155,6 +164,9 @@
"name": "art-run-test-067-preemptive-unpark[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-069-field-type[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-070-nio-buffer[com.google.android.art.apex]"
},
{
@@ -164,12 +176,21 @@
"name": "art-run-test-072-reachability-fence[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-073-mismatched-field[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-074-gc-thrash[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-075-verification-error[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-076-boolean-put[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-077-method-override[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-078-polymorphic-virtual[com.google.android.art.apex]"
},
{
@@ -305,15 +326,9 @@
"name": "art-run-test-143-string-value[com.google.android.art.apex]"
},
{
- "name": "art-run-test-144-static-field-sigquit[com.google.android.art.apex]"
- },
- {
"name": "art-run-test-145-alloc-tracking-stress[com.google.android.art.apex]"
},
{
- "name": "art-run-test-151-OpenFileLimit[com.google.android.art.apex]"
- },
- {
"name": "art-run-test-152-dead-large-object[com.google.android.art.apex]"
},
{
@@ -350,6 +365,9 @@
"name": "art-run-test-176-app-image-string[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-182-method-linking[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-1960-checker-bounds-codegen[com.google.android.art.apex]"
},
{
@@ -401,10 +419,19 @@
"name": "art-run-test-2042-checker-dce-always-throw[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-2042-reference-processing[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2043-reference-pauses[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2044-get-stack-traces[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-2231-checker-heap-poisoning[com.google.android.art.apex]"
},
{
- "name": "art-run-test-2232-write-metrics-to-log[com.google.android.art.apex]"
+ "name": "art-run-test-2233-checker-remove-loop-suspend-check[com.google.android.art.apex]"
},
{
"name": "art-run-test-2234-checker-remove-entry-suspendcheck[com.google.android.art.apex]"
@@ -413,6 +440,18 @@
"name": "art-run-test-2236-JdkUnsafeGetLong-regression[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-2241-checker-inline-try-catch[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2242-checker-lse-acquire-release-operations[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2243-checker-not-inline-into-throw[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2244-checker-remove-try-boundary[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-300-package-override[com.google.android.art.apex]"
},
{
@@ -800,9 +839,15 @@
"name": "art-run-test-536-checker-intrinsic-optimization[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-536-checker-needs-access-check[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-537-checker-arraycopy[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-537-checker-inline-and-unverified[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-537-checker-jump-over-jump[com.google.android.art.apex]"
},
{
@@ -1100,6 +1145,9 @@
"name": "art-run-test-662-regression-alias[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-663-checker-select-generator[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-665-checker-simd-zero[com.google.android.art.apex]"
},
{
@@ -1223,16 +1271,42 @@
"name": "art-run-test-835-b216762268[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-838-override[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-839-clinit-throw[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-841-defaults[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-843-default-interface[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-963-default-range-smali[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-965-default-verify[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-967-default-ame[com.google.android.art.apex]"
+ },
+ {
"name": "art_libnativebridge_cts_tests[com.google.android.art.apex]"
},
{
+ "name": "art_standalone_artd_tests[com.google.android.art.apex]"
+ },
+ {
"name": "art_standalone_cmdline_tests[com.google.android.art.apex]"
},
{
- "name": "art_standalone_compiler_tests[com.google.android.art.apex]"
+ "name": "art_standalone_compiler_tests[com.google.android.art.apex]",
+ "options": [
+ {
+ "exclude-filter": "JniCompilerTest*"
+ }
+ ]
},
{
"name": "art_standalone_dex2oat_tests[com.google.android.art.apex]"
@@ -1271,10 +1345,10 @@
"name": "art_standalone_oatdump_tests[com.google.android.art.apex]"
},
{
- "name": "art_standalone_profman_tests[com.google.android.art.apex]"
+ "name": "art_standalone_odrefresh_tests[com.google.android.art.apex]"
},
{
- "name": "art_standalone_runtime_compiler_tests[com.google.android.art.apex]"
+ "name": "art_standalone_profman_tests[com.google.android.art.apex]"
},
{
"name": "art_standalone_runtime_tests[com.google.android.art.apex]"
@@ -1291,16 +1365,19 @@
],
"presubmit": [
{
- "name": "CtsJdwpTestCases"
+ "name": "ArtServiceTests"
},
{
"name": "BootImageProfileTest"
},
{
- "name": "ArtServiceTests"
+ "name": "ComposHostTestCases"
},
{
- "name": "ComposHostTestCases"
+ "name": "CtsJdwpTestCases"
+ },
+ {
+ "name": "art-apex-update-rollback"
},
{
"name": "art_standalone_dexpreopt_tests"
@@ -1372,6 +1449,9 @@
"name": "art-run-test-022-interface"
},
{
+ "name": "art-run-test-024-illegal-access"
+ },
+ {
"name": "art-run-test-025-access-controller"
},
{
@@ -1387,6 +1467,9 @@
"name": "art-run-test-029-assert"
},
{
+ "name": "art-run-test-032-concrete-sub"
+ },
+ {
"name": "art-run-test-033-class-init-deadlock"
},
{
@@ -1408,6 +1491,9 @@
"name": "art-run-test-041-narrowing"
},
{
+ "name": "art-run-test-042-new-instance"
+ },
+ {
"name": "art-run-test-043-privates"
},
{
@@ -1456,6 +1542,9 @@
"name": "art-run-test-067-preemptive-unpark"
},
{
+ "name": "art-run-test-069-field-type"
+ },
+ {
"name": "art-run-test-070-nio-buffer"
},
{
@@ -1465,12 +1554,21 @@
"name": "art-run-test-072-reachability-fence"
},
{
+ "name": "art-run-test-073-mismatched-field"
+ },
+ {
"name": "art-run-test-074-gc-thrash"
},
{
+ "name": "art-run-test-075-verification-error"
+ },
+ {
"name": "art-run-test-076-boolean-put"
},
{
+ "name": "art-run-test-077-method-override"
+ },
+ {
"name": "art-run-test-078-polymorphic-virtual"
},
{
@@ -1606,15 +1704,9 @@
"name": "art-run-test-143-string-value"
},
{
- "name": "art-run-test-144-static-field-sigquit"
- },
- {
"name": "art-run-test-145-alloc-tracking-stress"
},
{
- "name": "art-run-test-151-OpenFileLimit"
- },
- {
"name": "art-run-test-152-dead-large-object"
},
{
@@ -1651,6 +1743,9 @@
"name": "art-run-test-176-app-image-string"
},
{
+ "name": "art-run-test-182-method-linking"
+ },
+ {
"name": "art-run-test-1960-checker-bounds-codegen"
},
{
@@ -1702,10 +1797,19 @@
"name": "art-run-test-2042-checker-dce-always-throw"
},
{
+ "name": "art-run-test-2042-reference-processing"
+ },
+ {
+ "name": "art-run-test-2043-reference-pauses"
+ },
+ {
+ "name": "art-run-test-2044-get-stack-traces"
+ },
+ {
"name": "art-run-test-2231-checker-heap-poisoning"
},
{
- "name": "art-run-test-2232-write-metrics-to-log"
+ "name": "art-run-test-2233-checker-remove-loop-suspend-check"
},
{
"name": "art-run-test-2234-checker-remove-entry-suspendcheck"
@@ -1714,6 +1818,18 @@
"name": "art-run-test-2236-JdkUnsafeGetLong-regression"
},
{
+ "name": "art-run-test-2241-checker-inline-try-catch"
+ },
+ {
+ "name": "art-run-test-2242-checker-lse-acquire-release-operations"
+ },
+ {
+ "name": "art-run-test-2243-checker-not-inline-into-throw"
+ },
+ {
+ "name": "art-run-test-2244-checker-remove-try-boundary"
+ },
+ {
"name": "art-run-test-300-package-override"
},
{
@@ -2101,9 +2217,15 @@
"name": "art-run-test-536-checker-intrinsic-optimization"
},
{
+ "name": "art-run-test-536-checker-needs-access-check"
+ },
+ {
"name": "art-run-test-537-checker-arraycopy"
},
{
+ "name": "art-run-test-537-checker-inline-and-unverified"
+ },
+ {
"name": "art-run-test-537-checker-jump-over-jump"
},
{
@@ -2401,6 +2523,9 @@
"name": "art-run-test-662-regression-alias"
},
{
+ "name": "art-run-test-663-checker-select-generator"
+ },
+ {
"name": "art-run-test-665-checker-simd-zero"
},
{
@@ -2524,12 +2649,33 @@
"name": "art-run-test-835-b216762268"
},
{
+ "name": "art-run-test-838-override"
+ },
+ {
+ "name": "art-run-test-839-clinit-throw"
+ },
+ {
+ "name": "art-run-test-841-defaults"
+ },
+ {
+ "name": "art-run-test-843-default-interface"
+ },
+ {
"name": "art-run-test-963-default-range-smali"
},
{
+ "name": "art-run-test-965-default-verify"
+ },
+ {
+ "name": "art-run-test-967-default-ame"
+ },
+ {
"name": "art_libnativebridge_cts_tests"
},
{
+ "name": "art_standalone_artd_tests"
+ },
+ {
"name": "art_standalone_cmdline_tests"
},
{
@@ -2578,7 +2724,1371 @@
"name": "art_standalone_profman_tests"
},
{
- "name": "art_standalone_runtime_compiler_tests"
+ "name": "art_standalone_runtime_tests"
+ },
+ {
+ "name": "art_standalone_sigchain_tests"
+ },
+ {
+ "name": "libnativeloader_e2e_tests"
+ },
+ {
+ "name": "libnativeloader_test"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "ArtServiceTests"
+ },
+ {
+ "name": "ComposHostTestCases"
+ },
+ {
+ "name": "art-apex-update-rollback"
+ },
+ {
+ "name": "art_standalone_dexpreopt_tests"
+ },
+ {
+ "name": "art-run-test-001-HelloWorld"
+ },
+ {
+ "name": "art-run-test-001-Main"
+ },
+ {
+ "name": "art-run-test-002-sleep"
+ },
+ {
+ "name": "art-run-test-004-InterfaceTest"
+ },
+ {
+ "name": "art-run-test-004-NativeAllocations"
+ },
+ {
+ "name": "art-run-test-004-checker-UnsafeTest18"
+ },
+ {
+ "name": "art-run-test-006-args"
+ },
+ {
+ "name": "art-run-test-007-count10"
+ },
+ {
+ "name": "art-run-test-009-instanceof"
+ },
+ {
+ "name": "art-run-test-010-instance"
+ },
+ {
+ "name": "art-run-test-011-array-copy"
+ },
+ {
+ "name": "art-run-test-012-math"
+ },
+ {
+ "name": "art-run-test-013-math2"
+ },
+ {
+ "name": "art-run-test-014-math3"
+ },
+ {
+ "name": "art-run-test-015-switch"
+ },
+ {
+ "name": "art-run-test-016-intern"
+ },
+ {
+ "name": "art-run-test-017-float"
+ },
+ {
+ "name": "art-run-test-018-stack-overflow"
+ },
+ {
+ "name": "art-run-test-019-wrong-array-type"
+ },
+ {
+ "name": "art-run-test-020-string"
+ },
+ {
+ "name": "art-run-test-021-string2"
+ },
+ {
+ "name": "art-run-test-022-interface"
+ },
+ {
+ "name": "art-run-test-024-illegal-access"
+ },
+ {
+ "name": "art-run-test-025-access-controller"
+ },
+ {
+ "name": "art-run-test-026-access"
+ },
+ {
+ "name": "art-run-test-027-arithmetic"
+ },
+ {
+ "name": "art-run-test-028-array-write"
+ },
+ {
+ "name": "art-run-test-029-assert"
+ },
+ {
+ "name": "art-run-test-032-concrete-sub"
+ },
+ {
+ "name": "art-run-test-033-class-init-deadlock"
+ },
+ {
+ "name": "art-run-test-035-enum"
+ },
+ {
+ "name": "art-run-test-036-finalizer"
+ },
+ {
+ "name": "art-run-test-037-inherit"
+ },
+ {
+ "name": "art-run-test-039-join-main"
+ },
+ {
+ "name": "art-run-test-040-miranda"
+ },
+ {
+ "name": "art-run-test-041-narrowing"
+ },
+ {
+ "name": "art-run-test-042-new-instance"
+ },
+ {
+ "name": "art-run-test-043-privates"
+ },
+ {
+ "name": "art-run-test-045-reflect-array"
+ },
+ {
+ "name": "art-run-test-046-reflect"
+ },
+ {
+ "name": "art-run-test-047-returns"
+ },
+ {
+ "name": "art-run-test-048-reflect-v8"
+ },
+ {
+ "name": "art-run-test-049-show-object"
+ },
+ {
+ "name": "art-run-test-050-sync-test"
+ },
+ {
+ "name": "art-run-test-052-verifier-fun"
+ },
+ {
+ "name": "art-run-test-053-wait-some"
+ },
+ {
+ "name": "art-run-test-055-enum-performance"
+ },
+ {
+ "name": "art-run-test-058-enum-order"
+ },
+ {
+ "name": "art-run-test-059-finalizer-throw"
+ },
+ {
+ "name": "art-run-test-061-out-of-memory"
+ },
+ {
+ "name": "art-run-test-062-character-encodings"
+ },
+ {
+ "name": "art-run-test-063-process-manager"
+ },
+ {
+ "name": "art-run-test-067-preemptive-unpark"
+ },
+ {
+ "name": "art-run-test-069-field-type"
+ },
+ {
+ "name": "art-run-test-070-nio-buffer"
+ },
+ {
+ "name": "art-run-test-072-precise-gc"
+ },
+ {
+ "name": "art-run-test-072-reachability-fence"
+ },
+ {
+ "name": "art-run-test-073-mismatched-field"
+ },
+ {
+ "name": "art-run-test-074-gc-thrash"
+ },
+ {
+ "name": "art-run-test-075-verification-error"
+ },
+ {
+ "name": "art-run-test-076-boolean-put"
+ },
+ {
+ "name": "art-run-test-077-method-override"
+ },
+ {
+ "name": "art-run-test-078-polymorphic-virtual"
+ },
+ {
+ "name": "art-run-test-079-phantom"
+ },
+ {
+ "name": "art-run-test-080-oom-fragmentation"
+ },
+ {
+ "name": "art-run-test-080-oom-throw"
+ },
+ {
+ "name": "art-run-test-080-oom-throw-with-finalizer"
+ },
+ {
+ "name": "art-run-test-081-hot-exceptions"
+ },
+ {
+ "name": "art-run-test-082-inline-execute"
+ },
+ {
+ "name": "art-run-test-083-compiler-regressions"
+ },
+ {
+ "name": "art-run-test-084-class-init"
+ },
+ {
+ "name": "art-run-test-090-loop-formation"
+ },
+ {
+ "name": "art-run-test-092-locale"
+ },
+ {
+ "name": "art-run-test-093-serialization"
+ },
+ {
+ "name": "art-run-test-094-pattern"
+ },
+ {
+ "name": "art-run-test-095-switch-MAX_INT"
+ },
+ {
+ "name": "art-run-test-096-array-copy-concurrent-gc"
+ },
+ {
+ "name": "art-run-test-099-vmdebug"
+ },
+ {
+ "name": "art-run-test-100-reflect2"
+ },
+ {
+ "name": "art-run-test-1000-non-moving-space-stress"
+ },
+ {
+ "name": "art-run-test-1004-checker-volatile-ref-load"
+ },
+ {
+ "name": "art-run-test-101-fibonacci"
+ },
+ {
+ "name": "art-run-test-102-concurrent-gc"
+ },
+ {
+ "name": "art-run-test-103-string-append"
+ },
+ {
+ "name": "art-run-test-104-growth-limit"
+ },
+ {
+ "name": "art-run-test-105-invoke"
+ },
+ {
+ "name": "art-run-test-106-exceptions2"
+ },
+ {
+ "name": "art-run-test-107-int-math2"
+ },
+ {
+ "name": "art-run-test-108-check-cast"
+ },
+ {
+ "name": "art-run-test-109-suspend-check"
+ },
+ {
+ "name": "art-run-test-110-field-access"
+ },
+ {
+ "name": "art-run-test-112-double-math"
+ },
+ {
+ "name": "art-run-test-114-ParallelGC"
+ },
+ {
+ "name": "art-run-test-120-hashcode"
+ },
+ {
+ "name": "art-run-test-121-simple-suspend-check"
+ },
+ {
+ "name": "art-run-test-122-npe"
+ },
+ {
+ "name": "art-run-test-123-compiler-regressions-mt"
+ },
+ {
+ "name": "art-run-test-123-inline-execute2"
+ },
+ {
+ "name": "art-run-test-125-gc-and-classloading"
+ },
+ {
+ "name": "art-run-test-128-reg-spill-on-implicit-nullcheck"
+ },
+ {
+ "name": "art-run-test-129-ThreadGetId"
+ },
+ {
+ "name": "art-run-test-132-daemon-locks-shutdown"
+ },
+ {
+ "name": "art-run-test-133-static-invoke-super"
+ },
+ {
+ "name": "art-run-test-1338-gc-no-los"
+ },
+ {
+ "name": "art-run-test-140-dce-regression"
+ },
+ {
+ "name": "art-run-test-140-field-packing"
+ },
+ {
+ "name": "art-run-test-143-string-value"
+ },
+ {
+ "name": "art-run-test-145-alloc-tracking-stress"
+ },
+ {
+ "name": "art-run-test-152-dead-large-object"
+ },
+ {
+ "name": "art-run-test-153-reference-stress"
+ },
+ {
+ "name": "art-run-test-156-register-dex-file-multi-loader"
+ },
+ {
+ "name": "art-run-test-159-app-image-fields"
+ },
+ {
+ "name": "art-run-test-160-read-barrier-stress"
+ },
+ {
+ "name": "art-run-test-163-app-image-methods"
+ },
+ {
+ "name": "art-run-test-165-lock-owner-proxy"
+ },
+ {
+ "name": "art-run-test-168-vmstack-annotated"
+ },
+ {
+ "name": "art-run-test-170-interface-init"
+ },
+ {
+ "name": "art-run-test-174-escaping-instance-of-bad-class"
+ },
+ {
+ "name": "art-run-test-175-alloc-big-bignums"
+ },
+ {
+ "name": "art-run-test-176-app-image-string"
+ },
+ {
+ "name": "art-run-test-182-method-linking"
+ },
+ {
+ "name": "art-run-test-1960-checker-bounds-codegen"
+ },
+ {
+ "name": "art-run-test-1961-checker-loop-vectorizer"
+ },
+ {
+ "name": "art-run-test-201-built-in-except-detail-messages"
+ },
+ {
+ "name": "art-run-test-2019-constantcalculationsinking"
+ },
+ {
+ "name": "art-run-test-202-thread-oome"
+ },
+ {
+ "name": "art-run-test-2020-InvokeVirtual-Inlining"
+ },
+ {
+ "name": "art-run-test-2021-InvokeStatic-Inlining"
+ },
+ {
+ "name": "art-run-test-2022-Invariantloops"
+ },
+ {
+ "name": "art-run-test-2023-InvariantLoops_typecast"
+ },
+ {
+ "name": "art-run-test-2024-InvariantNegativeLoop"
+ },
+ {
+ "name": "art-run-test-2025-ChangedArrayValue"
+ },
+ {
+ "name": "art-run-test-2026-DifferentMemoryLSCouples"
+ },
+ {
+ "name": "art-run-test-2027-TwiceTheSameMemoryCouple"
+ },
+ {
+ "name": "art-run-test-2028-MultiBackward"
+ },
+ {
+ "name": "art-run-test-2029-contended-monitors"
+ },
+ {
+ "name": "art-run-test-2030-long-running-child"
+ },
+ {
+ "name": "art-run-test-2042-checker-dce-always-throw"
+ },
+ {
+ "name": "art-run-test-2042-reference-processing"
+ },
+ {
+ "name": "art-run-test-2043-reference-pauses"
+ },
+ {
+ "name": "art-run-test-2044-get-stack-traces"
+ },
+ {
+ "name": "art-run-test-2231-checker-heap-poisoning"
+ },
+ {
+ "name": "art-run-test-2233-checker-remove-loop-suspend-check"
+ },
+ {
+ "name": "art-run-test-2234-checker-remove-entry-suspendcheck"
+ },
+ {
+ "name": "art-run-test-2236-JdkUnsafeGetLong-regression"
+ },
+ {
+ "name": "art-run-test-2241-checker-inline-try-catch"
+ },
+ {
+ "name": "art-run-test-2242-checker-lse-acquire-release-operations"
+ },
+ {
+ "name": "art-run-test-2243-checker-not-inline-into-throw"
+ },
+ {
+ "name": "art-run-test-2244-checker-remove-try-boundary"
+ },
+ {
+ "name": "art-run-test-300-package-override"
+ },
+ {
+ "name": "art-run-test-301-abstract-protected"
+ },
+ {
+ "name": "art-run-test-302-float-conversion"
+ },
+ {
+ "name": "art-run-test-304-method-tracing"
+ },
+ {
+ "name": "art-run-test-401-optimizing-compiler"
+ },
+ {
+ "name": "art-run-test-402-optimizing-control-flow"
+ },
+ {
+ "name": "art-run-test-403-optimizing-long"
+ },
+ {
+ "name": "art-run-test-404-optimizing-allocator"
+ },
+ {
+ "name": "art-run-test-405-optimizing-long-allocator"
+ },
+ {
+ "name": "art-run-test-406-fields"
+ },
+ {
+ "name": "art-run-test-407-arrays"
+ },
+ {
+ "name": "art-run-test-408-move-bug"
+ },
+ {
+ "name": "art-run-test-409-materialized-condition"
+ },
+ {
+ "name": "art-run-test-410-floats"
+ },
+ {
+ "name": "art-run-test-411-checker-hdiv-hrem-const"
+ },
+ {
+ "name": "art-run-test-411-checker-hdiv-hrem-pow2"
+ },
+ {
+ "name": "art-run-test-411-checker-instruct-simplifier-hrem"
+ },
+ {
+ "name": "art-run-test-411-optimizing-arith"
+ },
+ {
+ "name": "art-run-test-413-regalloc-regression"
+ },
+ {
+ "name": "art-run-test-414-static-fields"
+ },
+ {
+ "name": "art-run-test-418-const-string"
+ },
+ {
+ "name": "art-run-test-419-long-parameter"
+ },
+ {
+ "name": "art-run-test-420-const-class"
+ },
+ {
+ "name": "art-run-test-421-exceptions"
+ },
+ {
+ "name": "art-run-test-421-large-frame"
+ },
+ {
+ "name": "art-run-test-422-instanceof"
+ },
+ {
+ "name": "art-run-test-422-type-conversion"
+ },
+ {
+ "name": "art-run-test-423-invoke-interface"
+ },
+ {
+ "name": "art-run-test-424-checkcast"
+ },
+ {
+ "name": "art-run-test-426-monitor"
+ },
+ {
+ "name": "art-run-test-427-bitwise"
+ },
+ {
+ "name": "art-run-test-427-bounds"
+ },
+ {
+ "name": "art-run-test-429-ssa-builder"
+ },
+ {
+ "name": "art-run-test-430-live-register-slow-path"
+ },
+ {
+ "name": "art-run-test-433-gvn"
+ },
+ {
+ "name": "art-run-test-434-shifter-operand"
+ },
+ {
+ "name": "art-run-test-435-try-finally-without-catch"
+ },
+ {
+ "name": "art-run-test-436-rem-float"
+ },
+ {
+ "name": "art-run-test-436-shift-constant"
+ },
+ {
+ "name": "art-run-test-437-inline"
+ },
+ {
+ "name": "art-run-test-438-volatile"
+ },
+ {
+ "name": "art-run-test-439-npe"
+ },
+ {
+ "name": "art-run-test-439-swap-double"
+ },
+ {
+ "name": "art-run-test-440-stmp"
+ },
+ {
+ "name": "art-run-test-441-checker-inliner"
+ },
+ {
+ "name": "art-run-test-443-not-bool-inline"
+ },
+ {
+ "name": "art-run-test-444-checker-nce"
+ },
+ {
+ "name": "art-run-test-445-checker-licm"
+ },
+ {
+ "name": "art-run-test-446-checker-inliner2"
+ },
+ {
+ "name": "art-run-test-447-checker-inliner3"
+ },
+ {
+ "name": "art-run-test-449-checker-bce-rem"
+ },
+ {
+ "name": "art-run-test-450-checker-types"
+ },
+ {
+ "name": "art-run-test-451-regression-add-float"
+ },
+ {
+ "name": "art-run-test-451-spill-splot"
+ },
+ {
+ "name": "art-run-test-455-checker-gvn"
+ },
+ {
+ "name": "art-run-test-456-baseline-array-set"
+ },
+ {
+ "name": "art-run-test-458-long-to-fpu"
+ },
+ {
+ "name": "art-run-test-464-checker-inline-sharpen-calls"
+ },
+ {
+ "name": "art-run-test-465-checker-clinit-gvn"
+ },
+ {
+ "name": "art-run-test-469-condition-materialization"
+ },
+ {
+ "name": "art-run-test-470-huge-method"
+ },
+ {
+ "name": "art-run-test-471-deopt-environment"
+ },
+ {
+ "name": "art-run-test-472-type-propagation"
+ },
+ {
+ "name": "art-run-test-473-checker-inliner-constants"
+ },
+ {
+ "name": "art-run-test-473-remove-dead-block"
+ },
+ {
+ "name": "art-run-test-474-checker-boolean-input"
+ },
+ {
+ "name": "art-run-test-474-fp-sub-neg"
+ },
+ {
+ "name": "art-run-test-475-simplify-mul-zero"
+ },
+ {
+ "name": "art-run-test-476-checker-ctor-fence-redun-elim"
+ },
+ {
+ "name": "art-run-test-476-checker-ctor-memory-barrier"
+ },
+ {
+ "name": "art-run-test-476-clinit-inline-static-invoke"
+ },
+ {
+ "name": "art-run-test-477-checker-bound-type"
+ },
+ {
+ "name": "art-run-test-477-long-2-float-convers-precision"
+ },
+ {
+ "name": "art-run-test-478-checker-clinit-check-pruning"
+ },
+ {
+ "name": "art-run-test-478-checker-inline-noreturn"
+ },
+ {
+ "name": "art-run-test-478-checker-inliner-nested-loop"
+ },
+ {
+ "name": "art-run-test-479-regression-implicit-null-check"
+ },
+ {
+ "name": "art-run-test-480-checker-dead-blocks"
+ },
+ {
+ "name": "art-run-test-481-regression-phi-cond"
+ },
+ {
+ "name": "art-run-test-482-checker-loop-back-edge-use"
+ },
+ {
+ "name": "art-run-test-483-dce-block"
+ },
+ {
+ "name": "art-run-test-485-checker-dce-switch"
+ },
+ {
+ "name": "art-run-test-486-checker-must-do-null-check"
+ },
+ {
+ "name": "art-run-test-487-checker-inline-calls"
+ },
+ {
+ "name": "art-run-test-488-checker-inline-recursive-calls"
+ },
+ {
+ "name": "art-run-test-489-current-method-regression"
+ },
+ {
+ "name": "art-run-test-490-checker-inline"
+ },
+ {
+ "name": "art-run-test-491-current-method"
+ },
+ {
+ "name": "art-run-test-492-checker-inline-invoke-interface"
+ },
+ {
+ "name": "art-run-test-493-checker-inline-invoke-interface"
+ },
+ {
+ "name": "art-run-test-494-checker-instanceof-tests"
+ },
+ {
+ "name": "art-run-test-495-checker-checkcast-tests"
+ },
+ {
+ "name": "art-run-test-496-checker-inlining-class-loader"
+ },
+ {
+ "name": "art-run-test-499-bce-phi-array-length"
+ },
+ {
+ "name": "art-run-test-500-instanceof"
+ },
+ {
+ "name": "art-run-test-505-simplifier-type-propagation"
+ },
+ {
+ "name": "art-run-test-507-boolean-test"
+ },
+ {
+ "name": "art-run-test-507-referrer"
+ },
+ {
+ "name": "art-run-test-508-checker-disassembly"
+ },
+ {
+ "name": "art-run-test-508-referrer-method"
+ },
+ {
+ "name": "art-run-test-513-array-deopt"
+ },
+ {
+ "name": "art-run-test-514-shifts"
+ },
+ {
+ "name": "art-run-test-519-bound-load-class"
+ },
+ {
+ "name": "art-run-test-521-checker-array-set-null"
+ },
+ {
+ "name": "art-run-test-521-regression-integer-field-set"
+ },
+ {
+ "name": "art-run-test-524-boolean-simplifier-regression"
+ },
+ {
+ "name": "art-run-test-525-checker-arrays-fields1"
+ },
+ {
+ "name": "art-run-test-525-checker-arrays-fields2"
+ },
+ {
+ "name": "art-run-test-526-checker-caller-callee-regs"
+ },
+ {
+ "name": "art-run-test-526-long-regalloc"
+ },
+ {
+ "name": "art-run-test-527-checker-array-access-simd"
+ },
+ {
+ "name": "art-run-test-527-checker-array-access-split"
+ },
+ {
+ "name": "art-run-test-528-long-hint"
+ },
+ {
+ "name": "art-run-test-529-long-split"
+ },
+ {
+ "name": "art-run-test-530-checker-loops-try-catch"
+ },
+ {
+ "name": "art-run-test-530-checker-loops1"
+ },
+ {
+ "name": "art-run-test-530-checker-loops2"
+ },
+ {
+ "name": "art-run-test-530-checker-loops3"
+ },
+ {
+ "name": "art-run-test-530-checker-loops4"
+ },
+ {
+ "name": "art-run-test-530-checker-loops5"
+ },
+ {
+ "name": "art-run-test-530-checker-lse"
+ },
+ {
+ "name": "art-run-test-530-checker-lse-ctor-fences"
+ },
+ {
+ "name": "art-run-test-530-checker-lse-simd"
+ },
+ {
+ "name": "art-run-test-530-checker-lse-try-catch"
+ },
+ {
+ "name": "art-run-test-530-instanceof-checkcast"
+ },
+ {
+ "name": "art-run-test-532-checker-nonnull-arrayset"
+ },
+ {
+ "name": "art-run-test-534-checker-bce-deoptimization"
+ },
+ {
+ "name": "art-run-test-535-deopt-and-inlining"
+ },
+ {
+ "name": "art-run-test-536-checker-intrinsic-optimization"
+ },
+ {
+ "name": "art-run-test-536-checker-needs-access-check"
+ },
+ {
+ "name": "art-run-test-537-checker-arraycopy"
+ },
+ {
+ "name": "art-run-test-537-checker-inline-and-unverified"
+ },
+ {
+ "name": "art-run-test-537-checker-jump-over-jump"
+ },
+ {
+ "name": "art-run-test-538-checker-embed-constants"
+ },
+ {
+ "name": "art-run-test-540-checker-rtp-bug"
+ },
+ {
+ "name": "art-run-test-542-bitfield-rotates"
+ },
+ {
+ "name": "art-run-test-542-inline-trycatch"
+ },
+ {
+ "name": "art-run-test-542-unresolved-access-check"
+ },
+ {
+ "name": "art-run-test-545-tracing-and-jit"
+ },
+ {
+ "name": "art-run-test-548-checker-inlining-and-dce"
+ },
+ {
+ "name": "art-run-test-549-checker-types-merge"
+ },
+ {
+ "name": "art-run-test-550-checker-multiply-accumulate"
+ },
+ {
+ "name": "art-run-test-550-new-instance-clinit"
+ },
+ {
+ "name": "art-run-test-551-checker-clinit"
+ },
+ {
+ "name": "art-run-test-551-checker-shifter-operand"
+ },
+ {
+ "name": "art-run-test-551-implicit-null-checks"
+ },
+ {
+ "name": "art-run-test-552-checker-x86-avx2-bit-manipulation"
+ },
+ {
+ "name": "art-run-test-554-checker-rtp-checkcast"
+ },
+ {
+ "name": "art-run-test-557-checker-instruct-simplifier-ror"
+ },
+ {
+ "name": "art-run-test-558-switch"
+ },
+ {
+ "name": "art-run-test-559-bce-ssa"
+ },
+ {
+ "name": "art-run-test-559-checker-rtp-ifnotnull"
+ },
+ {
+ "name": "art-run-test-560-packed-switch"
+ },
+ {
+ "name": "art-run-test-561-divrem"
+ },
+ {
+ "name": "art-run-test-561-shared-slowpaths"
+ },
+ {
+ "name": "art-run-test-562-bce-preheader"
+ },
+ {
+ "name": "art-run-test-562-checker-no-intermediate"
+ },
+ {
+ "name": "art-run-test-563-checker-invoke-super"
+ },
+ {
+ "name": "art-run-test-564-checker-bitcount"
+ },
+ {
+ "name": "art-run-test-564-checker-inline-loop"
+ },
+ {
+ "name": "art-run-test-564-checker-negbitwise"
+ },
+ {
+ "name": "art-run-test-565-checker-condition-liveness"
+ },
+ {
+ "name": "art-run-test-566-checker-codegen-select"
+ },
+ {
+ "name": "art-run-test-567-checker-builder-intrinsics"
+ },
+ {
+ "name": "art-run-test-568-checker-onebit"
+ },
+ {
+ "name": "art-run-test-570-checker-select"
+ },
+ {
+ "name": "art-run-test-572-checker-array-get-regression"
+ },
+ {
+ "name": "art-run-test-573-checker-checkcast-regression"
+ },
+ {
+ "name": "art-run-test-576-polymorphic-inlining"
+ },
+ {
+ "name": "art-run-test-577-checker-fp2int"
+ },
+ {
+ "name": "art-run-test-578-bce-visit"
+ },
+ {
+ "name": "art-run-test-578-polymorphic-inlining"
+ },
+ {
+ "name": "art-run-test-579-inline-infinite"
+ },
+ {
+ "name": "art-run-test-580-checker-fp16"
+ },
+ {
+ "name": "art-run-test-580-checker-round"
+ },
+ {
+ "name": "art-run-test-580-checker-string-fact-intrinsics"
+ },
+ {
+ "name": "art-run-test-580-crc32"
+ },
+ {
+ "name": "art-run-test-581-checker-rtp"
+ },
+ {
+ "name": "art-run-test-582-checker-bce-length"
+ },
+ {
+ "name": "art-run-test-583-checker-zero"
+ },
+ {
+ "name": "art-run-test-584-checker-div-bool"
+ },
+ {
+ "name": "art-run-test-589-super-imt"
+ },
+ {
+ "name": "art-run-test-590-checker-arr-set-null-regression"
+ },
+ {
+ "name": "art-run-test-591-checker-regression-dead-loop"
+ },
+ {
+ "name": "art-run-test-593-checker-long-2-float-regression"
+ },
+ {
+ "name": "art-run-test-594-checker-array-alias"
+ },
+ {
+ "name": "art-run-test-594-load-string-regression"
+ },
+ {
+ "name": "art-run-test-603-checker-instanceof"
+ },
+ {
+ "name": "art-run-test-605-new-string-from-bytes"
+ },
+ {
+ "name": "art-run-test-607-daemon-stress"
+ },
+ {
+ "name": "art-run-test-609-checker-inline-interface"
+ },
+ {
+ "name": "art-run-test-609-checker-x86-bounds-check"
+ },
+ {
+ "name": "art-run-test-610-arraycopy"
+ },
+ {
+ "name": "art-run-test-611-checker-simplify-if"
+ },
+ {
+ "name": "art-run-test-614-checker-dump-constant-location"
+ },
+ {
+ "name": "art-run-test-615-checker-arm64-store-zero"
+ },
+ {
+ "name": "art-run-test-617-clinit-oome"
+ },
+ {
+ "name": "art-run-test-618-checker-induction"
+ },
+ {
+ "name": "art-run-test-619-checker-current-method"
+ },
+ {
+ "name": "art-run-test-620-checker-bce-intrinsics"
+ },
+ {
+ "name": "art-run-test-622-checker-bce-regressions"
+ },
+ {
+ "name": "art-run-test-625-checker-licm-regressions"
+ },
+ {
+ "name": "art-run-test-627-checker-unroll"
+ },
+ {
+ "name": "art-run-test-628-vdex"
+ },
+ {
+ "name": "art-run-test-631-checker-get-class"
+ },
+ {
+ "name": "art-run-test-632-checker-char-at-bounds"
+ },
+ {
+ "name": "art-run-test-635-checker-arm64-volatile-load-cc"
+ },
+ {
+ "name": "art-run-test-636-arm64-veneer-pool"
+ },
+ {
+ "name": "art-run-test-637-checker-throw-inline"
+ },
+ {
+ "name": "art-run-test-639-checker-code-sinking"
+ },
+ {
+ "name": "art-run-test-640-checker-boolean-simd"
+ },
+ {
+ "name": "art-run-test-640-checker-integer-valueof"
+ },
+ {
+ "name": "art-run-test-640-checker-simd"
+ },
+ {
+ "name": "art-run-test-641-checker-arraycopy"
+ },
+ {
+ "name": "art-run-test-641-iterations"
+ },
+ {
+ "name": "art-run-test-643-checker-bogus-ic"
+ },
+ {
+ "name": "art-run-test-645-checker-abs-simd"
+ },
+ {
+ "name": "art-run-test-646-checker-arraycopy-large-cst-pos"
+ },
+ {
+ "name": "art-run-test-646-checker-long-const-to-int"
+ },
+ {
+ "name": "art-run-test-646-checker-simd-hadd"
+ },
+ {
+ "name": "art-run-test-650-checker-inline-access-thunks"
+ },
+ {
+ "name": "art-run-test-654-checker-periodic"
+ },
+ {
+ "name": "art-run-test-655-checker-simd-arm-opt"
+ },
+ {
+ "name": "art-run-test-656-checker-simd-opt"
+ },
+ {
+ "name": "art-run-test-657-branches"
+ },
+ {
+ "name": "art-run-test-658-fp-read-barrier"
+ },
+ {
+ "name": "art-run-test-659-unpadded-array"
+ },
+ {
+ "name": "art-run-test-660-checker-sad"
+ },
+ {
+ "name": "art-run-test-660-checker-simd-sad"
+ },
+ {
+ "name": "art-run-test-661-checker-simd-reduc"
+ },
+ {
+ "name": "art-run-test-662-regression-alias"
+ },
+ {
+ "name": "art-run-test-663-checker-select-generator"
+ },
+ {
+ "name": "art-run-test-665-checker-simd-zero"
+ },
+ {
+ "name": "art-run-test-666-dex-cache-itf"
+ },
+ {
+ "name": "art-run-test-667-checker-simd-alignment"
+ },
+ {
+ "name": "art-run-test-667-out-of-bounds"
+ },
+ {
+ "name": "art-run-test-669-checker-break"
+ },
+ {
+ "name": "art-run-test-671-npe-field-opts"
+ },
+ {
+ "name": "art-run-test-672-checker-throw-method"
+ },
+ {
+ "name": "art-run-test-673-checker-throw-vmethod"
+ },
+ {
+ "name": "art-run-test-676-proxy-jit-at-first-use"
+ },
+ {
+ "name": "art-run-test-677-fsi2"
+ },
+ {
+ "name": "art-run-test-678-quickening"
+ },
+ {
+ "name": "art-run-test-684-checker-simd-dotprod"
+ },
+ {
+ "name": "art-run-test-684-select-condition"
+ },
+ {
+ "name": "art-run-test-689-multi-catch"
+ },
+ {
+ "name": "art-run-test-694-clinit-jit"
+ },
+ {
+ "name": "art-run-test-695-simplify-throws"
+ },
+ {
+ "name": "art-run-test-696-loop"
+ },
+ {
+ "name": "art-run-test-697-checker-string-append"
+ },
+ {
+ "name": "art-run-test-698-selects"
+ },
+ {
+ "name": "art-run-test-700-LoadArgRegs"
+ },
+ {
+ "name": "art-run-test-703-floating-point-div"
+ },
+ {
+ "name": "art-run-test-704-multiply-accumulate"
+ },
+ {
+ "name": "art-run-test-705-register-conflict"
+ },
+ {
+ "name": "art-run-test-711-checker-type-conversion"
+ },
+ {
+ "name": "art-run-test-713-varhandle-invokers"
+ },
+ {
+ "name": "art-run-test-718-zipfile-finalizer"
+ },
+ {
+ "name": "art-run-test-719-varhandle-concurrency"
+ },
+ {
+ "name": "art-run-test-721-osr"
+ },
+ {
+ "name": "art-run-test-726-array-store"
+ },
+ {
+ "name": "art-run-test-730-checker-inlining-super"
+ },
+ {
+ "name": "art-run-test-731-bounds-check-slow-path"
+ },
+ {
+ "name": "art-run-test-805-TooDeepClassInstanceOf"
+ },
+ {
+ "name": "art-run-test-806-TooWideClassInstanceOf"
+ },
+ {
+ "name": "art-run-test-812-recursive-default"
+ },
+ {
+ "name": "art-run-test-814-large-field-offsets"
+ },
+ {
+ "name": "art-run-test-815-invokeinterface-default"
+ },
+ {
+ "name": "art-run-test-818-clinit-nterp"
+ },
+ {
+ "name": "art-run-test-821-madvise-willneed"
+ },
+ {
+ "name": "art-run-test-828-partial-lse"
+ },
+ {
+ "name": "art-run-test-834-lse"
+ },
+ {
+ "name": "art-run-test-835-b216762268"
+ },
+ {
+ "name": "art-run-test-838-override"
+ },
+ {
+ "name": "art-run-test-839-clinit-throw"
+ },
+ {
+ "name": "art-run-test-841-defaults"
+ },
+ {
+ "name": "art-run-test-843-default-interface"
+ },
+ {
+ "name": "art-run-test-963-default-range-smali"
+ },
+ {
+ "name": "art-run-test-965-default-verify"
+ },
+ {
+ "name": "art-run-test-967-default-ame"
+ },
+ {
+ "name": "art_libnativebridge_cts_tests"
+ },
+ {
+ "name": "art_standalone_artd_tests"
+ },
+ {
+ "name": "art_standalone_cmdline_tests"
+ },
+ {
+ "name": "art_standalone_compiler_tests"
+ },
+ {
+ "name": "art_standalone_dex2oat_tests"
+ },
+ {
+ "name": "art_standalone_dexdump_tests"
+ },
+ {
+ "name": "art_standalone_dexlist_tests"
+ },
+ {
+ "name": "art_standalone_dexoptanalyzer_tests"
+ },
+ {
+ "name": "art_standalone_libartbase_tests"
+ },
+ {
+ "name": "art_standalone_libartpalette_tests"
+ },
+ {
+ "name": "art_standalone_libartservice_tests"
+ },
+ {
+ "name": "art_standalone_libarttools_tests"
+ },
+ {
+ "name": "art_standalone_libdexfile_support_tests"
+ },
+ {
+ "name": "art_standalone_libdexfile_tests"
+ },
+ {
+ "name": "art_standalone_libprofile_tests"
+ },
+ {
+ "name": "art_standalone_oatdump_tests"
+ },
+ {
+ "name": "art_standalone_odrefresh_tests"
+ },
+ {
+ "name": "art_standalone_profman_tests"
},
{
"name": "art_standalone_runtime_tests"
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index f9ebe40..239fe31 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -23,6 +23,8 @@
#include "adbconnection/client.h"
#include "android-base/endian.h"
#include "android-base/stringprintf.h"
+#include "art_field-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/file_utils.h"
#include "base/globals.h"
#include "base/logging.h"
@@ -32,6 +34,7 @@
#include "debugger.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_env_ext.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/throwable.h"
#include "nativehelper/scoped_local_ref.h"
#include "runtime-inl.h"
@@ -180,21 +183,25 @@
}
}
-static jobject CreateAdbConnectionThread(art::Thread* thr) {
- JNIEnv* env = thr->GetJniEnv();
- // Move to native state to talk with the jnienv api.
- art::ScopedThreadStateChange stsc(thr, art::ThreadState::kNative);
- ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName));
- ScopedLocalRef<jobject> thr_group(
- env,
- env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup,
- art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
- return env->NewObject(art::WellKnownClasses::java_lang_Thread,
- art::WellKnownClasses::java_lang_Thread_init,
- thr_group.get(),
- thr_name.get(),
- /*Priority=*/ 0,
- /*Daemon=*/ true);
+static art::ObjPtr<art::mirror::Object> CreateAdbConnectionThread(art::Thread* self)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::StackHandleScope<3u> hs(self);
+ art::Handle<art::mirror::String> thr_name =
+ hs.NewHandle(art::mirror::String::AllocFromModifiedUtf8(self, kAdbConnectionThreadName));
+ if (thr_name == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ art::ArtField* system_thread_group_field =
+ art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ DCHECK(system_thread_group_field->GetDeclaringClass()->IsInitialized());
+ // Avoid using `ArtField::GetObject` as it requires linking against `libdexfile` for
+ // `operator<<(std::ostream&, Primitive::Type)`.
+ art::Handle<art::mirror::Object> system_thread_group = hs.NewHandle(
+ system_thread_group_field->GetDeclaringClass()->GetFieldObject<art::mirror::Object>(
+ system_thread_group_field->GetOffset()));
+ return art::WellKnownClasses::java_lang_Thread_init->NewObject<'L', 'L', 'I', 'Z'>(
+ hs, self, system_thread_group, thr_name, /*priority=*/ 0, /*daemon=*/ true).Get();
}
struct CallbackData {
@@ -274,10 +281,14 @@
}
runtime->StartThreadBirth();
}
- ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self()));
+ jobject thr = soa.Env()->GetVm()->AddGlobalRef(self, CreateAdbConnectionThread(soa.Self()));
+ if (thr == nullptr) {
+ LOG(ERROR) << "Failed to create debugger thread!";
+ return;
+ }
// Note: Using pthreads instead of std::thread to not abort when the thread cannot be
// created (exception support required).
- std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) });
+ std::unique_ptr<CallbackData> data(new CallbackData { this, thr });
started_debugger_threads_ = true;
gPthread.emplace();
int pthread_create_result = pthread_create(&gPthread.value(),
@@ -289,7 +300,7 @@
started_debugger_threads_ = false;
// If the create succeeded the other thread will call EndThreadBirth.
art::Runtime* runtime = art::Runtime::Current();
- soa.Env()->DeleteGlobalRef(data->thr_);
+ soa.Env()->DeleteGlobalRef(thr);
LOG(ERROR) << "Failed to create thread for adb-jdwp connection manager!";
art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
@@ -810,8 +821,12 @@
void AdbConnectionState::AttachJdwpAgent(art::Thread* self) {
art::Runtime* runtime = art::Runtime::Current();
self->AssertNoPendingException();
+
+ std::string args = MakeAgentArg();
+ VLOG(jdwp) << "Attaching JDWP agent with args '" << args << "'";
+
runtime->AttachAgent(/* env= */ nullptr,
- MakeAgentArg(),
+ args,
/* class_loader= */ nullptr);
if (self->IsExceptionPending()) {
LOG(ERROR) << "Failed to load agent " << agent_name_;
diff --git a/artd/Android.bp b/artd/Android.bp
index 6db1287..e0674f0 100644
--- a/artd/Android.bp
+++ b/artd/Android.bp
@@ -22,22 +22,39 @@
default_applicable_licenses: ["art_license"],
}
-art_cc_binary {
- name: "artd",
+cc_defaults {
+ name: "artd_defaults",
defaults: ["art_defaults"],
-
srcs: [
"artd.cc",
+ "file_utils.cc",
+ "path_utils.cc",
],
-
+ header_libs: [
+ "profman_headers",
+ ],
shared_libs: [
- "artd-aidl-ndk",
- "libartbase",
"libarttools",
"libbase",
"libbinder_ndk",
+ "libselinux",
],
+ static_libs: [
+ "artd-aidl-ndk",
+ "libc++fs",
+ ],
+}
+art_cc_binary {
+ name: "artd",
+ defaults: ["artd_defaults"],
+ srcs: [
+ "artd_main.cc",
+ ],
+ shared_libs: [
+ "libart",
+ "libartbase",
+ ],
apex_available: [
"com.android.art",
"com.android.art.debug",
@@ -50,3 +67,45 @@
filename: "init.rc",
installable: false,
}
+
+art_cc_defaults {
+ name: "art_artd_tests_defaults",
+ defaults: ["artd_defaults"],
+ static_libs: [
+ "libgmock",
+ ],
+ srcs: [
+ "artd_test.cc",
+ "file_utils_test.cc",
+ "path_utils_test.cc",
+ ],
+ data: [
+ ":art-gtest-jars-Main",
+ ":art-gtest-jars-Nested",
+ ],
+}
+
+// Version of ART gtest `art_artd_tests` bundled with the ART APEX on target.
+//
+// This test requires the full libbinder_ndk implementation on host, which is
+// not available as a prebuilt on the thin master-art branch. Hence it won't
+// work there, and there's a conditional in Android.gtest.mk to exclude it from
+// test-art-host-gtest.
+art_cc_test {
+ name: "art_artd_tests",
+ defaults: [
+ "art_gtest_defaults",
+ "art_artd_tests_defaults",
+ ],
+}
+
+// Standalone version of ART gtest `art_artd_tests`, not bundled with the ART
+// APEX on target.
+art_cc_test {
+ name: "art_standalone_artd_tests",
+ defaults: [
+ "art_standalone_gtest_defaults",
+ "art_artd_tests_defaults",
+ ],
+ test_config_template: "art_standalone_artd_tests.xml",
+}
diff --git a/artd/art_standalone_artd_tests.xml b/artd/art_standalone_artd_tests.xml
new file mode 100644
index 0000000..9125046
--- /dev/null
+++ b/artd/art_standalone_artd_tests.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<!-- Note: This test config file for {MODULE} is generated from a template. -->
+<configuration description="Runs {MODULE}.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}/{MODULE}" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/{MODULE}/art-gtest-jars-Main.jar" />
+ <option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/{MODULE}/art-gtest-jars-Nested.jar" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp/{MODULE}" />
+ <option name="module-name" value="{MODULE}" />
+ <option name="ld-library-path-32" value="/apex/com.android.art/lib" />
+ <option name="ld-library-path-64" value="/apex/com.android.art/lib64" />
+ </test>
+
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- ART Mainline Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.art" />
+ <!-- ART Mainline Module (external (AOSP) version). -->
+ <option name="mainline-module-package-name" value="com.android.art" />
+ </object>
+
+ <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+ <!-- TODO(jiakaiz): Change this to U once `ro.build.version.sdk` is bumped. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
+</configuration>
diff --git a/artd/artd.cc b/artd/artd.cc
index 1dcd2e8..5b91879 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -1,87 +1,1255 @@
/*
-** Copyright 2021, 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.
-*/
+ * Copyright (C) 2021 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 <string>
-#define LOG_TAG "artd"
+#include "artd.h"
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <utils/Errors.h>
-#include "aidl/android/os/BnArtd.h"
-#include "base/logging.h"
-#include "base/macros.h"
+#include <climits>
+#include <csignal>
+#include <cstdint>
+#include <cstring>
+#include <filesystem>
+#include <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <ostream>
+#include <string>
+#include <string_view>
+#include <system_error>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "aidl/com/android/server/art/DexoptTrigger.h"
+#include "aidl/com/android/server/art/IArtdCancellationSignal.h"
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/result.h"
+#include "android-base/scopeguard.h"
+#include "android-base/strings.h"
+#include "android/binder_auto_utils.h"
+#include "android/binder_interface_utils.h"
+#include "android/binder_manager.h"
+#include "android/binder_process.h"
+#include "base/compiler_filter.h"
+#include "base/file_utils.h"
+#include "base/globals.h"
+#include "base/os.h"
+#include "exec_utils.h"
+#include "file_utils.h"
+#include "fmt/format.h"
+#include "oat_file_assistant.h"
+#include "oat_file_assistant_context.h"
+#include "path_utils.h"
+#include "profman/profman_result.h"
+#include "selinux/android.h"
+#include "tools/cmdline_builder.h"
#include "tools/tools.h"
-using ::ndk::ScopedAStatus;
-
-namespace android {
+namespace art {
namespace artd {
-class Artd : public aidl::android::os::BnArtd {
- constexpr static const char* const SERVICE_NAME = "artd";
+namespace {
- public:
- Artd() {}
+using ::aidl::com::android::server::art::ArtdDexoptResult;
+using ::aidl::com::android::server::art::ArtifactsPath;
+using ::aidl::com::android::server::art::DexMetadataPath;
+using ::aidl::com::android::server::art::DexoptOptions;
+using ::aidl::com::android::server::art::DexoptTrigger;
+using ::aidl::com::android::server::art::FileVisibility;
+using ::aidl::com::android::server::art::FsPermission;
+using ::aidl::com::android::server::art::GetDexoptNeededResult;
+using ::aidl::com::android::server::art::GetDexoptStatusResult;
+using ::aidl::com::android::server::art::IArtdCancellationSignal;
+using ::aidl::com::android::server::art::MergeProfileOptions;
+using ::aidl::com::android::server::art::OutputArtifacts;
+using ::aidl::com::android::server::art::OutputProfile;
+using ::aidl::com::android::server::art::PriorityClass;
+using ::aidl::com::android::server::art::ProfilePath;
+using ::aidl::com::android::server::art::VdexPath;
+using ::android::base::Dirname;
+using ::android::base::Error;
+using ::android::base::Join;
+using ::android::base::make_scope_guard;
+using ::android::base::ReadFileToString;
+using ::android::base::Result;
+using ::android::base::Split;
+using ::android::base::StringReplace;
+using ::android::base::WriteStringToFd;
+using ::art::tools::CmdlineBuilder;
+using ::ndk::ScopedAStatus;
- /*
- * Binder API
- */
+using ::fmt::literals::operator""_format; // NOLINT
- ScopedAStatus isAlive(bool* _aidl_return) {
- *_aidl_return = true;
- return ScopedAStatus::ok();
- }
+using ArtifactsLocation = GetDexoptNeededResult::ArtifactsLocation;
+using TmpProfilePath = ProfilePath::TmpProfilePath;
- /*
- * Server API
- */
+constexpr const char* kServiceName = "artd";
+constexpr const char* kArtdCancellationSignalType = "ArtdCancellationSignal";
- ScopedAStatus Start() {
- LOG(INFO) << "Starting artd";
+// Timeout for short operations, such as merging profiles.
+constexpr int kShortTimeoutSec = 60; // 1 minute.
- status_t ret = AServiceManager_addService(this->asBinder().get(), SERVICE_NAME);
- if (ret != android::OK) {
- return ScopedAStatus::fromStatus(ret);
+// Timeout for long operations, such as compilation. We set it to be smaller than the Package
+// Manager watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that if the operation
+// is called from the Package Manager's thread handler, it will be aborted before that watchdog
+// would take down the system server.
+constexpr int kLongTimeoutSec = 570; // 9.5 minutes.
+
+std::optional<int64_t> GetSize(std::string_view path) {
+ std::error_code ec;
+ int64_t size = std::filesystem::file_size(path, ec);
+ if (ec) {
+ // It is okay if the file does not exist. We don't have to log it.
+ if (ec.value() != ENOENT) {
+ LOG(ERROR) << "Failed to get the file size of '{}': {}"_format(path, ec.message());
}
-
- ABinderProcess_startThreadPool();
-
- return ScopedAStatus::ok();
+ return std::nullopt;
}
+ return size;
+}
+
+// Deletes a file. Returns the size of the deleted file, or 0 if the deleted file is empty or an
+// error occurs.
+int64_t GetSizeAndDeleteFile(const std::string& path) {
+ std::optional<int64_t> size = GetSize(path);
+ if (!size.has_value()) {
+ return 0;
+ }
+
+ std::error_code ec;
+ if (!std::filesystem::remove(path, ec)) {
+ LOG(ERROR) << "Failed to remove '{}': {}"_format(path, ec.message());
+ return 0;
+ }
+
+ return size.value();
+}
+
+std::string EscapeErrorMessage(const std::string& message) {
+ return StringReplace(message, std::string("\0", /*n=*/1), "\\0", /*all=*/true);
+}
+
+// Indicates an error that should never happen (e.g., illegal arguments passed by service-art
+// internally). System server should crash if this kind of error happens.
+ScopedAStatus Fatal(const std::string& message) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
+ EscapeErrorMessage(message).c_str());
+}
+
+// Indicates an error that service-art should handle (e.g., I/O errors, sub-process crashes).
+// The scope of the error depends on the function that throws it, so service-art should catch the
+// error at every call site and take different actions.
+// Ideally, this should be a checked exception or an additional return value that forces service-art
+// to handle it, but `ServiceSpecificException` (a separate runtime exception type) is the best
+// approximate we have given the limitation of Java and Binder.
+ScopedAStatus NonFatal(const std::string& message) {
+ constexpr int32_t kArtdNonFatalErrorCode = 1;
+ return ScopedAStatus::fromServiceSpecificErrorWithMessage(kArtdNonFatalErrorCode,
+ EscapeErrorMessage(message).c_str());
+}
+
+Result<CompilerFilter::Filter> ParseCompilerFilter(const std::string& compiler_filter_str) {
+ CompilerFilter::Filter compiler_filter;
+ if (!CompilerFilter::ParseCompilerFilter(compiler_filter_str.c_str(), &compiler_filter)) {
+ return Errorf("Failed to parse compiler filter '{}'", compiler_filter_str);
+ }
+ return compiler_filter;
+}
+
+OatFileAssistant::DexOptTrigger DexOptTriggerFromAidl(int32_t aidl_value) {
+ OatFileAssistant::DexOptTrigger trigger{};
+ if ((aidl_value & static_cast<int32_t>(DexoptTrigger::COMPILER_FILTER_IS_BETTER)) != 0) {
+ trigger.targetFilterIsBetter = true;
+ }
+ if ((aidl_value & static_cast<int32_t>(DexoptTrigger::COMPILER_FILTER_IS_SAME)) != 0) {
+ trigger.targetFilterIsSame = true;
+ }
+ if ((aidl_value & static_cast<int32_t>(DexoptTrigger::COMPILER_FILTER_IS_WORSE)) != 0) {
+ trigger.targetFilterIsWorse = true;
+ }
+ if ((aidl_value & static_cast<int32_t>(DexoptTrigger::PRIMARY_BOOT_IMAGE_BECOMES_USABLE)) != 0) {
+ trigger.primaryBootImageBecomesUsable = true;
+ }
+ if ((aidl_value & static_cast<int32_t>(DexoptTrigger::NEED_EXTRACTION)) != 0) {
+ trigger.needExtraction = true;
+ }
+ return trigger;
+}
+
+ArtifactsLocation ArtifactsLocationToAidl(OatFileAssistant::Location location) {
+ switch (location) {
+ case OatFileAssistant::Location::kLocationNoneOrError:
+ return ArtifactsLocation::NONE_OR_ERROR;
+ case OatFileAssistant::Location::kLocationOat:
+ return ArtifactsLocation::DALVIK_CACHE;
+ case OatFileAssistant::Location::kLocationOdex:
+ return ArtifactsLocation::NEXT_TO_DEX;
+ case OatFileAssistant::Location::kLocationDm:
+ return ArtifactsLocation::DM;
+ // No default. All cases should be explicitly handled, or the compilation will fail.
+ }
+ // This should never happen. Just in case we get a non-enumerator value.
+ LOG(FATAL) << "Unexpected Location " << location;
+}
+
+Result<void> PrepareArtifactsDir(const std::string& path, const FsPermission& fs_permission) {
+ std::error_code ec;
+ bool created = std::filesystem::create_directory(path, ec);
+ if (ec) {
+ return Errorf("Failed to create directory '{}': {}", path, ec.message());
+ }
+
+ auto cleanup = make_scope_guard([&] {
+ if (created) {
+ std::filesystem::remove(path, ec);
+ }
+ });
+
+ if (chmod(path.c_str(), DirFsPermissionToMode(fs_permission)) != 0) {
+ return ErrnoErrorf("Failed to chmod directory '{}'", path);
+ }
+ OR_RETURN(Chown(path, fs_permission));
+
+ cleanup.Disable();
+ return {};
+}
+
+Result<void> PrepareArtifactsDirs(const OutputArtifacts& output_artifacts,
+ /*out*/ std::string* oat_dir_path) {
+ if (output_artifacts.artifactsPath.isInDalvikCache) {
+ return {};
+ }
+
+ std::filesystem::path oat_path(OR_RETURN(BuildOatPath(output_artifacts.artifactsPath)));
+ std::filesystem::path isa_dir = oat_path.parent_path();
+ std::filesystem::path oat_dir = isa_dir.parent_path();
+ DCHECK_EQ(oat_dir.filename(), "oat");
+
+ OR_RETURN(PrepareArtifactsDir(oat_dir, output_artifacts.permissionSettings.dirFsPermission));
+ OR_RETURN(PrepareArtifactsDir(isa_dir, output_artifacts.permissionSettings.dirFsPermission));
+ *oat_dir_path = oat_dir;
+ return {};
+}
+
+Result<void> Restorecon(
+ const std::string& path,
+ const std::optional<OutputArtifacts::PermissionSettings::SeContext>& se_context) {
+ if (!kIsTargetAndroid) {
+ return {};
+ }
+
+ int res = 0;
+ if (se_context.has_value()) {
+ res = selinux_android_restorecon_pkgdir(path.c_str(),
+ se_context->seInfo.c_str(),
+ se_context->uid,
+ SELINUX_ANDROID_RESTORECON_RECURSE);
+ } else {
+ res = selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+ }
+ if (res != 0) {
+ return ErrnoErrorf("Failed to restorecon directory '{}'", path);
+ }
+ return {};
+}
+
+Result<FileVisibility> GetFileVisibility(const std::string& file) {
+ std::error_code ec;
+ std::filesystem::file_status status = std::filesystem::status(file, ec);
+ if (!std::filesystem::status_known(status)) {
+ return Errorf("Failed to get status of '{}': {}", file, ec.message());
+ }
+ if (!std::filesystem::exists(status)) {
+ return FileVisibility::NOT_FOUND;
+ }
+
+ return (status.permissions() & std::filesystem::perms::others_read) !=
+ std::filesystem::perms::none ?
+ FileVisibility::OTHER_READABLE :
+ FileVisibility::NOT_OTHER_READABLE;
+}
+
+Result<ArtdCancellationSignal*> ToArtdCancellationSignal(IArtdCancellationSignal* input) {
+ if (input == nullptr) {
+ return Error() << "Cancellation signal must not be nullptr";
+ }
+ // We cannot use `dynamic_cast` because ART code is compiled with `-fno-rtti`, so we have to check
+ // the magic number.
+ int64_t type;
+ if (!input->getType(&type).isOk() ||
+ type != reinterpret_cast<intptr_t>(kArtdCancellationSignalType)) {
+ // The cancellation signal must be created by `Artd::createCancellationSignal`.
+ return Error() << "Invalid cancellation signal type";
+ }
+ return static_cast<ArtdCancellationSignal*>(input);
+}
+
+Result<void> CopyFile(const std::string& src_path, const NewFile& dst_file) {
+ std::string content;
+ if (!ReadFileToString(src_path, &content)) {
+ return Errorf("Failed to read file '{}': {}", src_path, strerror(errno));
+ }
+ if (!WriteStringToFd(content, dst_file.Fd())) {
+ return Errorf("Failed to write file '{}': {}", dst_file.TempPath(), strerror(errno));
+ }
+ if (fsync(dst_file.Fd()) != 0) {
+ return Errorf("Failed to flush file '{}': {}", dst_file.TempPath(), strerror(errno));
+ }
+ if (lseek(dst_file.Fd(), /*offset=*/0, SEEK_SET) != 0) {
+ return Errorf(
+ "Failed to reset the offset for file '{}': {}", dst_file.TempPath(), strerror(errno));
+ }
+ return {};
+}
+
+class FdLogger {
+ public:
+ void Add(const NewFile& file) { fd_mapping_.emplace_back(file.Fd(), file.TempPath()); }
+ void Add(const File& file) { fd_mapping_.emplace_back(file.Fd(), file.GetPath()); }
+
+ std::string GetFds() {
+ std::vector<int> fds;
+ fds.reserve(fd_mapping_.size());
+ for (const auto& [fd, path] : fd_mapping_) {
+ fds.push_back(fd);
+ }
+ return Join(fds, ':');
+ }
+
+ private:
+ std::vector<std::pair<int, std::string>> fd_mapping_;
+
+ friend std::ostream& operator<<(std::ostream& os, const FdLogger& fd_logger);
};
-} // namespace artd
-} // namespace android
+std::ostream& operator<<(std::ostream& os, const FdLogger& fd_logger) {
+ for (const auto& [fd, path] : fd_logger.fd_mapping_) {
+ os << fd << ":" << path << ' ';
+ }
+ return os;
+}
-int main(const int argc __attribute__((unused)), char* argv[]) {
- setenv("ANDROID_LOG_TAGS", "*:v", 1);
- android::base::InitLogging(argv);
+} // namespace
- android::artd::Artd artd;
+#define OR_RETURN_ERROR(func, expr) \
+ ({ \
+ decltype(expr)&& tmp = (expr); \
+ if (!tmp.ok()) { \
+ return (func)(tmp.error().message()); \
+ } \
+ std::move(tmp).value(); \
+ })
- if (auto ret = artd.Start(); !ret.isOk()) {
- LOG(ERROR) << "Unable to start artd: " << ret.getMessage();
- exit(1);
+#define OR_RETURN_FATAL(expr) OR_RETURN_ERROR(Fatal, expr)
+#define OR_RETURN_NON_FATAL(expr) OR_RETURN_ERROR(NonFatal, expr)
+
+ScopedAStatus Artd::isAlive(bool* _aidl_return) {
+ *_aidl_return = true;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Artd::deleteArtifacts(const ArtifactsPath& in_artifactsPath, int64_t* _aidl_return) {
+ std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath));
+
+ *_aidl_return = 0;
+ *_aidl_return += GetSizeAndDeleteFile(oat_path);
+ *_aidl_return += GetSizeAndDeleteFile(OatPathToVdexPath(oat_path));
+ *_aidl_return += GetSizeAndDeleteFile(OatPathToArtPath(oat_path));
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Artd::getDexoptStatus(const std::string& in_dexFile,
+ const std::string& in_instructionSet,
+ const std::string& in_classLoaderContext,
+ GetDexoptStatusResult* _aidl_return) {
+ Result<OatFileAssistantContext*> ofa_context = GetOatFileAssistantContext();
+ if (!ofa_context.ok()) {
+ return NonFatal("Failed to get runtime options: " + ofa_context.error().message());
}
- ABinderProcess_joinThreadPool();
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ auto oat_file_assistant = OatFileAssistant::Create(in_dexFile.c_str(),
+ in_instructionSet.c_str(),
+ in_classLoaderContext.c_str(),
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ ofa_context.value(),
+ &context,
+ &error_msg);
+ if (oat_file_assistant == nullptr) {
+ return NonFatal("Failed to create OatFileAssistant: " + error_msg);
+ }
- LOG(INFO) << "artd shutting down";
+ std::string ignored_odex_status;
+ oat_file_assistant->GetOptimizationStatus(&_aidl_return->locationDebugString,
+ &_aidl_return->compilerFilter,
+ &_aidl_return->compilationReason,
+ &ignored_odex_status);
- return 0;
+ // We ignore odex_status because it is not meaningful. It can only be either "up-to-date",
+ // "apk-more-recent", or "io-error-no-oat", which means it doesn't give us information in addition
+ // to what we can learn from compiler_filter because compiler_filter will be the actual compiler
+ // filter, "run-from-apk-fallback", and "run-from-apk" in those three cases respectively.
+ DCHECK(ignored_odex_status == "up-to-date" || ignored_odex_status == "apk-more-recent" ||
+ ignored_odex_status == "io-error-no-oat");
+
+ return ScopedAStatus::ok();
}
+
+ndk::ScopedAStatus Artd::isProfileUsable(const ProfilePath& in_profile,
+ const std::string& in_dexFile,
+ bool* _aidl_return) {
+ std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
+ OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
+
+ FdLogger fd_logger;
+
+ CmdlineBuilder art_exec_args;
+ art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
+
+ CmdlineBuilder args;
+ args.Add(OR_RETURN_FATAL(GetProfman()));
+
+ Result<std::unique_ptr<File>> profile = OpenFileForReading(profile_path);
+ if (!profile.ok()) {
+ if (profile.error().code() == ENOENT) {
+ *_aidl_return = false;
+ return ScopedAStatus::ok();
+ }
+ return NonFatal(
+ "Failed to open profile '{}': {}"_format(profile_path, profile.error().message()));
+ }
+ args.Add("--reference-profile-file-fd=%d", profile.value()->Fd());
+ fd_logger.Add(*profile.value());
+
+ std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(in_dexFile));
+ args.Add("--apk-fd=%d", dex_file->Fd());
+ fd_logger.Add(*dex_file);
+
+ art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
+
+ LOG(INFO) << "Running profman: " << Join(art_exec_args.Get(), /*separator=*/" ")
+ << "\nOpened FDs: " << fd_logger;
+
+ Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec);
+ if (!result.ok()) {
+ return NonFatal("Failed to run profman: " + result.error().message());
+ }
+
+ LOG(INFO) << "profman returned code {}"_format(result.value());
+
+ if (result.value() != ProfmanResult::kSkipCompilationSmallDelta &&
+ result.value() != ProfmanResult::kSkipCompilationEmptyProfiles) {
+ return NonFatal("profman returned an unexpected code: {}"_format(result.value()));
+ }
+
+ *_aidl_return = result.value() == ProfmanResult::kSkipCompilationSmallDelta;
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::copyAndRewriteProfile(const ProfilePath& in_src,
+ OutputProfile* in_dst,
+ const std::string& in_dexFile,
+ bool* _aidl_return) {
+ std::string src_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_src));
+ std::string dst_path = OR_RETURN_FATAL(BuildFinalProfilePath(in_dst->profilePath));
+ OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
+
+ FdLogger fd_logger;
+
+ CmdlineBuilder art_exec_args;
+ art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
+
+ CmdlineBuilder args;
+ args.Add(OR_RETURN_FATAL(GetProfman())).Add("--copy-and-update-profile-key");
+
+ Result<std::unique_ptr<File>> src = OpenFileForReading(src_path);
+ if (!src.ok()) {
+ if (src.error().code() == ENOENT) {
+ *_aidl_return = false;
+ return ScopedAStatus::ok();
+ }
+ return NonFatal("Failed to open src profile '{}': {}"_format(src_path, src.error().message()));
+ }
+ args.Add("--profile-file-fd=%d", src.value()->Fd());
+ fd_logger.Add(*src.value());
+
+ std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(in_dexFile));
+ args.Add("--apk-fd=%d", dex_file->Fd());
+ fd_logger.Add(*dex_file);
+
+ std::unique_ptr<NewFile> dst =
+ OR_RETURN_NON_FATAL(NewFile::Create(dst_path, in_dst->fsPermission));
+ args.Add("--reference-profile-file-fd=%d", dst->Fd());
+ fd_logger.Add(*dst);
+
+ art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
+
+ LOG(INFO) << "Running profman: " << Join(art_exec_args.Get(), /*separator=*/" ")
+ << "\nOpened FDs: " << fd_logger;
+
+ Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec);
+ if (!result.ok()) {
+ return NonFatal("Failed to run profman: " + result.error().message());
+ }
+
+ LOG(INFO) << "profman returned code {}"_format(result.value());
+
+ if (result.value() == ProfmanResult::kCopyAndUpdateNoMatch) {
+ *_aidl_return = false;
+ return ScopedAStatus::ok();
+ }
+
+ if (result.value() != ProfmanResult::kCopyAndUpdateSuccess) {
+ return NonFatal("profman returned an unexpected code: {}"_format(result.value()));
+ }
+
+ OR_RETURN_NON_FATAL(dst->Keep());
+ *_aidl_return = true;
+ in_dst->profilePath.id = dst->TempId();
+ in_dst->profilePath.tmpPath = dst->TempPath();
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::commitTmpProfile(const TmpProfilePath& in_profile) {
+ std::string tmp_profile_path = OR_RETURN_FATAL(BuildTmpProfilePath(in_profile));
+ std::string ref_profile_path = OR_RETURN_FATAL(BuildFinalProfilePath(in_profile));
+
+ std::error_code ec;
+ std::filesystem::rename(tmp_profile_path, ref_profile_path, ec);
+ if (ec) {
+ return NonFatal(
+ "Failed to move '{}' to '{}': {}"_format(tmp_profile_path, ref_profile_path, ec.message()));
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::deleteProfile(const ProfilePath& in_profile) {
+ std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
+
+ std::error_code ec;
+ if (!std::filesystem::remove(profile_path, ec) && ec.value() != ENOENT) {
+ LOG(ERROR) << "Failed to remove '{}': {}"_format(profile_path, ec.message());
+ }
+
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::getProfileVisibility(const ProfilePath& in_profile,
+ FileVisibility* _aidl_return) {
+ std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile));
+ *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(profile_path));
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::getArtifactsVisibility(const ArtifactsPath& in_artifactsPath,
+ FileVisibility* _aidl_return) {
+ std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_artifactsPath));
+ *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(oat_path));
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::getDexFileVisibility(const std::string& in_dexFile,
+ FileVisibility* _aidl_return) {
+ OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
+ *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(in_dexFile));
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::getDmFileVisibility(const DexMetadataPath& in_dmFile,
+ FileVisibility* _aidl_return) {
+ std::string dm_path = OR_RETURN_FATAL(BuildDexMetadataPath(in_dmFile));
+ *_aidl_return = OR_RETURN_NON_FATAL(GetFileVisibility(dm_path));
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::mergeProfiles(const std::vector<ProfilePath>& in_profiles,
+ const std::optional<ProfilePath>& in_referenceProfile,
+ OutputProfile* in_outputProfile,
+ const std::vector<std::string>& in_dexFiles,
+ const MergeProfileOptions& in_options,
+ bool* _aidl_return) {
+ std::vector<std::string> profile_paths;
+ for (const ProfilePath& profile : in_profiles) {
+ std::string profile_path = OR_RETURN_FATAL(BuildProfileOrDmPath(profile));
+ if (profile.getTag() == ProfilePath::dexMetadataPath) {
+ return Fatal("Does not support DM file, got '{}'"_format(profile_path));
+ }
+ profile_paths.push_back(std::move(profile_path));
+ }
+ std::string output_profile_path =
+ OR_RETURN_FATAL(BuildFinalProfilePath(in_outputProfile->profilePath));
+ for (const std::string& dex_file : in_dexFiles) {
+ OR_RETURN_FATAL(ValidateDexPath(dex_file));
+ }
+ if (in_options.forceMerge + in_options.dumpOnly + in_options.dumpClassesAndMethods > 1) {
+ return Fatal("Only one of 'forceMerge', 'dumpOnly', and 'dumpClassesAndMethods' can be set");
+ }
+
+ FdLogger fd_logger;
+
+ CmdlineBuilder art_exec_args;
+ art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
+
+ CmdlineBuilder args;
+ args.Add(OR_RETURN_FATAL(GetProfman()));
+
+ std::vector<std::unique_ptr<File>> profile_files;
+ for (const std::string& profile_path : profile_paths) {
+ Result<std::unique_ptr<File>> profile_file = OpenFileForReading(profile_path);
+ if (!profile_file.ok()) {
+ if (profile_file.error().code() == ENOENT) {
+ // Skip non-existing file.
+ continue;
+ }
+ return NonFatal(
+ "Failed to open profile '{}': {}"_format(profile_path, profile_file.error().message()));
+ }
+ args.Add("--profile-file-fd=%d", profile_file.value()->Fd());
+ fd_logger.Add(*profile_file.value());
+ profile_files.push_back(std::move(profile_file.value()));
+ }
+
+ if (profile_files.empty()) {
+ LOG(INFO) << "Merge skipped because there are no existing profiles";
+ *_aidl_return = false;
+ return ScopedAStatus::ok();
+ }
+
+ std::unique_ptr<NewFile> output_profile_file =
+ OR_RETURN_NON_FATAL(NewFile::Create(output_profile_path, in_outputProfile->fsPermission));
+
+ if (in_referenceProfile.has_value()) {
+ if (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) {
+ return Fatal(
+ "Reference profile must not be set when 'forceMerge', 'dumpOnly', or "
+ "'dumpClassesAndMethods' is set");
+ }
+ std::string reference_profile_path =
+ OR_RETURN_FATAL(BuildProfileOrDmPath(*in_referenceProfile));
+ if (in_referenceProfile->getTag() == ProfilePath::dexMetadataPath) {
+ return Fatal("Does not support DM file, got '{}'"_format(reference_profile_path));
+ }
+ OR_RETURN_NON_FATAL(CopyFile(reference_profile_path, *output_profile_file));
+ }
+
+ if (in_options.dumpOnly || in_options.dumpClassesAndMethods) {
+ args.Add("--dump-output-to-fd=%d", output_profile_file->Fd());
+ } else {
+ // profman is ok with this being an empty file when in_referenceProfile isn't set.
+ args.Add("--reference-profile-file-fd=%d", output_profile_file->Fd());
+ }
+ fd_logger.Add(*output_profile_file);
+
+ std::vector<std::unique_ptr<File>> dex_files;
+ for (const std::string& dex_path : in_dexFiles) {
+ std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(dex_path));
+ args.Add("--apk-fd=%d", dex_file->Fd());
+ fd_logger.Add(*dex_file);
+ dex_files.push_back(std::move(dex_file));
+ }
+
+ if (in_options.dumpOnly || in_options.dumpClassesAndMethods) {
+ args.Add(in_options.dumpOnly ? "--dump-only" : "--dump-classes-and-methods");
+ } else {
+ args.AddIfNonEmpty("--min-new-classes-percent-change=%s",
+ props_->GetOrEmpty("dalvik.vm.bgdexopt.new-classes-percent"))
+ .AddIfNonEmpty("--min-new-methods-percent-change=%s",
+ props_->GetOrEmpty("dalvik.vm.bgdexopt.new-methods-percent"))
+ .AddIf(in_options.forceMerge, "--force-merge")
+ .AddIf(in_options.forBootImage, "--boot-image-merge");
+ }
+
+ art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
+
+ LOG(INFO) << "Running profman: " << Join(art_exec_args.Get(), /*separator=*/" ")
+ << "\nOpened FDs: " << fd_logger;
+
+ Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kShortTimeoutSec);
+ if (!result.ok()) {
+ return NonFatal("Failed to run profman: " + result.error().message());
+ }
+
+ LOG(INFO) << "profman returned code {}"_format(result.value());
+
+ if (result.value() == ProfmanResult::kSkipCompilationSmallDelta ||
+ result.value() == ProfmanResult::kSkipCompilationEmptyProfiles) {
+ *_aidl_return = false;
+ return ScopedAStatus::ok();
+ }
+
+ ProfmanResult::ProcessingResult expected_result =
+ (in_options.forceMerge || in_options.dumpOnly || in_options.dumpClassesAndMethods) ?
+ ProfmanResult::kSuccess :
+ ProfmanResult::kCompile;
+ if (result.value() != expected_result) {
+ return NonFatal("profman returned an unexpected code: {}"_format(result.value()));
+ }
+
+ OR_RETURN_NON_FATAL(output_profile_file->Keep());
+ *_aidl_return = true;
+ in_outputProfile->profilePath.id = output_profile_file->TempId();
+ in_outputProfile->profilePath.tmpPath = output_profile_file->TempPath();
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::getDexoptNeeded(const std::string& in_dexFile,
+ const std::string& in_instructionSet,
+ const std::optional<std::string>& in_classLoaderContext,
+ const std::string& in_compilerFilter,
+ int32_t in_dexoptTrigger,
+ GetDexoptNeededResult* _aidl_return) {
+ Result<OatFileAssistantContext*> ofa_context = GetOatFileAssistantContext();
+ if (!ofa_context.ok()) {
+ return NonFatal("Failed to get runtime options: " + ofa_context.error().message());
+ }
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ auto oat_file_assistant = OatFileAssistant::Create(in_dexFile,
+ in_instructionSet,
+ in_classLoaderContext,
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ ofa_context.value(),
+ &context,
+ &error_msg);
+ if (oat_file_assistant == nullptr) {
+ return NonFatal("Failed to create OatFileAssistant: " + error_msg);
+ }
+
+ OatFileAssistant::DexOptStatus status;
+ _aidl_return->isDexoptNeeded =
+ oat_file_assistant->GetDexOptNeeded(OR_RETURN_FATAL(ParseCompilerFilter(in_compilerFilter)),
+ DexOptTriggerFromAidl(in_dexoptTrigger),
+ &status);
+ _aidl_return->isVdexUsable = status.IsVdexUsable();
+ _aidl_return->artifactsLocation = ArtifactsLocationToAidl(status.GetLocation());
+
+ return ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Artd::dexopt(
+ const OutputArtifacts& in_outputArtifacts,
+ const std::string& in_dexFile,
+ const std::string& in_instructionSet,
+ const std::optional<std::string>& in_classLoaderContext,
+ const std::string& in_compilerFilter,
+ const std::optional<ProfilePath>& in_profile,
+ const std::optional<VdexPath>& in_inputVdex,
+ const std::optional<DexMetadataPath>& in_dmFile,
+ PriorityClass in_priorityClass,
+ const DexoptOptions& in_dexoptOptions,
+ const std::shared_ptr<IArtdCancellationSignal>& in_cancellationSignal,
+ ArtdDexoptResult* _aidl_return) {
+ _aidl_return->cancelled = false;
+
+ std::string oat_path = OR_RETURN_FATAL(BuildOatPath(in_outputArtifacts.artifactsPath));
+ std::string vdex_path = OatPathToVdexPath(oat_path);
+ std::string art_path = OatPathToArtPath(oat_path);
+ OR_RETURN_FATAL(ValidateDexPath(in_dexFile));
+ std::optional<std::string> profile_path =
+ in_profile.has_value() ?
+ std::make_optional(OR_RETURN_FATAL(BuildProfileOrDmPath(in_profile.value()))) :
+ std::nullopt;
+ ArtdCancellationSignal* cancellation_signal =
+ OR_RETURN_FATAL(ToArtdCancellationSignal(in_cancellationSignal.get()));
+
+ std::unique_ptr<ClassLoaderContext> context = nullptr;
+ if (in_classLoaderContext.has_value()) {
+ context = ClassLoaderContext::Create(in_classLoaderContext->c_str());
+ if (context == nullptr) {
+ return Fatal("Class loader context '{}' is invalid"_format(in_classLoaderContext.value()));
+ }
+ }
+
+ std::string oat_dir_path; // For restorecon, can be empty if the artifacts are in dalvik-cache.
+ OR_RETURN_NON_FATAL(PrepareArtifactsDirs(in_outputArtifacts, &oat_dir_path));
+
+ // First-round restorecon. artd doesn't have the permission to create files with the
+ // `apk_data_file` label, so we need to restorecon the "oat" directory first so that files will
+ // inherit `dalvikcache_data_file` rather than `apk_data_file`.
+ if (!in_outputArtifacts.artifactsPath.isInDalvikCache) {
+ OR_RETURN_NON_FATAL(Restorecon(oat_dir_path, in_outputArtifacts.permissionSettings.seContext));
+ }
+
+ FdLogger fd_logger;
+
+ CmdlineBuilder art_exec_args;
+ art_exec_args.Add(OR_RETURN_FATAL(GetArtExec())).Add("--drop-capabilities");
+
+ CmdlineBuilder args;
+ args.Add(OR_RETURN_FATAL(GetDex2Oat()));
+
+ const FsPermission& fs_permission = in_outputArtifacts.permissionSettings.fileFsPermission;
+
+ std::unique_ptr<File> dex_file = OR_RETURN_NON_FATAL(OpenFileForReading(in_dexFile));
+ args.Add("--zip-fd=%d", dex_file->Fd()).Add("--zip-location=%s", in_dexFile);
+ fd_logger.Add(*dex_file);
+ struct stat dex_st = OR_RETURN_NON_FATAL(Fstat(*dex_file));
+ if ((dex_st.st_mode & S_IROTH) == 0) {
+ if (fs_permission.isOtherReadable) {
+ return NonFatal(
+ "Outputs cannot be other-readable because the dex file '{}' is not other-readable"_format(
+ dex_file->GetPath()));
+ }
+ // Negative numbers mean no `chown`. 0 means root.
+ // Note: this check is more strict than it needs to be. For example, it doesn't allow the
+ // outputs to belong to a group that is a subset of the dex file's group. This is for
+ // simplicity, and it's okay as we don't have to handle such complicated cases in practice.
+ if ((fs_permission.uid > 0 && static_cast<uid_t>(fs_permission.uid) != dex_st.st_uid) ||
+ (fs_permission.gid > 0 && static_cast<gid_t>(fs_permission.gid) != dex_st.st_uid &&
+ static_cast<gid_t>(fs_permission.gid) != dex_st.st_gid)) {
+ return NonFatal(
+ "Outputs' owner doesn't match the dex file '{}' (outputs: {}:{}, dex file: {}:{})"_format(
+ dex_file->GetPath(),
+ fs_permission.uid,
+ fs_permission.gid,
+ dex_st.st_uid,
+ dex_st.st_gid));
+ }
+ }
+
+ std::unique_ptr<NewFile> oat_file = OR_RETURN_NON_FATAL(NewFile::Create(oat_path, fs_permission));
+ args.Add("--oat-fd=%d", oat_file->Fd()).Add("--oat-location=%s", oat_path);
+ fd_logger.Add(*oat_file);
+
+ std::unique_ptr<NewFile> vdex_file =
+ OR_RETURN_NON_FATAL(NewFile::Create(vdex_path, fs_permission));
+ args.Add("--output-vdex-fd=%d", vdex_file->Fd());
+ fd_logger.Add(*vdex_file);
+
+ std::vector<NewFile*> files_to_commit{oat_file.get(), vdex_file.get()};
+ std::vector<std::string_view> files_to_delete;
+
+ std::unique_ptr<NewFile> art_file = nullptr;
+ if (in_dexoptOptions.generateAppImage) {
+ art_file = OR_RETURN_NON_FATAL(NewFile::Create(art_path, fs_permission));
+ args.Add("--app-image-fd=%d", art_file->Fd());
+ args.AddIfNonEmpty("--image-format=%s", props_->GetOrEmpty("dalvik.vm.appimageformat"));
+ fd_logger.Add(*art_file);
+ files_to_commit.push_back(art_file.get());
+ } else {
+ files_to_delete.push_back(art_path);
+ }
+
+ std::unique_ptr<NewFile> swap_file = nullptr;
+ if (ShouldCreateSwapFileForDexopt()) {
+ swap_file = OR_RETURN_NON_FATAL(
+ NewFile::Create("{}.swap"_format(oat_path), FsPermission{.uid = -1, .gid = -1}));
+ args.Add("--swap-fd=%d", swap_file->Fd());
+ fd_logger.Add(*swap_file);
+ }
+
+ std::vector<std::unique_ptr<File>> context_files;
+ if (context != nullptr) {
+ std::vector<std::string> flattened_context = context->FlattenDexPaths();
+ std::string dex_dir = Dirname(in_dexFile.c_str());
+ std::vector<int> context_fds;
+ for (const std::string& context_element : flattened_context) {
+ std::string context_path = std::filesystem::path(dex_dir).append(context_element);
+ OR_RETURN_FATAL(ValidateDexPath(context_path));
+ std::unique_ptr<File> context_file = OR_RETURN_NON_FATAL(OpenFileForReading(context_path));
+ context_fds.push_back(context_file->Fd());
+ fd_logger.Add(*context_file);
+ context_files.push_back(std::move(context_file));
+ }
+ args.AddIfNonEmpty("--class-loader-context-fds=%s", Join(context_fds, /*separator=*/':'))
+ .Add("--class-loader-context=%s", in_classLoaderContext.value())
+ .Add("--classpath-dir=%s", dex_dir);
+ }
+
+ std::unique_ptr<File> input_vdex_file = nullptr;
+ if (in_inputVdex.has_value()) {
+ std::string input_vdex_path = OR_RETURN_FATAL(BuildVdexPath(in_inputVdex.value()));
+ input_vdex_file = OR_RETURN_NON_FATAL(OpenFileForReading(input_vdex_path));
+ args.Add("--input-vdex-fd=%d", input_vdex_file->Fd());
+ fd_logger.Add(*input_vdex_file);
+ }
+
+ std::unique_ptr<File> dm_file = nullptr;
+ if (in_dmFile.has_value()) {
+ std::string dm_path = OR_RETURN_FATAL(BuildDexMetadataPath(in_dmFile.value()));
+ dm_file = OR_RETURN_NON_FATAL(OpenFileForReading(dm_path));
+ args.Add("--dm-fd=%d", dm_file->Fd());
+ fd_logger.Add(*dm_file);
+ }
+
+ std::unique_ptr<File> profile_file = nullptr;
+ if (profile_path.has_value()) {
+ profile_file = OR_RETURN_NON_FATAL(OpenFileForReading(profile_path.value()));
+ args.Add("--profile-file-fd=%d", profile_file->Fd());
+ fd_logger.Add(*profile_file);
+ struct stat profile_st = OR_RETURN_NON_FATAL(Fstat(*profile_file));
+ if (fs_permission.isOtherReadable && (profile_st.st_mode & S_IROTH) == 0) {
+ return NonFatal(
+ "Outputs cannot be other-readable because the profile '{}' is not other-readable"_format(
+ profile_file->GetPath()));
+ }
+ // TODO(b/260228411): Check uid and gid.
+ }
+
+ // Second-round restorecon. Restorecon recursively after the output files are created, so that the
+ // SELinux context is applied to all of them. The SELinux context of a file is mostly inherited
+ // from the parent directory upon creation, but the MLS label is not inherited, so we need to
+ // restorecon every file so that they have the right MLS label. If the files are in dalvik-cache,
+ // there's no need to restorecon because they inherits the SELinux context of the dalvik-cache
+ // directory and they don't need to have MLS labels.
+ if (!in_outputArtifacts.artifactsPath.isInDalvikCache) {
+ OR_RETURN_NON_FATAL(Restorecon(oat_dir_path, in_outputArtifacts.permissionSettings.seContext));
+ }
+
+ AddBootImageFlags(args);
+ AddCompilerConfigFlags(
+ in_instructionSet, in_compilerFilter, in_priorityClass, in_dexoptOptions, args);
+ AddPerfConfigFlags(in_priorityClass, art_exec_args, args);
+
+ art_exec_args.Add("--keep-fds=%s", fd_logger.GetFds()).Add("--").Concat(std::move(args));
+
+ LOG(INFO) << "Running dex2oat: " << Join(art_exec_args.Get(), /*separator=*/" ")
+ << "\nOpened FDs: " << fd_logger;
+
+ ExecCallbacks callbacks{
+ .on_start =
+ [&](pid_t pid) {
+ std::lock_guard<std::mutex> lock(cancellation_signal->mu_);
+ cancellation_signal->pids_.insert(pid);
+ // Handle cancellation signals sent before the process starts.
+ if (cancellation_signal->is_cancelled_) {
+ int res = kill_(pid, SIGKILL);
+ DCHECK_EQ(res, 0);
+ }
+ },
+ .on_end =
+ [&](pid_t pid) {
+ std::lock_guard<std::mutex> lock(cancellation_signal->mu_);
+ // The pid should no longer receive kill signals sent by `cancellation_signal`.
+ cancellation_signal->pids_.erase(pid);
+ },
+ };
+
+ ProcessStat stat;
+ Result<int> result = ExecAndReturnCode(art_exec_args.Get(), kLongTimeoutSec, callbacks, &stat);
+ _aidl_return->wallTimeMs = stat.wall_time_ms;
+ _aidl_return->cpuTimeMs = stat.cpu_time_ms;
+ if (!result.ok()) {
+ {
+ std::lock_guard<std::mutex> lock(cancellation_signal->mu_);
+ if (cancellation_signal->is_cancelled_) {
+ _aidl_return->cancelled = true;
+ return ScopedAStatus::ok();
+ }
+ }
+ return NonFatal("Failed to run dex2oat: " + result.error().message());
+ }
+
+ LOG(INFO) << "dex2oat returned code {}"_format(result.value());
+
+ if (result.value() != 0) {
+ return NonFatal("dex2oat returned an unexpected code: {}"_format(result.value()));
+ }
+
+ int64_t size_bytes = 0;
+ int64_t size_before_bytes = 0;
+ for (const NewFile* file : files_to_commit) {
+ size_bytes += GetSize(file->TempPath()).value_or(0);
+ size_before_bytes += GetSize(file->FinalPath()).value_or(0);
+ }
+ for (std::string_view path : files_to_delete) {
+ size_before_bytes += GetSize(path).value_or(0);
+ }
+ OR_RETURN_NON_FATAL(NewFile::CommitAllOrAbandon(files_to_commit, files_to_delete));
+
+ _aidl_return->sizeBytes = size_bytes;
+ _aidl_return->sizeBeforeBytes = size_before_bytes;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus ArtdCancellationSignal::cancel() {
+ std::lock_guard<std::mutex> lock(mu_);
+ is_cancelled_ = true;
+ for (pid_t pid : pids_) {
+ int res = kill_(pid, SIGKILL);
+ DCHECK_EQ(res, 0);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus ArtdCancellationSignal::getType(int64_t* _aidl_return) {
+ *_aidl_return = reinterpret_cast<intptr_t>(kArtdCancellationSignalType);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Artd::createCancellationSignal(
+ std::shared_ptr<IArtdCancellationSignal>* _aidl_return) {
+ *_aidl_return = ndk::SharedRefBase::make<ArtdCancellationSignal>(kill_);
+ return ScopedAStatus::ok();
+}
+
+Result<void> Artd::Start() {
+ ScopedAStatus status = ScopedAStatus::fromStatus(
+ AServiceManager_registerLazyService(this->asBinder().get(), kServiceName));
+ if (!status.isOk()) {
+ return Error() << status.getDescription();
+ }
+
+ ABinderProcess_startThreadPool();
+
+ return {};
+}
+
+Result<OatFileAssistantContext*> Artd::GetOatFileAssistantContext() {
+ std::lock_guard<std::mutex> lock(ofa_context_mu_);
+
+ if (ofa_context_ == nullptr) {
+ ofa_context_ = std::make_unique<OatFileAssistantContext>(
+ std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+ OatFileAssistantContext::RuntimeOptions{
+ .image_locations = *OR_RETURN(GetBootImageLocations()),
+ .boot_class_path = *OR_RETURN(GetBootClassPath()),
+ .boot_class_path_locations = *OR_RETURN(GetBootClassPath()),
+ .deny_art_apex_data_files = DenyArtApexDataFiles(),
+ }));
+ std::string error_msg;
+ if (!ofa_context_->FetchAll(&error_msg)) {
+ return Error() << error_msg;
+ }
+ }
+
+ return ofa_context_.get();
+}
+
+Result<const std::vector<std::string>*> Artd::GetBootImageLocations() {
+ std::lock_guard<std::mutex> lock(cache_mu_);
+
+ if (!cached_boot_image_locations_.has_value()) {
+ std::string location_str;
+
+ if (UseJitZygoteLocked()) {
+ location_str = GetJitZygoteBootImageLocation();
+ } else if (std::string value = GetUserDefinedBootImageLocationsLocked(); !value.empty()) {
+ location_str = std::move(value);
+ } else {
+ std::string error_msg;
+ std::string android_root = GetAndroidRootSafe(&error_msg);
+ if (!error_msg.empty()) {
+ return Errorf("Failed to get ANDROID_ROOT: {}", error_msg);
+ }
+ location_str = GetDefaultBootImageLocation(android_root, DenyArtApexDataFilesLocked());
+ }
+
+ cached_boot_image_locations_ = Split(location_str, ":");
+ }
+
+ return &cached_boot_image_locations_.value();
+}
+
+Result<const std::vector<std::string>*> Artd::GetBootClassPath() {
+ std::lock_guard<std::mutex> lock(cache_mu_);
+
+ if (!cached_boot_class_path_.has_value()) {
+ const char* env_value = getenv("BOOTCLASSPATH");
+ if (env_value == nullptr || strlen(env_value) == 0) {
+ return Errorf("Failed to get environment variable 'BOOTCLASSPATH'");
+ }
+ cached_boot_class_path_ = Split(env_value, ":");
+ }
+
+ return &cached_boot_class_path_.value();
+}
+
+bool Artd::UseJitZygote() {
+ std::lock_guard<std::mutex> lock(cache_mu_);
+ return UseJitZygoteLocked();
+}
+
+bool Artd::UseJitZygoteLocked() {
+ if (!cached_use_jit_zygote_.has_value()) {
+ cached_use_jit_zygote_ =
+ props_->GetBool("persist.device_config.runtime_native_boot.profilebootclasspath",
+ "dalvik.vm.profilebootclasspath",
+ /*default_value=*/false);
+ }
+
+ return cached_use_jit_zygote_.value();
+}
+
+const std::string& Artd::GetUserDefinedBootImageLocations() {
+ std::lock_guard<std::mutex> lock(cache_mu_);
+ return GetUserDefinedBootImageLocationsLocked();
+}
+
+const std::string& Artd::GetUserDefinedBootImageLocationsLocked() {
+ if (!cached_user_defined_boot_image_locations_.has_value()) {
+ cached_user_defined_boot_image_locations_ = props_->GetOrEmpty("dalvik.vm.boot-image");
+ }
+
+ return cached_user_defined_boot_image_locations_.value();
+}
+
+bool Artd::DenyArtApexDataFiles() {
+ std::lock_guard<std::mutex> lock(cache_mu_);
+ return DenyArtApexDataFilesLocked();
+}
+
+bool Artd::DenyArtApexDataFilesLocked() {
+ if (!cached_deny_art_apex_data_files_.has_value()) {
+ cached_deny_art_apex_data_files_ =
+ !props_->GetBool("odsign.verification.success", /*default_value=*/false);
+ }
+
+ return cached_deny_art_apex_data_files_.value();
+}
+
+Result<std::string> Artd::GetProfman() { return BuildArtBinPath("profman"); }
+
+Result<std::string> Artd::GetArtExec() { return BuildArtBinPath("art_exec"); }
+
+bool Artd::ShouldUseDex2Oat64() {
+ return !props_->GetOrEmpty("ro.product.cpu.abilist64").empty() &&
+ props_->GetBool("dalvik.vm.dex2oat64.enabled", /*default_value=*/false);
+}
+
+Result<std::string> Artd::GetDex2Oat() {
+ std::string binary_name = ShouldUseDex2Oat64() ? "dex2oat64" : "dex2oat32";
+ // TODO(b/234351700): Should we use the "d" variant?
+ return BuildArtBinPath(binary_name);
+}
+
+bool Artd::ShouldCreateSwapFileForDexopt() {
+ // Create a swap file by default. Dex2oat will decide whether to use it or not.
+ return props_->GetBool("dalvik.vm.dex2oat-swap", /*default_value=*/true);
+}
+
+void Artd::AddBootImageFlags(/*out*/ CmdlineBuilder& args) {
+ if (UseJitZygote()) {
+ args.Add("--force-jit-zygote");
+ } else {
+ args.AddIfNonEmpty("--boot-image=%s", GetUserDefinedBootImageLocations());
+ }
+}
+
+void Artd::AddCompilerConfigFlags(const std::string& instruction_set,
+ const std::string& compiler_filter,
+ PriorityClass priority_class,
+ const DexoptOptions& dexopt_options,
+ /*out*/ CmdlineBuilder& args) {
+ args.Add("--instruction-set=%s", instruction_set);
+ std::string features_prop = "dalvik.vm.isa.{}.features"_format(instruction_set);
+ args.AddIfNonEmpty("--instruction-set-features=%s", props_->GetOrEmpty(features_prop));
+ std::string variant_prop = "dalvik.vm.isa.{}.variant"_format(instruction_set);
+ args.AddIfNonEmpty("--instruction-set-variant=%s", props_->GetOrEmpty(variant_prop));
+
+ args.Add("--compiler-filter=%s", compiler_filter)
+ .Add("--compilation-reason=%s", dexopt_options.compilationReason);
+
+ args.AddIf(priority_class >= PriorityClass::INTERACTIVE, "--compact-dex-level=none");
+
+ args.AddIfNonEmpty("--max-image-block-size=%s",
+ props_->GetOrEmpty("dalvik.vm.dex2oat-max-image-block-size"))
+ .AddIfNonEmpty("--very-large-app-threshold=%s",
+ props_->GetOrEmpty("dalvik.vm.dex2oat-very-large"))
+ .AddIfNonEmpty(
+ "--resolve-startup-const-strings=%s",
+ props_->GetOrEmpty("persist.device_config.runtime.dex2oat_resolve_startup_strings",
+ "dalvik.vm.dex2oat-resolve-startup-strings"));
+
+ args.AddIf(dexopt_options.debuggable, "--debuggable")
+ .AddIf(props_->GetBool("debug.generate-debug-info", /*default_value=*/false),
+ "--generate-debug-info")
+ .AddIf(props_->GetBool("dalvik.vm.dex2oat-minidebuginfo", /*default_value=*/false),
+ "--generate-mini-debug-info");
+
+ args.AddRuntimeIf(DenyArtApexDataFiles(), "-Xdeny-art-apex-data-files")
+ .AddRuntime("-Xtarget-sdk-version:%d", dexopt_options.targetSdkVersion)
+ .AddRuntimeIf(dexopt_options.hiddenApiPolicyEnabled, "-Xhidden-api-policy:enabled");
+}
+
+void Artd::AddPerfConfigFlags(PriorityClass priority_class,
+ /*out*/ CmdlineBuilder& art_exec_args,
+ /*out*/ CmdlineBuilder& dex2oat_args) {
+ // CPU set and number of threads.
+ std::string default_cpu_set_prop = "dalvik.vm.dex2oat-cpu-set";
+ std::string default_threads_prop = "dalvik.vm.dex2oat-threads";
+ std::string cpu_set;
+ std::string threads;
+ if (priority_class >= PriorityClass::BOOT) {
+ cpu_set = props_->GetOrEmpty("dalvik.vm.boot-dex2oat-cpu-set");
+ threads = props_->GetOrEmpty("dalvik.vm.boot-dex2oat-threads");
+ } else if (priority_class >= PriorityClass::INTERACTIVE_FAST) {
+ cpu_set = props_->GetOrEmpty("dalvik.vm.restore-dex2oat-cpu-set", default_cpu_set_prop);
+ threads = props_->GetOrEmpty("dalvik.vm.restore-dex2oat-threads", default_threads_prop);
+ } else if (priority_class <= PriorityClass::BACKGROUND) {
+ cpu_set = props_->GetOrEmpty("dalvik.vm.background-dex2oat-cpu-set", default_cpu_set_prop);
+ threads = props_->GetOrEmpty("dalvik.vm.background-dex2oat-threads", default_threads_prop);
+ } else {
+ cpu_set = props_->GetOrEmpty(default_cpu_set_prop);
+ threads = props_->GetOrEmpty(default_threads_prop);
+ }
+ dex2oat_args.AddIfNonEmpty("--cpu-set=%s", cpu_set).AddIfNonEmpty("-j%s", threads);
+
+ if (priority_class < PriorityClass::BOOT) {
+ art_exec_args
+ .Add(priority_class <= PriorityClass::BACKGROUND ? "--set-task-profile=Dex2OatBackground" :
+ "--set-task-profile=Dex2OatBootComplete")
+ .Add("--set-priority=background");
+ }
+
+ dex2oat_args.AddRuntimeIfNonEmpty("-Xms%s", props_->GetOrEmpty("dalvik.vm.dex2oat-Xms"))
+ .AddRuntimeIfNonEmpty("-Xmx%s", props_->GetOrEmpty("dalvik.vm.dex2oat-Xmx"));
+
+ // Enable compiling dex files in isolation on low ram devices.
+ // It takes longer but reduces the memory footprint.
+ dex2oat_args.AddIf(props_->GetBool("ro.config.low_ram", /*default_value=*/false),
+ "--compile-individually");
+}
+
+Result<int> Artd::ExecAndReturnCode(const std::vector<std::string>& args,
+ int timeout_sec,
+ const ExecCallbacks& callbacks,
+ ProcessStat* stat) const {
+ std::string error_msg;
+ ExecResult result =
+ exec_utils_->ExecAndReturnResult(args, timeout_sec, callbacks, stat, &error_msg);
+ if (result.status != ExecResult::kExited) {
+ return Error() << error_msg;
+ }
+ return result.exit_code;
+}
+
+Result<struct stat> Artd::Fstat(const File& file) const {
+ struct stat st;
+ if (fstat_(file.Fd(), &st) != 0) {
+ return Errorf("Unable to fstat file '{}'", file.GetPath());
+ }
+ return st;
+}
+
+} // namespace artd
+} // namespace art
diff --git a/artd/artd.h b/artd/artd.h
new file mode 100644
index 0000000..fbaaed0
--- /dev/null
+++ b/artd/artd.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_ARTD_ARTD_H_
+#define ART_ARTD_ARTD_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <csignal>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "aidl/com/android/server/art/BnArtdCancellationSignal.h"
+#include "android-base/result.h"
+#include "android-base/thread_annotations.h"
+#include "android/binder_auto_utils.h"
+#include "base/os.h"
+#include "exec_utils.h"
+#include "oat_file_assistant_context.h"
+#include "tools/cmdline_builder.h"
+#include "tools/system_properties.h"
+
+namespace art {
+namespace artd {
+
+class ArtdCancellationSignal : public aidl::com::android::server::art::BnArtdCancellationSignal {
+ public:
+ explicit ArtdCancellationSignal(std::function<int(pid_t, int)> kill_func)
+ : kill_(std::move(kill_func)) {}
+
+ ndk::ScopedAStatus cancel() override;
+
+ ndk::ScopedAStatus getType(int64_t* _aidl_return) override;
+
+ private:
+ std::mutex mu_;
+ // True if cancellation has been signaled.
+ bool is_cancelled_ GUARDED_BY(mu_) = false;
+ // The pids of currently running child processes that are bound to this signal.
+ std::unordered_set<pid_t> pids_ GUARDED_BY(mu_);
+
+ std::function<int(pid_t, int)> kill_;
+
+ friend class Artd;
+};
+
+class Artd : public aidl::com::android::server::art::BnArtd {
+ public:
+ explicit Artd(std::unique_ptr<art::tools::SystemProperties> props =
+ std::make_unique<art::tools::SystemProperties>(),
+ std::unique_ptr<ExecUtils> exec_utils = std::make_unique<ExecUtils>(),
+ std::function<int(pid_t, int)> kill_func = kill,
+ std::function<int(int, struct stat*)> fstat_func = fstat)
+ : props_(std::move(props)),
+ exec_utils_(std::move(exec_utils)),
+ kill_(std::move(kill_func)),
+ fstat_(std::move(fstat_func)) {}
+
+ ndk::ScopedAStatus isAlive(bool* _aidl_return) override;
+
+ ndk::ScopedAStatus deleteArtifacts(
+ const aidl::com::android::server::art::ArtifactsPath& in_artifactsPath,
+ int64_t* _aidl_return) override;
+
+ ndk::ScopedAStatus getDexoptStatus(
+ const std::string& in_dexFile,
+ const std::string& in_instructionSet,
+ const std::string& in_classLoaderContext,
+ aidl::com::android::server::art::GetDexoptStatusResult* _aidl_return) override;
+
+ ndk::ScopedAStatus isProfileUsable(const aidl::com::android::server::art::ProfilePath& in_profile,
+ const std::string& in_dexFile,
+ bool* _aidl_return) override;
+
+ ndk::ScopedAStatus copyAndRewriteProfile(
+ const aidl::com::android::server::art::ProfilePath& in_src,
+ aidl::com::android::server::art::OutputProfile* in_dst,
+ const std::string& in_dexFile,
+ bool* _aidl_return) override;
+
+ ndk::ScopedAStatus commitTmpProfile(
+ const aidl::com::android::server::art::ProfilePath::TmpProfilePath& in_profile) override;
+
+ ndk::ScopedAStatus deleteProfile(
+ const aidl::com::android::server::art::ProfilePath& in_profile) override;
+
+ ndk::ScopedAStatus getProfileVisibility(
+ const aidl::com::android::server::art::ProfilePath& in_profile,
+ aidl::com::android::server::art::FileVisibility* _aidl_return) override;
+
+ ndk::ScopedAStatus mergeProfiles(
+ const std::vector<aidl::com::android::server::art::ProfilePath>& in_profiles,
+ const std::optional<aidl::com::android::server::art::ProfilePath>& in_referenceProfile,
+ aidl::com::android::server::art::OutputProfile* in_outputProfile,
+ const std::vector<std::string>& in_dexFiles,
+ const aidl::com::android::server::art::MergeProfileOptions& in_options,
+ bool* _aidl_return) override;
+
+ ndk::ScopedAStatus getArtifactsVisibility(
+ const aidl::com::android::server::art::ArtifactsPath& in_artifactsPath,
+ aidl::com::android::server::art::FileVisibility* _aidl_return) override;
+
+ ndk::ScopedAStatus getDexFileVisibility(
+ const std::string& in_dexFile,
+ aidl::com::android::server::art::FileVisibility* _aidl_return) override;
+
+ ndk::ScopedAStatus getDmFileVisibility(
+ const aidl::com::android::server::art::DexMetadataPath& in_dmFile,
+ aidl::com::android::server::art::FileVisibility* _aidl_return) override;
+
+ ndk::ScopedAStatus getDexoptNeeded(
+ const std::string& in_dexFile,
+ const std::string& in_instructionSet,
+ const std::optional<std::string>& in_classLoaderContext,
+ const std::string& in_compilerFilter,
+ int32_t in_dexoptTrigger,
+ aidl::com::android::server::art::GetDexoptNeededResult* _aidl_return) override;
+
+ ndk::ScopedAStatus dexopt(
+ const aidl::com::android::server::art::OutputArtifacts& in_outputArtifacts,
+ const std::string& in_dexFile,
+ const std::string& in_instructionSet,
+ const std::optional<std::string>& in_classLoaderContext,
+ const std::string& in_compilerFilter,
+ const std::optional<aidl::com::android::server::art::ProfilePath>& in_profile,
+ const std::optional<aidl::com::android::server::art::VdexPath>& in_inputVdex,
+ const std::optional<aidl::com::android::server::art::DexMetadataPath>& in_dmFile,
+ aidl::com::android::server::art::PriorityClass in_priorityClass,
+ const aidl::com::android::server::art::DexoptOptions& in_dexoptOptions,
+ const std::shared_ptr<aidl::com::android::server::art::IArtdCancellationSignal>&
+ in_cancellationSignal,
+ aidl::com::android::server::art::ArtdDexoptResult* _aidl_return) override;
+
+ ndk::ScopedAStatus createCancellationSignal(
+ std::shared_ptr<aidl::com::android::server::art::IArtdCancellationSignal>* _aidl_return)
+ override;
+
+ android::base::Result<void> Start();
+
+ private:
+ android::base::Result<OatFileAssistantContext*> GetOatFileAssistantContext()
+ EXCLUDES(ofa_context_mu_);
+
+ android::base::Result<const std::vector<std::string>*> GetBootImageLocations()
+ EXCLUDES(cache_mu_);
+
+ android::base::Result<const std::vector<std::string>*> GetBootClassPath() EXCLUDES(cache_mu_);
+
+ bool UseJitZygote() EXCLUDES(cache_mu_);
+ bool UseJitZygoteLocked() REQUIRES(cache_mu_);
+
+ const std::string& GetUserDefinedBootImageLocations() EXCLUDES(cache_mu_);
+ const std::string& GetUserDefinedBootImageLocationsLocked() REQUIRES(cache_mu_);
+
+ bool DenyArtApexDataFiles() EXCLUDES(cache_mu_);
+ bool DenyArtApexDataFilesLocked() REQUIRES(cache_mu_);
+
+ android::base::Result<int> ExecAndReturnCode(const std::vector<std::string>& arg_vector,
+ int timeout_sec,
+ const ExecCallbacks& callbacks = ExecCallbacks(),
+ ProcessStat* stat = nullptr) const;
+
+ android::base::Result<std::string> GetProfman();
+
+ android::base::Result<std::string> GetArtExec();
+
+ bool ShouldUseDex2Oat64();
+
+ android::base::Result<std::string> GetDex2Oat();
+
+ bool ShouldCreateSwapFileForDexopt();
+
+ void AddBootImageFlags(/*out*/ art::tools::CmdlineBuilder& args);
+
+ void AddCompilerConfigFlags(const std::string& instruction_set,
+ const std::string& compiler_filter,
+ aidl::com::android::server::art::PriorityClass priority_class,
+ const aidl::com::android::server::art::DexoptOptions& dexopt_options,
+ /*out*/ art::tools::CmdlineBuilder& args);
+
+ void AddPerfConfigFlags(aidl::com::android::server::art::PriorityClass priority_class,
+ /*out*/ art::tools::CmdlineBuilder& art_exec_args,
+ /*out*/ art::tools::CmdlineBuilder& args);
+
+ android::base::Result<struct stat> Fstat(const art::File& file) const;
+
+ std::mutex cache_mu_;
+ std::optional<std::vector<std::string>> cached_boot_image_locations_ GUARDED_BY(cache_mu_);
+ std::optional<std::vector<std::string>> cached_boot_class_path_ GUARDED_BY(cache_mu_);
+ std::optional<bool> cached_use_jit_zygote_ GUARDED_BY(cache_mu_);
+ std::optional<std::string> cached_user_defined_boot_image_locations_ GUARDED_BY(cache_mu_);
+ std::optional<bool> cached_deny_art_apex_data_files_ GUARDED_BY(cache_mu_);
+
+ std::mutex ofa_context_mu_;
+ std::unique_ptr<OatFileAssistantContext> ofa_context_ GUARDED_BY(ofa_context_mu_);
+
+ const std::unique_ptr<art::tools::SystemProperties> props_;
+ const std::unique_ptr<ExecUtils> exec_utils_;
+ const std::function<int(pid_t, int)> kill_;
+ const std::function<int(int, struct stat*)> fstat_;
+};
+
+} // namespace artd
+} // namespace art
+
+#endif // ART_ARTD_ARTD_H_
diff --git a/artd/artd.rc b/artd/artd.rc
index eebec2d..5ddfcdc 100644
--- a/artd/artd.rc
+++ b/artd/artd.rc
@@ -12,8 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This service is disabled until b/192042812 is resolved.
+# A lazy service that is started and stopped dynamically as needed.
service artd /apex/com.android.art/bin/artd
- disabled
+ interface aidl artd
+ disabled # Prevents the service from automatically starting at boot.
+ oneshot # Prevents the service from automatically restarting each time it is stopped.
class core
- user artd
\ No newline at end of file
+ user artd
+ group artd
+ capabilities DAC_OVERRIDE DAC_READ_SEARCH FOWNER CHOWN
diff --git a/artd/artd_main.cc b/artd/artd_main.cc
new file mode 100644
index 0000000..3644eba
--- /dev/null
+++ b/artd/artd_main.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 <stdlib.h>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android/binder_interface_utils.h"
+#include "android/binder_process.h"
+#include "artd.h"
+
+int main(int argc ATTRIBUTE_UNUSED, char* argv[]) {
+ android::base::InitLogging(argv);
+
+ auto artd = ndk::SharedRefBase::make<art::artd::Artd>();
+
+ LOG(INFO) << "Starting artd";
+
+ if (auto ret = artd->Start(); !ret.ok()) {
+ LOG(ERROR) << "Unable to start artd: " << ret.error();
+ exit(1);
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ LOG(INFO) << "artd shutting down";
+
+ return 0;
+}
diff --git a/artd/artd_test.cc b/artd/artd_test.cc
new file mode 100644
index 0000000..8d03536
--- /dev/null
+++ b/artd/artd_test.cc
@@ -0,0 +1,1822 @@
+/*
+ * Copyright (C) 2022 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 "artd.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <chrono>
+#include <condition_variable>
+#include <csignal>
+#include <filesystem>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <thread>
+#include <type_traits>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "android-base/collections.h"
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/parseint.h"
+#include "android-base/result.h"
+#include "android-base/scopeguard.h"
+#include "android-base/strings.h"
+#include "android/binder_auto_utils.h"
+#include "android/binder_status.h"
+#include "base/array_ref.h"
+#include "base/common_art_test.h"
+#include "exec_utils.h"
+#include "fmt/format.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "path_utils.h"
+#include "profman/profman_result.h"
+#include "testing.h"
+#include "tools/system_properties.h"
+
+namespace art {
+namespace artd {
+namespace {
+
+using ::aidl::com::android::server::art::ArtdDexoptResult;
+using ::aidl::com::android::server::art::ArtifactsPath;
+using ::aidl::com::android::server::art::DexMetadataPath;
+using ::aidl::com::android::server::art::DexoptOptions;
+using ::aidl::com::android::server::art::FileVisibility;
+using ::aidl::com::android::server::art::FsPermission;
+using ::aidl::com::android::server::art::IArtdCancellationSignal;
+using ::aidl::com::android::server::art::OutputArtifacts;
+using ::aidl::com::android::server::art::OutputProfile;
+using ::aidl::com::android::server::art::PriorityClass;
+using ::aidl::com::android::server::art::ProfilePath;
+using ::aidl::com::android::server::art::VdexPath;
+using ::android::base::Append;
+using ::android::base::Error;
+using ::android::base::make_scope_guard;
+using ::android::base::ParseInt;
+using ::android::base::ReadFdToString;
+using ::android::base::ReadFileToString;
+using ::android::base::Result;
+using ::android::base::ScopeGuard;
+using ::android::base::Split;
+using ::android::base::WriteStringToFd;
+using ::android::base::WriteStringToFile;
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::AnyNumber;
+using ::testing::AnyOf;
+using ::testing::Contains;
+using ::testing::ContainsRegex;
+using ::testing::DoAll;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::HasSubstr;
+using ::testing::IsEmpty;
+using ::testing::Matcher;
+using ::testing::MockFunction;
+using ::testing::Not;
+using ::testing::Property;
+using ::testing::ResultOf;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::UnorderedElementsAreArray;
+using ::testing::WithArg;
+
+using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath;
+using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath;
+using TmpProfilePath = ProfilePath::TmpProfilePath;
+
+using ::fmt::literals::operator""_format; // NOLINT
+
+ScopeGuard<std::function<void()>> ScopedSetLogger(android::base::LogFunction&& logger) {
+ android::base::LogFunction old_logger = android::base::SetLogger(std::move(logger));
+ return make_scope_guard([old_logger = std::move(old_logger)]() mutable {
+ android::base::SetLogger(std::move(old_logger));
+ });
+}
+
+void CheckContent(const std::string& path, const std::string& expected_content) {
+ std::string actual_content;
+ ASSERT_TRUE(ReadFileToString(path, &actual_content));
+ EXPECT_EQ(actual_content, expected_content);
+}
+
+void CheckOtherReadable(const std::string& path, bool expected_value) {
+ EXPECT_EQ((std::filesystem::status(path).permissions() & std::filesystem::perms::others_read) !=
+ std::filesystem::perms::none,
+ expected_value);
+}
+
+Result<std::vector<std::string>> GetFlagValues(ArrayRef<const std::string> args,
+ std::string_view flag) {
+ std::vector<std::string> values;
+ for (const std::string& arg : args) {
+ std::string_view value(arg);
+ if (android::base::ConsumePrefix(&value, flag)) {
+ values.emplace_back(value);
+ }
+ }
+ if (values.empty()) {
+ return Errorf("Flag '{}' not found", flag);
+ }
+ return values;
+}
+
+Result<std::string> GetFlagValue(ArrayRef<const std::string> args, std::string_view flag) {
+ std::vector<std::string> flag_values = OR_RETURN(GetFlagValues(args, flag));
+ if (flag_values.size() > 1) {
+ return Errorf("Duplicate flag '{}'", flag);
+ }
+ return flag_values[0];
+}
+
+void WriteToFdFlagImpl(const std::vector<std::string>& args,
+ std::string_view flag,
+ std::string_view content,
+ bool assume_empty) {
+ std::string value = OR_FAIL(GetFlagValue(ArrayRef<const std::string>(args), flag));
+ ASSERT_NE(value, "");
+ int fd;
+ ASSERT_TRUE(ParseInt(value, &fd));
+ if (assume_empty) {
+ ASSERT_EQ(lseek(fd, /*offset=*/0, SEEK_CUR), 0);
+ } else {
+ ASSERT_EQ(ftruncate(fd, /*length=*/0), 0);
+ ASSERT_EQ(lseek(fd, /*offset=*/0, SEEK_SET), 0);
+ }
+ ASSERT_TRUE(WriteStringToFd(content, fd));
+}
+
+// Writes `content` to the FD specified by the `flag`.
+ACTION_P(WriteToFdFlag, flag, content) {
+ WriteToFdFlagImpl(arg0, flag, content, /*assume_empty=*/true);
+}
+
+// Clears any existing content and writes `content` to the FD specified by the `flag`.
+ACTION_P(ClearAndWriteToFdFlag, flag, content) {
+ WriteToFdFlagImpl(arg0, flag, content, /*assume_empty=*/false);
+}
+
+// Matches a flag that starts with `flag` and whose value matches `matcher`.
+MATCHER_P2(Flag, flag, matcher, "") {
+ std::string_view value(arg);
+ if (!android::base::ConsumePrefix(&value, flag)) {
+ return false;
+ }
+ return ExplainMatchResult(matcher, std::string(value), result_listener);
+}
+
+// Matches a flag that starts with `flag` and whose value is a colon-separated list that matches
+// `matcher`. The matcher acts on an `std::vector<std::string>` of the split list argument.
+MATCHER_P2(ListFlag, flag, matcher, "") {
+ return ExplainMatchResult(
+ Flag(flag, ResultOf(std::bind(Split, std::placeholders::_1, ":"), matcher)),
+ arg,
+ result_listener);
+}
+
+// Matches an FD of a file whose path matches `matcher`.
+MATCHER_P(FdOf, matcher, "") {
+ std::string proc_path = "/proc/self/fd/{}"_format(arg);
+ char path[PATH_MAX];
+ ssize_t len = readlink(proc_path.c_str(), path, sizeof(path));
+ if (len < 0) {
+ return false;
+ }
+ return ExplainMatchResult(matcher, std::string(path, static_cast<size_t>(len)), result_listener);
+}
+
+// Matches an FD of a file whose content matches `matcher`.
+MATCHER_P(FdHasContent, matcher, "") {
+ int fd;
+ if (!ParseInt(arg, &fd)) {
+ return false;
+ }
+ std::string actual_content;
+ if (!ReadFdToString(fd, &actual_content)) {
+ return false;
+ }
+ return ExplainMatchResult(matcher, actual_content, result_listener);
+}
+
+template <typename T, typename U>
+Result<std::pair<ArrayRef<const T>, ArrayRef<const T>>> SplitBy(const std::vector<T>& list,
+ const U& separator) {
+ auto it = std::find(list.begin(), list.end(), separator);
+ if (it == list.end()) {
+ return Errorf("'{}' not found", separator);
+ }
+ size_t pos = it - list.begin();
+ return std::make_pair(ArrayRef<const T>(list).SubArray(0, pos),
+ ArrayRef<const T>(list).SubArray(pos + 1));
+}
+
+// Matches a container that, when split by `separator`, the first part matches `head_matcher`, and
+// the second part matches `tail_matcher`.
+MATCHER_P3(WhenSplitBy, separator, head_matcher, tail_matcher, "") {
+ auto [head, tail] = OR_MISMATCH(SplitBy(arg, separator));
+ return ExplainMatchResult(head_matcher, head, result_listener) &&
+ ExplainMatchResult(tail_matcher, tail, result_listener);
+}
+
+MATCHER_P(HasKeepFdsForImpl, fd_flags, "") {
+ auto [head, tail] = OR_MISMATCH(SplitBy(arg, "--"));
+ std::string keep_fds_value = OR_MISMATCH(GetFlagValue(head, "--keep-fds="));
+ std::vector<std::string> keep_fds = Split(keep_fds_value, ":");
+ std::vector<std::string> fd_flag_values;
+ for (std::string_view fd_flag : fd_flags) {
+ for (const std::string& fd_flag_value : OR_MISMATCH(GetFlagValues(tail, fd_flag))) {
+ for (std::string& fd : Split(fd_flag_value, ":")) {
+ fd_flag_values.push_back(std::move(fd));
+ }
+ }
+ }
+ return ExplainMatchResult(UnorderedElementsAreArray(fd_flag_values), keep_fds, result_listener);
+}
+
+// Matches an argument list that has the "--keep-fds=" flag before "--", whose value is a
+// semicolon-separated list that contains exactly the values of the given flags after "--".
+//
+// E.g., if the flags after "--" are "--foo=1", "--bar=2:3", "--baz=4", "--baz=5", and the matcher
+// is `HasKeepFdsFor("--foo=", "--bar=", "--baz=")`, then it requires the "--keep-fds=" flag before
+// "--" to contain exactly 1, 2, 3, 4, and 5.
+template <typename... Args>
+auto HasKeepFdsFor(Args&&... args) {
+ std::vector<std::string_view> fd_flags;
+ Append(fd_flags, std::forward<Args>(args)...);
+ return HasKeepFdsForImpl(fd_flags);
+}
+
+class MockSystemProperties : public tools::SystemProperties {
+ public:
+ MOCK_METHOD(std::string, GetProperty, (const std::string& key), (const, override));
+};
+
+class MockExecUtils : public ExecUtils {
+ public:
+ // A workaround to avoid MOCK_METHOD on a method with an `std::string*` parameter, which will lead
+ // to a conflict between gmock and android-base/logging.h (b/132668253).
+ ExecResult ExecAndReturnResult(const std::vector<std::string>& arg_vector,
+ int,
+ const ExecCallbacks& callbacks,
+ ProcessStat* stat,
+ std::string*) const override {
+ Result<int> code = DoExecAndReturnCode(arg_vector, callbacks, stat);
+ if (code.ok()) {
+ return {.status = ExecResult::kExited, .exit_code = code.value()};
+ }
+ return {.status = ExecResult::kUnknown};
+ }
+
+ MOCK_METHOD(Result<int>,
+ DoExecAndReturnCode,
+ (const std::vector<std::string>& arg_vector,
+ const ExecCallbacks& callbacks,
+ ProcessStat* stat),
+ (const));
+};
+
+class ArtdTest : public CommonArtTest {
+ protected:
+ void SetUp() override {
+ CommonArtTest::SetUp();
+ auto mock_props = std::make_unique<MockSystemProperties>();
+ mock_props_ = mock_props.get();
+ EXPECT_CALL(*mock_props_, GetProperty).Times(AnyNumber()).WillRepeatedly(Return(""));
+ auto mock_exec_utils = std::make_unique<MockExecUtils>();
+ mock_exec_utils_ = mock_exec_utils.get();
+ artd_ = ndk::SharedRefBase::make<Artd>(std::move(mock_props),
+ std::move(mock_exec_utils),
+ mock_kill_.AsStdFunction(),
+ mock_fstat_.AsStdFunction());
+ scratch_dir_ = std::make_unique<ScratchDir>();
+ scratch_path_ = scratch_dir_->GetPath();
+ // Remove the trailing '/';
+ scratch_path_.resize(scratch_path_.length() - 1);
+
+ ON_CALL(mock_fstat_, Call).WillByDefault(fstat);
+
+ // Use an arbitrary existing directory as ART root.
+ art_root_ = scratch_path_ + "/com.android.art";
+ std::filesystem::create_directories(art_root_);
+ setenv("ANDROID_ART_ROOT", art_root_.c_str(), /*overwrite=*/1);
+
+ // Use an arbitrary existing directory as Android data.
+ android_data_ = scratch_path_ + "/data";
+ std::filesystem::create_directories(android_data_);
+ setenv("ANDROID_DATA", android_data_.c_str(), /*overwrite=*/1);
+
+ dex_file_ = scratch_path_ + "/a/b.apk";
+ isa_ = "arm64";
+ artifacts_path_ = ArtifactsPath{
+ .dexPath = dex_file_,
+ .isa = isa_,
+ .isInDalvikCache = false,
+ };
+ struct stat st;
+ ASSERT_EQ(stat(scratch_path_.c_str(), &st), 0);
+ output_artifacts_ = OutputArtifacts{
+ .artifactsPath = artifacts_path_,
+ .permissionSettings =
+ OutputArtifacts::PermissionSettings{
+ .dirFsPermission =
+ FsPermission{
+ .uid = static_cast<int32_t>(st.st_uid),
+ .gid = static_cast<int32_t>(st.st_gid),
+ .isOtherReadable = true,
+ .isOtherExecutable = true,
+ },
+ .fileFsPermission =
+ FsPermission{
+ .uid = static_cast<int32_t>(st.st_uid),
+ .gid = static_cast<int32_t>(st.st_gid),
+ .isOtherReadable = true,
+ },
+ },
+ };
+ clc_1_ = GetTestDexFileName("Main");
+ clc_2_ = GetTestDexFileName("Nested");
+ class_loader_context_ = "PCL[{}:{}]"_format(clc_1_, clc_2_);
+ compiler_filter_ = "speed";
+ TmpProfilePath tmp_profile_path{
+ .finalPath =
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"},
+ .id = "12345"};
+ profile_path_ = tmp_profile_path;
+ vdex_path_ = artifacts_path_;
+ dm_path_ = DexMetadataPath{.dexPath = dex_file_};
+ std::filesystem::create_directories(
+ std::filesystem::path(OR_FATAL(BuildFinalProfilePath(tmp_profile_path))).parent_path());
+ }
+
+ void TearDown() override {
+ scratch_dir_.reset();
+ CommonArtTest::TearDown();
+ }
+
+ void RunDexopt(binder_exception_t expected_status = EX_NONE,
+ Matcher<ArtdDexoptResult> aidl_return_matcher = Field(&ArtdDexoptResult::cancelled,
+ false),
+ std::shared_ptr<IArtdCancellationSignal> cancellation_signal = nullptr) {
+ RunDexopt(Property(&ndk::ScopedAStatus::getExceptionCode, expected_status),
+ std::move(aidl_return_matcher),
+ cancellation_signal);
+ }
+
+ void RunDexopt(Matcher<ndk::ScopedAStatus> status_matcher,
+ Matcher<ArtdDexoptResult> aidl_return_matcher = Field(&ArtdDexoptResult::cancelled,
+ false),
+ std::shared_ptr<IArtdCancellationSignal> cancellation_signal = nullptr) {
+ InitFilesBeforeDexopt();
+ if (cancellation_signal == nullptr) {
+ ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
+ }
+ ArtdDexoptResult aidl_return;
+ ndk::ScopedAStatus status = artd_->dexopt(output_artifacts_,
+ dex_file_,
+ isa_,
+ class_loader_context_,
+ compiler_filter_,
+ profile_path_,
+ vdex_path_,
+ dm_path_,
+ priority_class_,
+ dexopt_options_,
+ cancellation_signal,
+ &aidl_return);
+ ASSERT_THAT(status, std::move(status_matcher)) << status.getMessage();
+ if (status.isOk()) {
+ ASSERT_THAT(aidl_return, std::move(aidl_return_matcher));
+ }
+ }
+
+ void CreateFile(const std::string& filename, const std::string& content = "") {
+ std::filesystem::path path(filename);
+ std::filesystem::create_directories(path.parent_path());
+ ASSERT_TRUE(WriteStringToFile(content, filename));
+ }
+
+ std::shared_ptr<Artd> artd_;
+ std::unique_ptr<ScratchDir> scratch_dir_;
+ std::string scratch_path_;
+ std::string art_root_;
+ std::string android_data_;
+ MockFunction<android::base::LogFunction> mock_logger_;
+ ScopedUnsetEnvironmentVariable art_root_env_ = ScopedUnsetEnvironmentVariable("ANDROID_ART_ROOT");
+ ScopedUnsetEnvironmentVariable android_data_env_ = ScopedUnsetEnvironmentVariable("ANDROID_DATA");
+ MockSystemProperties* mock_props_;
+ MockExecUtils* mock_exec_utils_;
+ MockFunction<int(pid_t, int)> mock_kill_;
+ MockFunction<int(int, struct stat*)> mock_fstat_;
+
+ std::string dex_file_;
+ std::string isa_;
+ ArtifactsPath artifacts_path_;
+ OutputArtifacts output_artifacts_;
+ std::string clc_1_;
+ std::string clc_2_;
+ std::optional<std::string> class_loader_context_;
+ std::string compiler_filter_;
+ std::optional<VdexPath> vdex_path_;
+ std::optional<DexMetadataPath> dm_path_;
+ PriorityClass priority_class_ = PriorityClass::BACKGROUND;
+ DexoptOptions dexopt_options_;
+ std::optional<ProfilePath> profile_path_;
+ bool dex_file_other_readable_ = true;
+ bool profile_other_readable_ = true;
+
+ private:
+ void InitFilesBeforeDexopt() {
+ // Required files.
+ CreateFile(dex_file_);
+ std::filesystem::permissions(dex_file_,
+ std::filesystem::perms::others_read,
+ dex_file_other_readable_ ? std::filesystem::perm_options::add :
+ std::filesystem::perm_options::remove);
+
+ // Optional files.
+ if (vdex_path_.has_value()) {
+ CreateFile(OR_FATAL(BuildVdexPath(vdex_path_.value())), "old_vdex");
+ }
+ if (dm_path_.has_value()) {
+ CreateFile(OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
+ }
+ if (profile_path_.has_value()) {
+ std::string path = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
+ CreateFile(path);
+ std::filesystem::permissions(path,
+ std::filesystem::perms::others_read,
+ profile_other_readable_ ? std::filesystem::perm_options::add :
+ std::filesystem::perm_options::remove);
+ }
+
+ // Files to be replaced.
+ std::string oat_path = OR_FATAL(BuildOatPath(artifacts_path_));
+ CreateFile(oat_path, "old_oat");
+ CreateFile(OatPathToVdexPath(oat_path), "old_vdex");
+ CreateFile(OatPathToArtPath(oat_path), "old_art");
+ }
+};
+
+TEST_F(ArtdTest, isAlive) {
+ bool result = false;
+ artd_->isAlive(&result);
+ EXPECT_TRUE(result);
+}
+
+TEST_F(ArtdTest, deleteArtifacts) {
+ std::string oat_dir = scratch_path_ + "/a/oat/arm64";
+ std::filesystem::create_directories(oat_dir);
+ ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
+ ASSERT_TRUE(WriteStringToFile("ab", oat_dir + "/b.vdex")); // 2 bytes.
+ ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
+
+ int64_t result = -1;
+ EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
+ EXPECT_EQ(result, 4 + 2 + 1);
+
+ EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.odex"));
+ EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.vdex"));
+ EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art"));
+}
+
+TEST_F(ArtdTest, deleteArtifactsMissingFile) {
+ // Missing VDEX file.
+ std::string oat_dir = android_data_ + "/dalvik-cache/arm64";
+ std::filesystem::create_directories(oat_dir);
+ ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/a@b.apk@classes.dex")); // 4 bytes.
+ ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/a@b.apk@classes.art")); // 1 byte.
+
+ auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
+ EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(0);
+
+ int64_t result = -1;
+ EXPECT_TRUE(artd_
+ ->deleteArtifacts(
+ ArtifactsPath{
+ .dexPath = "/a/b.apk",
+ .isa = "arm64",
+ .isInDalvikCache = true,
+ },
+ &result)
+ .isOk());
+ EXPECT_EQ(result, 4 + 1);
+
+ EXPECT_FALSE(std::filesystem::exists(oat_dir + "/a@b.apk@classes.dex"));
+ EXPECT_FALSE(std::filesystem::exists(oat_dir + "/a@b.apk@classes.art"));
+}
+
+TEST_F(ArtdTest, deleteArtifactsNoFile) {
+ auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
+ EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(0);
+
+ int64_t result = -1;
+ EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
+ EXPECT_EQ(result, 0);
+}
+
+TEST_F(ArtdTest, deleteArtifactsPermissionDenied) {
+ std::string oat_dir = scratch_path_ + "/a/oat/arm64";
+ std::filesystem::create_directories(oat_dir);
+ ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
+ ASSERT_TRUE(WriteStringToFile("ab", oat_dir + "/b.vdex")); // 2 bytes.
+ ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
+
+ auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
+ EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(3);
+
+ auto scoped_inaccessible = ScopedInaccessible(oat_dir);
+ auto scoped_unroot = ScopedUnroot();
+
+ int64_t result = -1;
+ EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
+ EXPECT_EQ(result, 0);
+}
+
+TEST_F(ArtdTest, deleteArtifactsFileIsDir) {
+ // VDEX file is a directory.
+ std::string oat_dir = scratch_path_ + "/a/oat/arm64";
+ std::filesystem::create_directories(oat_dir);
+ std::filesystem::create_directories(oat_dir + "/b.vdex");
+ ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
+ ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
+
+ auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
+ EXPECT_CALL(mock_logger_,
+ Call(_, _, _, _, _, ContainsRegex(R"re(Failed to get the file size.*b\.vdex)re")))
+ .Times(1);
+
+ int64_t result = -1;
+ EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
+ EXPECT_EQ(result, 4 + 1);
+
+ // The directory is kept because getting the file size failed.
+ EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.odex"));
+ EXPECT_TRUE(std::filesystem::exists(oat_dir + "/b.vdex"));
+ EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art"));
+}
+
+TEST_F(ArtdTest, dexopt) {
+ dexopt_options_.generateAppImage = true;
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(WhenSplitBy(
+ "--",
+ AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
+ AllOf(Contains(art_root_ + "/bin/dex2oat32"),
+ Contains(Flag("--zip-fd=", FdOf(dex_file_))),
+ Contains(Flag("--zip-location=", dex_file_)),
+ Contains(Flag("--oat-location=", scratch_path_ + "/a/oat/arm64/b.odex")),
+ Contains(Flag("--instruction-set=", "arm64")),
+ Contains(Flag("--compiler-filter=", "speed")),
+ Contains(Flag(
+ "--profile-file-fd=",
+ FdOf(android_data_ +
+ "/misc/profiles/ref/com.android.foo/primary.prof.12345.tmp"))),
+ Contains(Flag("--input-vdex-fd=",
+ FdOf(scratch_path_ + "/a/oat/arm64/b.vdex"))),
+ Contains(Flag("--dm-fd=", FdOf(scratch_path_ + "/a/b.dm"))))),
+ HasKeepFdsFor("--zip-fd=",
+ "--profile-file-fd=",
+ "--input-vdex-fd=",
+ "--dm-fd=",
+ "--oat-fd=",
+ "--output-vdex-fd=",
+ "--app-image-fd=",
+ "--class-loader-context-fds=",
+ "--swap-fd=")),
+ _,
+ _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "oat")),
+ WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "vdex")),
+ WithArg<0>(WriteToFdFlag("--app-image-fd=", "art")),
+ SetArgPointee<2>(ProcessStat{.wall_time_ms = 100, .cpu_time_ms = 400}),
+ Return(0)));
+ RunDexopt(
+ EX_NONE,
+ AllOf(Field(&ArtdDexoptResult::cancelled, false),
+ Field(&ArtdDexoptResult::wallTimeMs, 100),
+ Field(&ArtdDexoptResult::cpuTimeMs, 400),
+ Field(&ArtdDexoptResult::sizeBytes, strlen("art") + strlen("oat") + strlen("vdex")),
+ Field(&ArtdDexoptResult::sizeBeforeBytes,
+ strlen("old_art") + strlen("old_oat") + strlen("old_vdex"))));
+
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "oat");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "vdex");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "art");
+ CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.odex", true);
+ CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.vdex", true);
+ CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.art", true);
+}
+
+TEST_F(ArtdTest, dexoptClassLoaderContext) {
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ _,
+ AllOf(Contains(ListFlag("--class-loader-context-fds=",
+ ElementsAre(FdOf(clc_1_), FdOf(clc_2_)))),
+ Contains(Flag("--class-loader-context=", class_loader_context_)),
+ Contains(Flag("--classpath-dir=", scratch_path_ + "/a")))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptClassLoaderContextNull) {
+ class_loader_context_ = std::nullopt;
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(WhenSplitBy("--",
+ _,
+ AllOf(Not(Contains(Flag("--class-loader-context-fds=", _))),
+ Not(Contains(Flag("--class-loader-context=", _))),
+ Not(Contains(Flag("--classpath-dir=", _))))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptNoOptionalInputFiles) {
+ profile_path_ = std::nullopt;
+ vdex_path_ = std::nullopt;
+ dm_path_ = std::nullopt;
+
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(WhenSplitBy("--",
+ _,
+ AllOf(Not(Contains(Flag("--profile-file-fd=", _))),
+ Not(Contains(Flag("--input-vdex-fd=", _))),
+ Not(Contains(Flag("--dm-fd=", _))))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptPriorityClassBoot) {
+ priority_class_ = PriorityClass::BOOT;
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(WhenSplitBy("--",
+ AllOf(Not(Contains(Flag("--set-task-profile=", _))),
+ Not(Contains(Flag("--set-priority=", _)))),
+ Contains(Flag("--compact-dex-level=", "none"))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptPriorityClassInteractive) {
+ priority_class_ = PriorityClass::INTERACTIVE;
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBootComplete")),
+ Contains(Flag("--set-priority=", "background"))),
+ Contains(Flag("--compact-dex-level=", "none"))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptPriorityClassInteractiveFast) {
+ priority_class_ = PriorityClass::INTERACTIVE_FAST;
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBootComplete")),
+ Contains(Flag("--set-priority=", "background"))),
+ Contains(Flag("--compact-dex-level=", "none"))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptPriorityClassBackground) {
+ priority_class_ = PriorityClass::BACKGROUND;
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBackground")),
+ Contains(Flag("--set-priority=", "background"))),
+ Not(Contains(Flag("--compact-dex-level=", _)))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptDexoptOptions) {
+ dexopt_options_ = DexoptOptions{
+ .compilationReason = "install",
+ .targetSdkVersion = 123,
+ .debuggable = false,
+ .generateAppImage = false,
+ .hiddenApiPolicyEnabled = false,
+ };
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(WhenSplitBy("--",
+ _,
+ AllOf(Contains(Flag("--compilation-reason=", "install")),
+ Contains(Flag("-Xtarget-sdk-version:", "123")),
+ Not(Contains("--debuggable")),
+ Not(Contains(Flag("--app-image-fd=", _))),
+ Not(Contains(Flag("-Xhidden-api-policy:", _))))),
+ _,
+ _))
+ .WillOnce(Return(0));
+
+ // `sizeBeforeBytes` should include the size of the old ART file even if no new ART file is
+ // generated.
+ RunDexopt(EX_NONE,
+ Field(&ArtdDexoptResult::sizeBeforeBytes,
+ strlen("old_art") + strlen("old_oat") + strlen("old_vdex")));
+}
+
+TEST_F(ArtdTest, dexoptDexoptOptions2) {
+ dexopt_options_ = DexoptOptions{
+ .compilationReason = "bg-dexopt",
+ .targetSdkVersion = 456,
+ .debuggable = true,
+ .generateAppImage = true,
+ .hiddenApiPolicyEnabled = true,
+ };
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(WhenSplitBy("--",
+ _,
+ AllOf(Contains(Flag("--compilation-reason=", "bg-dexopt")),
+ Contains(Flag("-Xtarget-sdk-version:", "456")),
+ Contains("--debuggable"),
+ Contains(Flag("--app-image-fd=", _)),
+ Contains(Flag("-Xhidden-api-policy:", "enabled")))),
+ _,
+ _))
+ .WillOnce(Return(0));
+
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptDefaultFlagsWhenNoSystemProps) {
+ dexopt_options_.generateAppImage = true;
+
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ _,
+ AllOf(Contains(Flag("--swap-fd=", FdOf(_))),
+ Not(Contains(Flag("--instruction-set-features=", _))),
+ Not(Contains(Flag("--instruction-set-variant=", _))),
+ Not(Contains(Flag("--max-image-block-size=", _))),
+ Not(Contains(Flag("--very-large-app-threshold=", _))),
+ Not(Contains(Flag("--resolve-startup-const-strings=", _))),
+ Not(Contains("--generate-debug-info")),
+ Not(Contains("--generate-mini-debug-info")),
+ Contains("-Xdeny-art-apex-data-files"),
+ Not(Contains(Flag("--cpu-set=", _))),
+ Not(Contains(Flag("-j", _))),
+ Not(Contains(Flag("-Xms", _))),
+ Not(Contains(Flag("-Xmx", _))),
+ Not(Contains("--compile-individually")),
+ Not(Contains(Flag("--image-format=", _))),
+ Not(Contains("--force-jit-zygote")),
+ Not(Contains(Flag("--boot-image=", _))))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptFlagsFromSystemProps) {
+ dexopt_options_.generateAppImage = true;
+
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-swap")).WillOnce(Return("0"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.isa.arm64.features"))
+ .WillOnce(Return("features"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.isa.arm64.variant")).WillOnce(Return("variant"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-max-image-block-size"))
+ .WillOnce(Return("size"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-very-large"))
+ .WillOnce(Return("threshold"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-resolve-startup-strings"))
+ .WillOnce(Return("strings"));
+ EXPECT_CALL(*mock_props_, GetProperty("debug.generate-debug-info")).WillOnce(Return("1"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-minidebuginfo")).WillOnce(Return("1"));
+ EXPECT_CALL(*mock_props_, GetProperty("odsign.verification.success")).WillOnce(Return("1"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-Xms")).WillOnce(Return("xms"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-Xmx")).WillOnce(Return("xmx"));
+ EXPECT_CALL(*mock_props_, GetProperty("ro.config.low_ram")).WillOnce(Return("1"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.appimageformat")).WillOnce(Return("imgfmt"));
+ EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.boot-image")).WillOnce(Return("boot-image"));
+
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ _,
+ AllOf(Not(Contains(Flag("--swap-fd=", _))),
+ Contains(Flag("--instruction-set-features=", "features")),
+ Contains(Flag("--instruction-set-variant=", "variant")),
+ Contains(Flag("--max-image-block-size=", "size")),
+ Contains(Flag("--very-large-app-threshold=", "threshold")),
+ Contains(Flag("--resolve-startup-const-strings=", "strings")),
+ Contains("--generate-debug-info"),
+ Contains("--generate-mini-debug-info"),
+ Not(Contains("-Xdeny-art-apex-data-files")),
+ Contains(Flag("-Xms", "xms")),
+ Contains(Flag("-Xmx", "xmx")),
+ Contains("--compile-individually"),
+ Contains(Flag("--image-format=", "imgfmt")),
+ Not(Contains("--force-jit-zygote")),
+ Contains(Flag("--boot-image=", "boot-image")))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptFlagsForceJitZygote) {
+ EXPECT_CALL(*mock_props_,
+ GetProperty("persist.device_config.runtime_native_boot.profilebootclasspath"))
+ .WillOnce(Return("true"));
+ ON_CALL(*mock_props_, GetProperty("dalvik.vm.boot-image")).WillByDefault(Return("boot-image"));
+
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(WhenSplitBy("--",
+ _,
+ AllOf(Contains("--force-jit-zygote"),
+ Not(Contains(Flag("--boot-image=", _))))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ RunDexopt();
+}
+
+static void SetDefaultResourceControlProps(MockSystemProperties* mock_props) {
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-cpu-set")).WillRepeatedly(Return("0,2"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-threads")).WillRepeatedly(Return("4"));
+}
+
+TEST_F(ArtdTest, dexoptDefaultResourceControlBoot) {
+ SetDefaultResourceControlProps(mock_props_);
+
+ // The default resource control properties don't apply to BOOT.
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy(
+ "--", _, AllOf(Not(Contains(Flag("--cpu-set=", _))), Contains(Not(Flag("-j", _))))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ priority_class_ = PriorityClass::BOOT;
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptDefaultResourceControlOther) {
+ SetDefaultResourceControlProps(mock_props_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy(
+ "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2")), Contains(Flag("-j", "4")))),
+ _,
+ _))
+ .Times(3)
+ .WillRepeatedly(Return(0));
+ priority_class_ = PriorityClass::INTERACTIVE_FAST;
+ RunDexopt();
+ priority_class_ = PriorityClass::INTERACTIVE;
+ RunDexopt();
+ priority_class_ = PriorityClass::BACKGROUND;
+ RunDexopt();
+}
+
+static void SetAllResourceControlProps(MockSystemProperties* mock_props) {
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-cpu-set")).WillRepeatedly(Return("0,2"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-threads")).WillRepeatedly(Return("4"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.boot-dex2oat-cpu-set"))
+ .WillRepeatedly(Return("0,1,2,3"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.boot-dex2oat-threads"))
+ .WillRepeatedly(Return("8"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.restore-dex2oat-cpu-set"))
+ .WillRepeatedly(Return("0,2,3"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.restore-dex2oat-threads"))
+ .WillRepeatedly(Return("6"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.background-dex2oat-cpu-set"))
+ .WillRepeatedly(Return("0"));
+ EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.background-dex2oat-threads"))
+ .WillRepeatedly(Return("2"));
+}
+
+TEST_F(ArtdTest, dexoptAllResourceControlBoot) {
+ SetAllResourceControlProps(mock_props_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy(
+ "--", _, AllOf(Contains(Flag("--cpu-set=", "0,1,2,3")), Contains(Flag("-j", "8")))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ priority_class_ = PriorityClass::BOOT;
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptAllResourceControlInteractiveFast) {
+ SetAllResourceControlProps(mock_props_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy(
+ "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2,3")), Contains(Flag("-j", "6")))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ priority_class_ = PriorityClass::INTERACTIVE_FAST;
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptAllResourceControlInteractive) {
+ SetAllResourceControlProps(mock_props_);
+
+ // INTERACTIVE always uses the default resource control properties.
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy(
+ "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2")), Contains(Flag("-j", "4")))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ priority_class_ = PriorityClass::INTERACTIVE;
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptAllResourceControlBackground) {
+ SetAllResourceControlProps(mock_props_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--", _, AllOf(Contains(Flag("--cpu-set=", "0")), Contains(Flag("-j", "2")))),
+ _,
+ _))
+ .WillOnce(Return(0));
+ priority_class_ = PriorityClass::BACKGROUND;
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, dexoptFailed) {
+ dexopt_options_.generateAppImage = true;
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
+ WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
+ WithArg<0>(WriteToFdFlag("--app-image-fd=", "new_art")),
+ Return(1)));
+ RunDexopt(EX_SERVICE_SPECIFIC);
+
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
+}
+
+TEST_F(ArtdTest, dexoptFailedToCommit) {
+ std::unique_ptr<ScopeGuard<std::function<void()>>> scoped_inaccessible;
+ std::unique_ptr<ScopeGuard<std::function<void()>>> scoped_unroot;
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
+ WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
+ [&](auto, auto, auto) {
+ scoped_inaccessible = std::make_unique<ScopeGuard<std::function<void()>>>(
+ ScopedInaccessible(scratch_path_ + "/a/oat/arm64"));
+ scoped_unroot =
+ std::make_unique<ScopeGuard<std::function<void()>>>(ScopedUnroot());
+ return 0;
+ }));
+
+ RunDexopt(
+ EX_SERVICE_SPECIFIC,
+ AllOf(Field(&ArtdDexoptResult::sizeBytes, 0), Field(&ArtdDexoptResult::sizeBeforeBytes, 0)));
+}
+
+TEST_F(ArtdTest, dexoptCancelledBeforeDex2oat) {
+ std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
+ ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
+
+ constexpr pid_t kPid = 123;
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
+ .WillOnce([&](auto, const ExecCallbacks& callbacks, auto) {
+ callbacks.on_start(kPid);
+ callbacks.on_end(kPid);
+ return Error();
+ });
+ EXPECT_CALL(mock_kill_, Call(kPid, SIGKILL));
+
+ cancellation_signal->cancel();
+
+ RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, true), cancellation_signal);
+
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
+}
+
+TEST_F(ArtdTest, dexoptCancelledDuringDex2oat) {
+ std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
+ ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
+
+ constexpr pid_t kPid = 123;
+ constexpr std::chrono::duration<int> kTimeout = std::chrono::seconds(1);
+
+ std::condition_variable process_started_cv, process_killed_cv;
+ std::mutex mu;
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
+ .WillOnce([&](auto, const ExecCallbacks& callbacks, auto) {
+ std::unique_lock<std::mutex> lock(mu);
+ // Step 2.
+ callbacks.on_start(kPid);
+ process_started_cv.notify_one();
+ EXPECT_EQ(process_killed_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
+ // Step 5.
+ callbacks.on_end(kPid);
+ return Error();
+ });
+
+ EXPECT_CALL(mock_kill_, Call(kPid, SIGKILL)).WillOnce([&](auto, auto) {
+ // Step 4.
+ process_killed_cv.notify_one();
+ return 0;
+ });
+
+ std::thread t;
+ {
+ std::unique_lock<std::mutex> lock(mu);
+ // Step 1.
+ t = std::thread([&] {
+ RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, true), cancellation_signal);
+ });
+ EXPECT_EQ(process_started_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
+ // Step 3.
+ cancellation_signal->cancel();
+ }
+
+ t.join();
+
+ // Step 6.
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
+}
+
+TEST_F(ArtdTest, dexoptCancelledAfterDex2oat) {
+ std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
+ ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
+
+ constexpr pid_t kPid = 123;
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
+ WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
+ [&](auto, const ExecCallbacks& callbacks, auto) {
+ callbacks.on_start(kPid);
+ callbacks.on_end(kPid);
+ return 0;
+ }));
+ EXPECT_CALL(mock_kill_, Call).Times(0);
+
+ RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, false), cancellation_signal);
+
+ // This signal should be ignored.
+ cancellation_signal->cancel();
+
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "new_oat");
+ CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "new_vdex");
+ EXPECT_FALSE(std::filesystem::exists(scratch_path_ + "/a/oat/arm64/b.art"));
+}
+
+TEST_F(ArtdTest, dexoptDexFileNotOtherReadable) {
+ dex_file_other_readable_ = false;
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
+ RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
+ Property(&ndk::ScopedAStatus::getMessage,
+ HasSubstr("Outputs cannot be other-readable because the dex file"))));
+}
+
+TEST_F(ArtdTest, dexoptProfileNotOtherReadable) {
+ profile_other_readable_ = false;
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
+ RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
+ Property(&ndk::ScopedAStatus::getMessage,
+ HasSubstr("Outputs cannot be other-readable because the profile"))));
+}
+
+TEST_F(ArtdTest, dexoptOutputNotOtherReadable) {
+ output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
+ dex_file_other_readable_ = false;
+ profile_other_readable_ = false;
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(0));
+ RunDexopt();
+ CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.odex", false);
+ CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.vdex", false);
+}
+
+TEST_F(ArtdTest, dexoptUidMismatch) {
+ output_artifacts_.permissionSettings.fileFsPermission.uid = 12345;
+ output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
+ dex_file_other_readable_ = false;
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
+ RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
+ Property(&ndk::ScopedAStatus::getMessage,
+ HasSubstr("Outputs' owner doesn't match the dex file"))));
+}
+
+TEST_F(ArtdTest, dexoptGidMismatch) {
+ output_artifacts_.permissionSettings.fileFsPermission.gid = 12345;
+ output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
+ dex_file_other_readable_ = false;
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
+ RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
+ Property(&ndk::ScopedAStatus::getMessage,
+ HasSubstr("Outputs' owner doesn't match the dex file"))));
+}
+
+TEST_F(ArtdTest, dexoptGidMatchesUid) {
+ output_artifacts_.permissionSettings.fileFsPermission = {
+ .uid = 123, .gid = 123, .isOtherReadable = false};
+ EXPECT_CALL(mock_fstat_, Call(_, _)).WillRepeatedly(fstat); // For profile.
+ EXPECT_CALL(mock_fstat_, Call(FdOf(dex_file_), _))
+ .WillOnce(DoAll(SetArgPointee<1>((struct stat){
+ .st_mode = S_IRUSR | S_IRGRP, .st_uid = 123, .st_gid = 456}),
+ Return(0)));
+ ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
+ // It's okay to fail on chown. This happens when the test is not run as root.
+ RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
+ AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
+ Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
+}
+
+TEST_F(ArtdTest, dexoptGidMatchesGid) {
+ output_artifacts_.permissionSettings.fileFsPermission = {
+ .uid = 123, .gid = 456, .isOtherReadable = false};
+ EXPECT_CALL(mock_fstat_, Call(_, _)).WillRepeatedly(fstat); // For profile.
+ EXPECT_CALL(mock_fstat_, Call(FdOf(dex_file_), _))
+ .WillOnce(DoAll(SetArgPointee<1>((struct stat){
+ .st_mode = S_IRUSR | S_IRGRP, .st_uid = 123, .st_gid = 456}),
+ Return(0)));
+ ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
+ // It's okay to fail on chown. This happens when the test is not run as root.
+ RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
+ AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
+ Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
+}
+
+TEST_F(ArtdTest, dexoptUidGidChangeOk) {
+ // The dex file is other-readable, so we don't check uid and gid.
+ output_artifacts_.permissionSettings.fileFsPermission = {
+ .uid = 12345, .gid = 12345, .isOtherReadable = false};
+ ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
+ // It's okay to fail on chown. This happens when the test is not run as root.
+ RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
+ AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
+ Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
+}
+
+TEST_F(ArtdTest, dexoptNoUidGidChange) {
+ output_artifacts_.permissionSettings.fileFsPermission = {
+ .uid = -1, .gid = -1, .isOtherReadable = false};
+ dex_file_other_readable_ = false;
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(0));
+ RunDexopt();
+}
+
+TEST_F(ArtdTest, isProfileUsable) {
+ std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
+ CreateFile(profile_file);
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(WhenSplitBy(
+ "--",
+ AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
+ AllOf(Contains(art_root_ + "/bin/profman"),
+ Contains(Flag("--reference-profile-file-fd=", FdOf(profile_file))),
+ Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
+ HasKeepFdsFor("--reference-profile-file-fd=", "--apk-fd=")),
+ _,
+ _))
+ .WillOnce(Return(ProfmanResult::kSkipCompilationSmallDelta));
+
+ bool result;
+ EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
+ EXPECT_TRUE(result);
+}
+
+TEST_F(ArtdTest, isProfileUsableFalse) {
+ std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
+ CreateFile(profile_file);
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
+ .WillOnce(Return(ProfmanResult::kSkipCompilationEmptyProfiles));
+
+ bool result;
+ EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
+ EXPECT_FALSE(result);
+}
+
+TEST_F(ArtdTest, isProfileUsableNotFound) {
+ CreateFile(dex_file_);
+
+ bool result;
+ EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
+ EXPECT_FALSE(result);
+}
+
+TEST_F(ArtdTest, isProfileUsableFailed) {
+ std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
+ CreateFile(profile_file);
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(100));
+
+ bool result;
+ ndk::ScopedAStatus status = artd_->isProfileUsable(profile_path_.value(), dex_file_, &result);
+
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ EXPECT_THAT(status.getMessage(), HasSubstr("profman returned an unexpected code: 100"));
+}
+
+TEST_F(ArtdTest, copyAndRewriteProfile) {
+ const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
+ std::string src_file = OR_FATAL(BuildTmpProfilePath(src));
+ CreateFile(src_file, "abc");
+ OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ dst.profilePath.id = "";
+ dst.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(WhenSplitBy(
+ "--",
+ AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
+ AllOf(Contains(art_root_ + "/bin/profman"),
+ Contains("--copy-and-update-profile-key"),
+ Contains(Flag("--profile-file-fd=", FdOf(src_file))),
+ Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
+ HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
+ _,
+ _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")),
+ Return(ProfmanResult::kCopyAndUpdateSuccess)));
+
+ bool result;
+ EXPECT_TRUE(artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result).isOk());
+ EXPECT_TRUE(result);
+ EXPECT_THAT(dst.profilePath.id, Not(IsEmpty()));
+ std::string real_path = OR_FATAL(BuildTmpProfilePath(dst.profilePath));
+ EXPECT_EQ(dst.profilePath.tmpPath, real_path);
+ CheckContent(real_path, "def");
+}
+
+TEST_F(ArtdTest, copyAndRewriteProfileFalse) {
+ const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
+ std::string src_file = OR_FATAL(BuildTmpProfilePath(src));
+ CreateFile(src_file, "abc");
+ OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ dst.profilePath.id = "";
+ dst.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
+ .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
+
+ bool result;
+ EXPECT_TRUE(artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_THAT(dst.profilePath.id, IsEmpty());
+ EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
+}
+
+TEST_F(ArtdTest, copyAndRewriteProfileNotFound) {
+ CreateFile(dex_file_);
+
+ const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
+ OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ dst.profilePath.id = "";
+ dst.profilePath.tmpPath = "";
+
+ bool result;
+ EXPECT_TRUE(artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result).isOk());
+ EXPECT_FALSE(result);
+ EXPECT_THAT(dst.profilePath.id, IsEmpty());
+ EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
+}
+
+TEST_F(ArtdTest, copyAndRewriteProfileFailed) {
+ const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
+ std::string src_file = OR_FATAL(BuildTmpProfilePath(src));
+ CreateFile(src_file, "abc");
+ OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ dst.profilePath.id = "";
+ dst.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(100));
+
+ bool result;
+ ndk::ScopedAStatus status = artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result);
+
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ EXPECT_THAT(status.getMessage(), HasSubstr("profman returned an unexpected code: 100"));
+ EXPECT_THAT(dst.profilePath.id, IsEmpty());
+ EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
+}
+
+TEST_F(ArtdTest, commitTmpProfile) {
+ const TmpProfilePath& tmp_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
+ std::string tmp_profile_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path));
+ CreateFile(tmp_profile_file);
+
+ EXPECT_TRUE(artd_->commitTmpProfile(tmp_profile_path).isOk());
+
+ EXPECT_FALSE(std::filesystem::exists(tmp_profile_file));
+ EXPECT_TRUE(std::filesystem::exists(OR_FATAL(BuildFinalProfilePath(tmp_profile_path))));
+}
+
+TEST_F(ArtdTest, commitTmpProfileFailed) {
+ const TmpProfilePath& tmp_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
+ ndk::ScopedAStatus status = artd_->commitTmpProfile(tmp_profile_path);
+
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ EXPECT_THAT(
+ status.getMessage(),
+ ContainsRegex(R"re(Failed to move .*primary\.prof\.12345\.tmp.* to .*primary\.prof)re"));
+
+ EXPECT_FALSE(std::filesystem::exists(OR_FATAL(BuildFinalProfilePath(tmp_profile_path))));
+}
+
+TEST_F(ArtdTest, deleteProfile) {
+ std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
+ CreateFile(profile_file);
+
+ EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
+
+ EXPECT_FALSE(std::filesystem::exists(profile_file));
+}
+
+TEST_F(ArtdTest, deleteProfileDoesNotExist) {
+ std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
+ EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
+}
+
+TEST_F(ArtdTest, deleteProfileFailed) {
+ auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
+ EXPECT_CALL(
+ mock_logger_,
+ Call(_, _, _, _, _, ContainsRegex(R"re(Failed to remove .*primary\.prof\.12345\.tmp)re")));
+
+ EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
+}
+
+class ArtdGetVisibilityTest : public ArtdTest {
+ protected:
+ template <typename PathType>
+ using Method = ndk::ScopedAStatus (Artd::*)(const PathType&, FileVisibility*);
+
+ template <typename PathType>
+ void TestGetVisibilityOtherReadable(Method<PathType> method,
+ const PathType& input,
+ const std::string& path) {
+ CreateFile(path);
+ std::filesystem::permissions(
+ path, std::filesystem::perms::others_read, std::filesystem::perm_options::add);
+
+ FileVisibility result;
+ ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
+ EXPECT_EQ(result, FileVisibility::OTHER_READABLE);
+ }
+
+ template <typename PathType>
+ void TestGetVisibilityNotOtherReadable(Method<PathType> method,
+ const PathType& input,
+ const std::string& path) {
+ CreateFile(path);
+ std::filesystem::permissions(
+ path, std::filesystem::perms::others_read, std::filesystem::perm_options::remove);
+
+ FileVisibility result;
+ ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
+ EXPECT_EQ(result, FileVisibility::NOT_OTHER_READABLE);
+ }
+
+ template <typename PathType>
+ void TestGetVisibilityNotFound(Method<PathType> method, const PathType& input) {
+ FileVisibility result;
+ ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
+ EXPECT_EQ(result, FileVisibility::NOT_FOUND);
+ }
+
+ template <typename PathType>
+ void TestGetVisibilityPermissionDenied(Method<PathType> method,
+ const PathType& input,
+ const std::string& path) {
+ CreateFile(path);
+
+ auto scoped_inaccessible = ScopedInaccessible(std::filesystem::path(path).parent_path());
+ auto scoped_unroot = ScopedUnroot();
+
+ FileVisibility result;
+ ndk::ScopedAStatus status = ((*artd_).*method)(input, &result);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
+ EXPECT_THAT(status.getMessage(), HasSubstr("Failed to get status of"));
+ }
+};
+
+TEST_F(ArtdGetVisibilityTest, getProfileVisibilityOtherReadable) {
+ TestGetVisibilityOtherReadable(&Artd::getProfileVisibility,
+ profile_path_.value(),
+ OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
+}
+
+TEST_F(ArtdGetVisibilityTest, getProfileVisibilityNotOtherReadable) {
+ TestGetVisibilityNotOtherReadable(&Artd::getProfileVisibility,
+ profile_path_.value(),
+ OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
+}
+
+TEST_F(ArtdGetVisibilityTest, getProfileVisibilityNotFound) {
+ TestGetVisibilityNotFound(&Artd::getProfileVisibility, profile_path_.value());
+}
+
+TEST_F(ArtdGetVisibilityTest, getProfileVisibilityPermissionDenied) {
+ TestGetVisibilityPermissionDenied(&Artd::getProfileVisibility,
+ profile_path_.value(),
+ OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
+}
+
+TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityOtherReadable) {
+ TestGetVisibilityOtherReadable(
+ &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_)));
+}
+
+TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotOtherReadable) {
+ TestGetVisibilityNotOtherReadable(
+ &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_)));
+}
+
+TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotFound) {
+ TestGetVisibilityNotFound(&Artd::getArtifactsVisibility, artifacts_path_);
+}
+
+TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityPermissionDenied) {
+ TestGetVisibilityPermissionDenied(
+ &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_)));
+}
+
+TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityOtherReadable) {
+ TestGetVisibilityOtherReadable(&Artd::getDexFileVisibility, dex_file_, dex_file_);
+}
+
+TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityNotOtherReadable) {
+ TestGetVisibilityNotOtherReadable(&Artd::getDexFileVisibility, dex_file_, dex_file_);
+}
+
+TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityNotFound) {
+ TestGetVisibilityNotFound(&Artd::getDexFileVisibility, dex_file_);
+}
+
+TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityPermissionDenied) {
+ TestGetVisibilityPermissionDenied(&Artd::getDexFileVisibility, dex_file_, dex_file_);
+}
+
+TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityOtherReadable) {
+ TestGetVisibilityOtherReadable(&Artd::getDmFileVisibility,
+ dm_path_.value(),
+ OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
+}
+
+TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityNotOtherReadable) {
+ TestGetVisibilityNotOtherReadable(&Artd::getDmFileVisibility,
+ dm_path_.value(),
+ OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
+}
+
+TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityNotFound) {
+ TestGetVisibilityNotFound(&Artd::getDmFileVisibility, dm_path_.value());
+}
+
+TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityPermissionDenied) {
+ TestGetVisibilityPermissionDenied(&Artd::getDmFileVisibility,
+ dm_path_.value(),
+ OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
+}
+
+TEST_F(ArtdTest, mergeProfiles) {
+ const TmpProfilePath& reference_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
+ std::string reference_profile_file = OR_FATAL(BuildTmpProfilePath(reference_profile_path));
+ CreateFile(reference_profile_file, "abc");
+
+ // Doesn't exist.
+ PrimaryCurProfilePath profile_0_path{
+ .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
+
+ PrimaryCurProfilePath profile_1_path{
+ .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
+ CreateFile(profile_1_file, "def");
+
+ OutputProfile output_profile{.profilePath = reference_profile_path,
+ .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ output_profile.profilePath.id = "";
+ output_profile.profilePath.tmpPath = "";
+
+ std::string dex_file_1 = scratch_path_ + "/a/b.apk";
+ std::string dex_file_2 = scratch_path_ + "/a/c.apk";
+ CreateFile(dex_file_1);
+ CreateFile(dex_file_2);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(WhenSplitBy(
+ "--",
+ AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
+ AllOf(Contains(art_root_ + "/bin/profman"),
+ Not(Contains(Flag("--profile-file-fd=", FdOf(profile_0_file)))),
+ Contains(Flag("--profile-file-fd=", FdOf(profile_1_file))),
+ Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))),
+ Contains(Flag("--apk-fd=", FdOf(dex_file_1))),
+ Contains(Flag("--apk-fd=", FdOf(dex_file_2))),
+ Not(Contains("--force-merge")),
+ Not(Contains("--boot-image-merge")))),
+ HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
+ _,
+ _))
+ .WillOnce(DoAll(WithArg<0>(ClearAndWriteToFdFlag("--reference-profile-file-fd=", "merged")),
+ Return(ProfmanResult::kCompile)));
+
+ bool result;
+ EXPECT_TRUE(artd_
+ ->mergeProfiles({profile_0_path, profile_1_path},
+ reference_profile_path,
+ &output_profile,
+ {dex_file_1, dex_file_2},
+ /*in_options=*/{},
+ &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
+ std::string real_path = OR_FATAL(BuildTmpProfilePath(output_profile.profilePath));
+ EXPECT_EQ(output_profile.profilePath.tmpPath, real_path);
+ CheckContent(real_path, "merged");
+}
+
+TEST_F(ArtdTest, mergeProfilesEmptyReferenceProfile) {
+ PrimaryCurProfilePath profile_0_path{
+ .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
+ CreateFile(profile_0_file, "def");
+
+ OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
+ .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ output_profile.profilePath.id = "";
+ output_profile.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
+ AllOf(Contains(art_root_ + "/bin/profman"),
+ Contains(Flag("--profile-file-fd=", FdOf(profile_0_file))),
+ Contains(Flag("--reference-profile-file-fd=", FdHasContent(""))),
+ Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
+ _,
+ _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "merged")),
+ Return(ProfmanResult::kCompile)));
+
+ bool result;
+ EXPECT_TRUE(artd_
+ ->mergeProfiles({profile_0_path},
+ std::nullopt,
+ &output_profile,
+ {dex_file_},
+ /*in_options=*/{},
+ &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
+ EXPECT_THAT(output_profile.profilePath.tmpPath, Not(IsEmpty()));
+}
+
+TEST_F(ArtdTest, mergeProfilesProfilesDontExist) {
+ const TmpProfilePath& reference_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
+ std::string reference_profile_file = OR_FATAL(BuildTmpProfilePath(reference_profile_path));
+ CreateFile(reference_profile_file, "abc");
+
+ // Doesn't exist.
+ PrimaryCurProfilePath profile_0_path{
+ .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
+
+ // Doesn't exist.
+ PrimaryCurProfilePath profile_1_path{
+ .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
+
+ OutputProfile output_profile{.profilePath = reference_profile_path,
+ .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ output_profile.profilePath.id = "";
+ output_profile.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode).Times(0);
+
+ bool result;
+ EXPECT_TRUE(artd_
+ ->mergeProfiles({profile_0_path},
+ std::nullopt,
+ &output_profile,
+ {dex_file_},
+ /*in_options=*/{},
+ &result)
+ .isOk());
+ EXPECT_FALSE(result);
+ EXPECT_THAT(output_profile.profilePath.id, IsEmpty());
+ EXPECT_THAT(output_profile.profilePath.tmpPath, IsEmpty());
+}
+
+TEST_F(ArtdTest, mergeProfilesWithOptionsForceMerge) {
+ PrimaryCurProfilePath profile_0_path{
+ .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
+ CreateFile(profile_0_file, "def");
+
+ OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
+ .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ output_profile.profilePath.id = "";
+ output_profile.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(
+ *mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--", _, AllOf(Contains("--force-merge"), Contains("--boot-image-merge"))),
+ _,
+ _))
+ .WillOnce(Return(ProfmanResult::kSuccess));
+
+ bool result;
+ EXPECT_TRUE(artd_
+ ->mergeProfiles({profile_0_path},
+ std::nullopt,
+ &output_profile,
+ {dex_file_},
+ {.forceMerge = true, .forBootImage = true},
+ &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
+ EXPECT_THAT(output_profile.profilePath.tmpPath, Not(IsEmpty()));
+}
+
+TEST_F(ArtdTest, mergeProfilesWithOptionsDumpOnly) {
+ PrimaryCurProfilePath profile_0_path{
+ .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
+ CreateFile(profile_0_file, "def");
+
+ OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
+ .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ output_profile.profilePath.id = "";
+ output_profile.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(WhenSplitBy("--",
+ _,
+ AllOf(Contains("--dump-only"),
+ Not(Contains(Flag("--reference-profile-file-fd=", _))))),
+ HasKeepFdsFor("--profile-file-fd=", "--apk-fd=", "--dump-output-to-fd=")),
+ _,
+ _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--dump-output-to-fd=", "dump")),
+ Return(ProfmanResult::kSuccess)));
+
+ bool result;
+ EXPECT_TRUE(artd_
+ ->mergeProfiles({profile_0_path},
+ std::nullopt,
+ &output_profile,
+ {dex_file_},
+ {.dumpOnly = true},
+ &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
+ CheckContent(output_profile.profilePath.tmpPath, "dump");
+}
+
+TEST_F(ArtdTest, mergeProfilesWithOptionsDumpClassesAndMethods) {
+ PrimaryCurProfilePath profile_0_path{
+ .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
+ std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
+ CreateFile(profile_0_file, "def");
+
+ OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
+ .fsPermission = FsPermission{.uid = -1, .gid = -1}};
+ output_profile.profilePath.id = "";
+ output_profile.profilePath.tmpPath = "";
+
+ CreateFile(dex_file_);
+
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ WhenSplitBy("--",
+ _,
+ AllOf(Contains("--dump-classes-and-methods"),
+ Not(Contains(Flag("--reference-profile-file-fd=", _))))),
+ _,
+ _))
+ .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--dump-output-to-fd=", "dump")),
+ Return(ProfmanResult::kSuccess)));
+
+ bool result;
+ EXPECT_TRUE(artd_
+ ->mergeProfiles({profile_0_path},
+ std::nullopt,
+ &output_profile,
+ {dex_file_},
+ {.dumpClassesAndMethods = true},
+ &result)
+ .isOk());
+ EXPECT_TRUE(result);
+ EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
+ CheckContent(output_profile.profilePath.tmpPath, "dump");
+}
+
+} // namespace
+} // namespace artd
+} // namespace art
diff --git a/artd/binder/Android.bp b/artd/binder/Android.bp
index 6acfe4e..b6fd5b8 100644
--- a/artd/binder/Android.bp
+++ b/artd/binder/Android.bp
@@ -25,12 +25,16 @@
aidl_interface {
name: "artd-aidl",
srcs: [
- "android/os/IArtd.aidl",
+ "com/android/server/art/*.aidl",
],
host_supported: true,
backend: {
java: {
enabled: true,
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
},
cpp: {
enabled: false,
@@ -40,9 +44,7 @@
apex_available: [
"com.android.art",
"com.android.art.debug",
- "com.android.compos",
],
- min_sdk_version: "31",
},
},
unstable: true,
@@ -50,4 +52,5 @@
"//system/tools/aidl/build",
"//art:__subpackages__",
],
+ min_sdk_version: "31",
}
diff --git a/artd/binder/android/os/IArtd.aidl b/artd/binder/android/os/IArtd.aidl
deleted file mode 100644
index a16764b..0000000
--- a/artd/binder/android/os/IArtd.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2021 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 android.os;
-
-/** {@hide} */
-interface IArtd {
- // Test to see if the artd service is available.
- boolean isAlive();
-}
diff --git a/artd/binder/com/android/server/art/ArtdDexoptResult.aidl b/artd/binder/com/android/server/art/ArtdDexoptResult.aidl
new file mode 100644
index 0000000..6f031f2
--- /dev/null
+++ b/artd/binder/com/android/server/art/ArtdDexoptResult.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * The result of {@code IArtd.dexopt}.
+ *
+ * @hide
+ */
+parcelable ArtdDexoptResult {
+ /** True if the operation is cancelled. */
+ boolean cancelled;
+ /**
+ * The wall time of the dex2oat invocation, in milliseconds, or 0 if dex2oat is not run or if
+ * failed to get the value.
+ */
+ long wallTimeMs;
+ /**
+ * The CPU time of the dex2oat invocation, in milliseconds, or 0 if dex2oat is not run or if
+ * failed to get the value.
+ */
+ long cpuTimeMs;
+ /**
+ * The total size, in bytes, of the dexopt artifacts, or 0 if dex2oat fails, is cancelled, or
+ * is not run.
+ */
+ long sizeBytes;
+ /**
+ * The total size, in bytes, of the previous dexopt artifacts that have been replaced, or
+ * 0 if there were no previous dexopt artifacts or dex2oat fails, is cancelled, or is not
+ * run.
+ */
+ long sizeBeforeBytes;
+}
diff --git a/artd/binder/com/android/server/art/ArtifactsPath.aidl b/artd/binder/com/android/server/art/ArtifactsPath.aidl
new file mode 100644
index 0000000..3122f0f
--- /dev/null
+++ b/artd/binder/com/android/server/art/ArtifactsPath.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents the path to the dexopt artifacts of a dex file (i.e., ART, OAT, and VDEX files).
+ *
+ * @hide
+ */
+parcelable ArtifactsPath {
+ /** The absolute path starting with '/' to the dex file (i.e., APK or JAR file). */
+ @utf8InCpp String dexPath;
+ /** The instruction set of the dexopt artifacts. */
+ @utf8InCpp String isa;
+ /** Whether the dexopt artifacts are in the dalvik-cache folder. */
+ boolean isInDalvikCache;
+}
diff --git a/artd/binder/com/android/server/art/DexMetadataPath.aidl b/artd/binder/com/android/server/art/DexMetadataPath.aidl
new file mode 100644
index 0000000..5f9ab81
--- /dev/null
+++ b/artd/binder/com/android/server/art/DexMetadataPath.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents the path to a dex metadata file.
+ *
+ * @hide
+ */
+parcelable DexMetadataPath {
+ /**
+ * The absolute path starting with '/' to the dex file that the dex metadata file is next to.
+ */
+ @utf8InCpp String dexPath;
+}
diff --git a/artd/binder/com/android/server/art/DexoptOptions.aidl b/artd/binder/com/android/server/art/DexoptOptions.aidl
new file mode 100644
index 0000000..351c079
--- /dev/null
+++ b/artd/binder/com/android/server/art/DexoptOptions.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Miscellaneous options for performing dexopt. Every field corresponds to a dex2oat command line
+ * flag.
+ *
+ * DO NOT add fields for flags that artd can determine directly with trivial logic. That includes
+ * static flags, and flags that only depend on system properties or other passed parameters, such as
+ * the priority class.
+ *
+ * All fields are required.
+ *
+ * @hide
+ */
+parcelable DexoptOptions {
+ /** --compilation-reason */
+ @utf8InCpp String compilationReason;
+ /** -Xtarget-sdk-version */
+ int targetSdkVersion;
+ /** --debuggable */
+ boolean debuggable;
+ /** --app-image-fd */
+ boolean generateAppImage;
+ /** -Xhidden-api-policy:enabled */
+ boolean hiddenApiPolicyEnabled;
+}
diff --git a/artd/binder/com/android/server/art/DexoptTrigger.aidl b/artd/binder/com/android/server/art/DexoptTrigger.aidl
new file mode 100644
index 0000000..79621a9
--- /dev/null
+++ b/artd/binder/com/android/server/art/DexoptTrigger.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents the conditions where dexopt should be performed.
+ * See `OatFileAssistant::DexOptTrigger`.
+ *
+ * This is actually used as a bit field, but is declared as an enum because AIDL doesn't support bit
+ * fields.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum DexoptTrigger {
+ COMPILER_FILTER_IS_BETTER = 1 << 0,
+ COMPILER_FILTER_IS_SAME = 1 << 1,
+ COMPILER_FILTER_IS_WORSE = 1 << 2,
+ PRIMARY_BOOT_IMAGE_BECOMES_USABLE = 1 << 3,
+ NEED_EXTRACTION = 1 << 4,
+}
diff --git a/artd/binder/com/android/server/art/FileVisibility.aidl b/artd/binder/com/android/server/art/FileVisibility.aidl
new file mode 100644
index 0000000..ceaa818
--- /dev/null
+++ b/artd/binder/com/android/server/art/FileVisibility.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Indicates the visibility of a file. I.e., whether the file has the "read" bit for "others"
+ * (S_IROTH).
+ *
+ * Theoretically, even if the value is {@code OTHER_READABLE}, others' access can still be denied
+ * due to the lack of the "exec" bit on parent directories. However, for compilation artifacts, all
+ * parent directories do have the "exec" bit for "others" in practice.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum FileVisibility {
+ NOT_FOUND = 0,
+ OTHER_READABLE = 1,
+ NOT_OTHER_READABLE = 2,
+}
diff --git a/artd/binder/com/android/server/art/FsPermission.aidl b/artd/binder/com/android/server/art/FsPermission.aidl
new file mode 100644
index 0000000..9c2ddb9
--- /dev/null
+++ b/artd/binder/com/android/server/art/FsPermission.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents the Linux filesystem permission of a file or a directory.
+ *
+ * If both `uid` and `gid` are negative, no `chown` will be performed.
+ *
+ * If none of the booleans are set, the default permission bits are `rw-r-----` for a file, and
+ * `rwxr-x---` for a directory.
+ *
+ * @hide
+ */
+parcelable FsPermission {
+ int uid;
+ int gid;
+ /**
+ * Whether the file/directory should have the "read" bit for "others" (S_IROTH).
+ */
+ boolean isOtherReadable;
+ /**
+ * Whether the file/directory should have the "execute" bit for "others" (S_IXOTH).
+ */
+ boolean isOtherExecutable;
+}
diff --git a/artd/binder/com/android/server/art/GetDexoptNeededResult.aidl b/artd/binder/com/android/server/art/GetDexoptNeededResult.aidl
new file mode 100644
index 0000000..99c4951
--- /dev/null
+++ b/artd/binder/com/android/server/art/GetDexoptNeededResult.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * The result of {@code IArtd.getDexoptNeeded}.
+ *
+ * @hide
+ */
+parcelable GetDexoptNeededResult {
+ /** Whether dexopt is needed. */
+ boolean isDexoptNeeded;
+ /** Whether there is a usable VDEX file. Note that this can be true even if dexopt is needed. */
+ boolean isVdexUsable;
+ /** The location of the best usable artifacts. */
+ ArtifactsLocation artifactsLocation = ArtifactsLocation.NONE_OR_ERROR;
+
+ enum ArtifactsLocation {
+ /** No usable artifacts. */
+ NONE_OR_ERROR = 0,
+ /** In the global "dalvik-cache" folder. */
+ DALVIK_CACHE = 1,
+ /** In the "oat" folder next to the dex file. */
+ NEXT_TO_DEX = 2,
+ /** In the dex metadata file. This means the only usable artifact is the VDEX file. */
+ DM = 3,
+ }
+}
diff --git a/artd/binder/com/android/server/art/GetDexoptStatusResult.aidl b/artd/binder/com/android/server/art/GetDexoptStatusResult.aidl
new file mode 100644
index 0000000..08786ca
--- /dev/null
+++ b/artd/binder/com/android/server/art/GetDexoptStatusResult.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * The result of {@code IArtd.getDexoptStatus}. Each field corresponds to a field in
+ * {@code com.android.server.art.model.DexoptStatus.DexFileDexoptStatus}.
+ *
+ * @hide
+ */
+parcelable GetDexoptStatusResult {
+ @utf8InCpp String compilerFilter;
+ @utf8InCpp String compilationReason;
+ @utf8InCpp String locationDebugString;
+}
diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl
new file mode 100644
index 0000000..5121063
--- /dev/null
+++ b/artd/binder/com/android/server/art/IArtd.aidl
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021 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 com.android.server.art;
+
+/** @hide */
+interface IArtd {
+ // Test to see if the artd service is available.
+ boolean isAlive();
+
+ /**
+ * Deletes artifacts and returns the released space, in bytes.
+ *
+ * Throws fatal errors. Logs and ignores non-fatal errors.
+ */
+ long deleteArtifacts(in com.android.server.art.ArtifactsPath artifactsPath);
+
+ /**
+ * Returns the dexopt status of a dex file.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ com.android.server.art.GetDexoptStatusResult getDexoptStatus(
+ @utf8InCpp String dexFile, @utf8InCpp String instructionSet,
+ @utf8InCpp String classLoaderContext);
+
+ /**
+ * Returns true if the profile exists and contains entries for the given dex file.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ boolean isProfileUsable(in com.android.server.art.ProfilePath profile,
+ @utf8InCpp String dexFile);
+
+ /**
+ * Copies the profile and rewrites it for the given dex file. Returns true and fills
+ * `dst.profilePath.id` if the operation succeeds and `src` exists and contains entries that
+ * match the given dex file.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ boolean copyAndRewriteProfile(in com.android.server.art.ProfilePath src,
+ inout com.android.server.art.OutputProfile dst, @utf8InCpp String dexFile);
+
+ /**
+ * Moves the temporary profile to the permanent location.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ void commitTmpProfile(in com.android.server.art.ProfilePath.TmpProfilePath profile);
+
+ /**
+ * Deletes the profile. Does nothing of the profile doesn't exist.
+ *
+ * Operates on the whole DM file if given one.
+ *
+ * Throws fatal errors. Logs and ignores non-fatal errors.
+ */
+ void deleteProfile(in com.android.server.art.ProfilePath profile);
+
+ /**
+ * Returns the visibility of the profile.
+ *
+ * Operates on the whole DM file if given one.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ com.android.server.art.FileVisibility getProfileVisibility(
+ in com.android.server.art.ProfilePath profile);
+
+ /**
+ * Merges profiles. Both `profiles` and `referenceProfile` are inputs, while the difference is
+ * that `referenceProfile` is also used as the reference to calculate the diff. `profiles` that
+ * don't exist are skipped, while `referenceProfile`, if provided, must exist. Returns true,
+ * writes the merge result to `outputProfile` and fills `outputProfile.profilePath.id` and
+ * `outputProfile.profilePath.tmpPath` if a merge has been performed.
+ *
+ * When `options.forceMerge`, `options.dumpOnly`, or `options.dumpClassesAndMethods` is set,
+ * `referenceProfile` must not be set. I.e., all inputs must be provided by `profiles`. This is
+ * because the merge will always happen, and hence no reference profile is needed to calculate
+ * the diff.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ boolean mergeProfiles(in List<com.android.server.art.ProfilePath> profiles,
+ in @nullable com.android.server.art.ProfilePath referenceProfile,
+ inout com.android.server.art.OutputProfile outputProfile,
+ in @utf8InCpp List<String> dexFiles,
+ in com.android.server.art.MergeProfileOptions options);
+
+ /**
+ * Returns the visibility of the artifacts.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ com.android.server.art.FileVisibility getArtifactsVisibility(
+ in com.android.server.art.ArtifactsPath artifactsPath);
+
+ /**
+ * Returns the visibility of the dex file.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ com.android.server.art.FileVisibility getDexFileVisibility(@utf8InCpp String dexFile);
+
+ /**
+ * Returns the visibility of the DM file.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ com.android.server.art.FileVisibility getDmFileVisibility(
+ in com.android.server.art.DexMetadataPath dmFile);
+
+ /**
+ * Returns true if dexopt is needed. `dexoptTrigger` is a bit field that consists of values
+ * defined in `com.android.server.art.DexoptTrigger`.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ com.android.server.art.GetDexoptNeededResult getDexoptNeeded(
+ @utf8InCpp String dexFile, @utf8InCpp String instructionSet,
+ @nullable @utf8InCpp String classLoaderContext, @utf8InCpp String compilerFilter,
+ int dexoptTrigger);
+
+ /**
+ * Dexopts a dex file for the given instruction set.
+ *
+ * Throws fatal and non-fatal errors.
+ */
+ com.android.server.art.ArtdDexoptResult dexopt(
+ in com.android.server.art.OutputArtifacts outputArtifacts,
+ @utf8InCpp String dexFile, @utf8InCpp String instructionSet,
+ @nullable @utf8InCpp String classLoaderContext, @utf8InCpp String compilerFilter,
+ in @nullable com.android.server.art.ProfilePath profile,
+ in @nullable com.android.server.art.VdexPath inputVdex,
+ in @nullable com.android.server.art.DexMetadataPath dmFile,
+ com.android.server.art.PriorityClass priorityClass,
+ in com.android.server.art.DexoptOptions dexoptOptions,
+ in com.android.server.art.IArtdCancellationSignal cancellationSignal);
+
+ /**
+ * Returns a cancellation signal which can be used to cancel {@code dexopt} calls.
+ */
+ com.android.server.art.IArtdCancellationSignal createCancellationSignal();
+}
diff --git a/artd/binder/com/android/server/art/IArtdCancellationSignal.aidl b/artd/binder/com/android/server/art/IArtdCancellationSignal.aidl
new file mode 100644
index 0000000..fb15e64
--- /dev/null
+++ b/artd/binder/com/android/server/art/IArtdCancellationSignal.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Similar to `android.os.CancellationSignal` but for artd. Must be created by
+ * `IArtd.createCancellationSignal`.
+ *
+ * @hide
+ */
+interface IArtdCancellationSignal {
+ oneway void cancel();
+
+ /** For artd internal type-checking. DO NOT USE. */
+ long getType();
+}
diff --git a/artd/binder/com/android/server/art/MergeProfileOptions.aidl b/artd/binder/com/android/server/art/MergeProfileOptions.aidl
new file mode 100644
index 0000000..2d007f9
--- /dev/null
+++ b/artd/binder/com/android/server/art/MergeProfileOptions.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Miscellaneous options for merging profiles. Every field corresponds to a profman command line
+ * flag.
+ *
+ * DO NOT add fields for flags that artd can determine directly with trivial logic. That includes
+ * static flags, and flags that only depend on system properties or other passed parameters.
+ *
+ * All fields are required.
+ *
+ * @hide
+ */
+parcelable MergeProfileOptions {
+ /** --force-merge */
+ boolean forceMerge;
+ /** --boot-image-merge */
+ boolean forBootImage;
+ /** --dump-only */
+ boolean dumpOnly;
+ /** --dump-classes-and-methods */
+ boolean dumpClassesAndMethods;
+}
diff --git a/artd/binder/com/android/server/art/OutputArtifacts.aidl b/artd/binder/com/android/server/art/OutputArtifacts.aidl
new file mode 100644
index 0000000..9a53965
--- /dev/null
+++ b/artd/binder/com/android/server/art/OutputArtifacts.aidl
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents output dexopt artifacts of a dex file (i.e., ART, OAT, and VDEX files).
+ *
+ * @hide
+ */
+parcelable OutputArtifacts {
+ /** The path to the output. */
+ com.android.server.art.ArtifactsPath artifactsPath;
+
+ parcelable PermissionSettings {
+ /**
+ * The permission of the directories that contain the artifacts. Has no effect if
+ * `artifactsPath.isInDalvikCache` is true.
+ */
+ com.android.server.art.FsPermission dirFsPermission;
+
+ /** The permission of the files. */
+ com.android.server.art.FsPermission fileFsPermission;
+
+ /** The tuple used for looking up for the SELinux context. */
+ parcelable SeContext {
+ /** The seinfo tag in SELinux policy. */
+ @utf8InCpp String seInfo;
+
+ /** The uid that represents the combination of the user id and the app id. */
+ int uid;
+ }
+
+ /**
+ * Determines the SELinux context of the directories and the files. If empty, the default
+ * context based on the file path will be used. Has no effect if
+ * `artifactsPath.isInDalvikCache` is true.
+ */
+ @nullable SeContext seContext;
+ }
+
+ PermissionSettings permissionSettings;
+}
diff --git a/artd/binder/com/android/server/art/OutputProfile.aidl b/artd/binder/com/android/server/art/OutputProfile.aidl
new file mode 100644
index 0000000..50efda2
--- /dev/null
+++ b/artd/binder/com/android/server/art/OutputProfile.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents output profile file.
+ *
+ * @hide
+ */
+parcelable OutputProfile {
+ /**
+ * The path to the output.
+ *
+ * Only outputing to a temporary file is supported to avoid race condition.
+ */
+ com.android.server.art.ProfilePath.TmpProfilePath profilePath;
+
+ /** The permission of the file. */
+ com.android.server.art.FsPermission fsPermission;
+}
diff --git a/artd/binder/com/android/server/art/PriorityClass.aidl b/artd/binder/com/android/server/art/PriorityClass.aidl
new file mode 100644
index 0000000..abea3f3
--- /dev/null
+++ b/artd/binder/com/android/server/art/PriorityClass.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Keep in sync with {@link ArtFlags.PriorityClassApi}.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum PriorityClass {
+ BOOT = 100,
+ INTERACTIVE_FAST = 80,
+ INTERACTIVE = 60,
+ BACKGROUND = 40,
+}
diff --git a/artd/binder/com/android/server/art/ProfilePath.aidl b/artd/binder/com/android/server/art/ProfilePath.aidl
new file mode 100644
index 0000000..43df531
--- /dev/null
+++ b/artd/binder/com/android/server/art/ProfilePath.aidl
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents the path to a profile file.
+ *
+ * @hide
+ */
+union ProfilePath {
+ PrimaryRefProfilePath primaryRefProfilePath;
+ PrebuiltProfilePath prebuiltProfilePath;
+ PrimaryCurProfilePath primaryCurProfilePath;
+ SecondaryRefProfilePath secondaryRefProfilePath;
+ SecondaryCurProfilePath secondaryCurProfilePath;
+ TmpProfilePath tmpProfilePath;
+
+ /** Represents a profile in the dex metadata file. */
+ com.android.server.art.DexMetadataPath dexMetadataPath;
+
+ /** Represents a reference profile. */
+ parcelable PrimaryRefProfilePath {
+ /** The name of the package. */
+ @utf8InCpp String packageName;
+ /** The stem of the profile file */
+ @utf8InCpp String profileName;
+ }
+
+ /**
+ * Represents a profile next to a dex file. This is usually a prebuilt profile in the system
+ * image, but it can also be a profile that package manager can potentially put along with the
+ * APK during installation. The latter one is not officially supported by package manager, but
+ * OEMs can customize package manager to support that.
+ */
+ parcelable PrebuiltProfilePath {
+ /** The path to the dex file that the profile is next to. */
+ @utf8InCpp String dexPath;
+ }
+
+ /** Represents a current profile. */
+ parcelable PrimaryCurProfilePath {
+ /** The user ID of the user that owns the profile. */
+ int userId;
+ /** The name of the package. */
+ @utf8InCpp String packageName;
+ /** The stem of the profile file */
+ @utf8InCpp String profileName;
+ }
+
+ /** Represents a reference profile of a secondary dex file. */
+ parcelable SecondaryRefProfilePath {
+ /**
+ * The path to the dex file that the profile is next to.
+ *
+ * Currently, possible paths are in the format of
+ * `{/data,/mnt/expand/<volume-uuid>}/{user,user_de}/<user-id>/<package-name>/...`.
+ */
+ @utf8InCpp String dexPath;
+ }
+
+ /** Represents a current profile of a secondary dex file. */
+ parcelable SecondaryCurProfilePath {
+ /** The path to the dex file that the profile is next to. */
+ @utf8InCpp String dexPath;
+ }
+
+ /** All types of profile paths that artd can write to. */
+ union WritableProfilePath {
+ PrimaryRefProfilePath forPrimary;
+ SecondaryRefProfilePath forSecondary;
+ }
+
+ /** Represents a temporary profile. */
+ parcelable TmpProfilePath {
+ /** The path that this temporary file will eventually be committed to. */
+ WritableProfilePath finalPath;
+ /** A unique identifier to distinguish this temporary file from others. Filled by artd. */
+ @utf8InCpp String id;
+ /** The path to the temporary file. Filled by artd. */
+ @utf8InCpp String tmpPath;
+ }
+}
diff --git a/artd/binder/com/android/server/art/VdexPath.aidl b/artd/binder/com/android/server/art/VdexPath.aidl
new file mode 100644
index 0000000..6112e7a
--- /dev/null
+++ b/artd/binder/com/android/server/art/VdexPath.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+/**
+ * Represents the path to a VDEX file.
+ *
+ * @hide
+ */
+union VdexPath {
+ /** Represents a VDEX file as part of the artifacts. */
+ com.android.server.art.ArtifactsPath artifactsPath;
+}
diff --git a/artd/file_utils.cc b/artd/file_utils.cc
new file mode 100644
index 0000000..53fe9f9
--- /dev/null
+++ b/artd/file_utils.cc
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2022 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 "file_utils.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <system_error>
+#include <utility>
+
+#include "aidl/com/android/server/art/FsPermission.h"
+#include "android-base/errors.h"
+#include "android-base/logging.h"
+#include "android-base/result.h"
+#include "android-base/scopeguard.h"
+#include "base/os.h"
+#include "base/unix_file/fd_file.h"
+#include "fmt/format.h"
+
+namespace art {
+namespace artd {
+
+namespace {
+
+using ::aidl::com::android::server::art::FsPermission;
+using ::android::base::make_scope_guard;
+using ::android::base::Result;
+
+using ::fmt::literals::operator""_format; // NOLINT
+
+void UnlinkIfExists(const std::string& path) {
+ std::error_code ec;
+ if (!std::filesystem::remove(path, ec)) {
+ if (ec.value() != ENOENT) {
+ LOG(WARNING) << "Failed to remove file '{}': {}"_format(path, ec.message());
+ }
+ }
+}
+
+} // namespace
+
+Result<std::unique_ptr<NewFile>> NewFile::Create(const std::string& path,
+ const FsPermission& fs_permission) {
+ std::unique_ptr<NewFile> output_file(new NewFile(path, fs_permission));
+ OR_RETURN(output_file->Init());
+ return output_file;
+}
+
+NewFile::~NewFile() { Cleanup(); }
+
+Result<void> NewFile::Keep() {
+ if (close(std::exchange(fd_, -1)) != 0) {
+ return ErrnoErrorf("Failed to close file '{}'", temp_path_);
+ }
+ return {};
+}
+
+Result<void> NewFile::CommitOrAbandon() {
+ auto cleanup = make_scope_guard([this] { Unlink(); });
+ OR_RETURN(Keep());
+ std::error_code ec;
+ std::filesystem::rename(temp_path_, final_path_, ec);
+ if (ec) {
+ return Errorf(
+ "Failed to move new file '{}' to path '{}': {}", temp_path_, final_path_, ec.message());
+ }
+ cleanup.Disable();
+ committed_ = true;
+ return {};
+}
+
+void NewFile::Cleanup() {
+ if (fd_ >= 0) {
+ Unlink();
+ if (close(std::exchange(fd_, -1)) != 0) {
+ // Nothing we can do. If the file is already unlinked, it will go away when the process exits.
+ PLOG(WARNING) << "Failed to close file '" << temp_path_ << "'";
+ }
+ }
+}
+
+Result<void> NewFile::Init() {
+ mode_t mode = FileFsPermissionToMode(fs_permission_);
+ // "<path_>.XXXXXX.tmp".
+ temp_path_ = BuildTempPath(final_path_, "XXXXXX");
+ fd_ = mkstemps(temp_path_.data(), /*suffixlen=*/4);
+ if (fd_ < 0) {
+ return ErrnoErrorf("Failed to create temp file for '{}'", final_path_);
+ }
+ temp_id_ = temp_path_.substr(/*pos=*/final_path_.length() + 1, /*count=*/6);
+ if (fchmod(fd_, mode) != 0) {
+ return ErrnoErrorf("Failed to chmod file '{}'", temp_path_);
+ }
+ OR_RETURN(Chown(temp_path_, fs_permission_));
+ return {};
+}
+
+void NewFile::Unlink() {
+ // This should never fail. We were able to create the file, so we should be able to remove it.
+ UnlinkIfExists(temp_path_);
+}
+
+Result<void> NewFile::CommitAllOrAbandon(const std::vector<NewFile*>& files_to_commit,
+ const std::vector<std::string_view>& files_to_remove) {
+ std::vector<std::pair<std::string_view, std::string>> moved_files;
+
+ auto cleanup = make_scope_guard([&]() {
+ // Clean up new files.
+ for (NewFile* new_file : files_to_commit) {
+ if (new_file->committed_) {
+ UnlinkIfExists(new_file->FinalPath());
+ } else {
+ new_file->Cleanup();
+ }
+ }
+
+ // Move old files back.
+ for (const auto& [original_path, temp_path] : moved_files) {
+ std::error_code ec;
+ std::filesystem::rename(temp_path, original_path, ec);
+ if (ec) {
+ // This should never happen. We were able to move the file from `original_path` to
+ // `temp_path`. We should be able to move it back.
+ LOG(WARNING) << "Failed to move old file '{}' back from temporary path '{}': {}"_format(
+ original_path, temp_path, ec.message());
+ }
+ }
+ });
+
+ // Move old files to temporary locations.
+ std::vector<std::string_view> all_files_to_remove;
+ all_files_to_remove.reserve(files_to_commit.size() + files_to_remove.size());
+ for (NewFile* file : files_to_commit) {
+ all_files_to_remove.push_back(file->FinalPath());
+ }
+ all_files_to_remove.insert(
+ all_files_to_remove.end(), files_to_remove.begin(), files_to_remove.end());
+
+ for (std::string_view original_path : all_files_to_remove) {
+ std::error_code ec;
+ std::filesystem::file_status status = std::filesystem::status(original_path, ec);
+ if (!std::filesystem::status_known(status)) {
+ return Errorf("Failed to get status of old file '{}': {}", original_path, ec.message());
+ }
+ if (std::filesystem::is_directory(status)) {
+ return ErrnoErrorf("Old file '{}' is a directory", original_path);
+ }
+ if (std::filesystem::exists(status)) {
+ std::string temp_path = BuildTempPath(original_path, "XXXXXX");
+ int fd = mkstemps(temp_path.data(), /*suffixlen=*/4);
+ if (fd < 0) {
+ return ErrnoErrorf("Failed to create temporary path for old file '{}'", original_path);
+ }
+ close(fd);
+
+ std::filesystem::rename(original_path, temp_path, ec);
+ if (ec) {
+ UnlinkIfExists(temp_path);
+ return Errorf("Failed to move old file '{}' to temporary path '{}': {}",
+ original_path,
+ temp_path,
+ ec.message());
+ }
+
+ moved_files.push_back({original_path, std::move(temp_path)});
+ }
+ }
+
+ // Commit new files.
+ for (NewFile* file : files_to_commit) {
+ OR_RETURN(file->CommitOrAbandon());
+ }
+
+ cleanup.Disable();
+
+ // Clean up old files.
+ for (const auto& [original_path, temp_path] : moved_files) {
+ // This should never fail. We were able to move the file to `temp_path`. We should be able to
+ // remove it.
+ UnlinkIfExists(temp_path);
+ }
+
+ return {};
+}
+
+std::string NewFile::BuildTempPath(std::string_view final_path, const std::string& id) {
+ return "{}.{}.tmp"_format(final_path, id);
+}
+
+Result<std::unique_ptr<File>> OpenFileForReading(const std::string& path) {
+ std::unique_ptr<File> file(OS::OpenFileForReading(path.c_str()));
+ if (file == nullptr) {
+ return ErrnoErrorf("Failed to open file '{}'", path);
+ }
+ return file;
+}
+
+mode_t FileFsPermissionToMode(const FsPermission& fs_permission) {
+ return S_IRUSR | S_IWUSR | S_IRGRP | (fs_permission.isOtherReadable ? S_IROTH : 0) |
+ (fs_permission.isOtherExecutable ? S_IXOTH : 0);
+}
+
+mode_t DirFsPermissionToMode(const FsPermission& fs_permission) {
+ return FileFsPermissionToMode(fs_permission) | S_IXUSR | S_IXGRP;
+}
+
+Result<void> Chown(const std::string& path, const FsPermission& fs_permission) {
+ if (fs_permission.uid < 0 && fs_permission.gid < 0) {
+ // Keep the default owner.
+ } else if (fs_permission.uid < 0 || fs_permission.gid < 0) {
+ return Errorf("uid and gid must be both non-negative or both negative, got {} and {}.",
+ fs_permission.uid,
+ fs_permission.gid);
+ }
+ if (chown(path.c_str(), fs_permission.uid, fs_permission.gid) != 0) {
+ return ErrnoErrorf("Failed to chown '{}'", path);
+ }
+ return {};
+}
+
+} // namespace artd
+} // namespace art
diff --git a/artd/file_utils.h b/artd/file_utils.h
new file mode 100644
index 0000000..b5fd170
--- /dev/null
+++ b/artd/file_utils.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_ARTD_FILE_UTILS_H_
+#define ART_ARTD_FILE_UTILS_H_
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "aidl/com/android/server/art/FsPermission.h"
+#include "android-base/result.h"
+#include "base/os.h"
+
+namespace art {
+namespace artd {
+
+// A class that creates a new file that will eventually be committed to the given path. The new file
+// is created at a temporary location. It will not overwrite the file at the given path until
+// `CommitOrAbandon` has been called and will be automatically cleaned up on object destruction
+// unless `CommitOrAbandon` has been called.
+// The new file is opened without O_CLOEXEC so that it can be passed to subprocesses.
+class NewFile {
+ public:
+ // Creates a new file at the given path with the given permission.
+ static android::base::Result<std::unique_ptr<NewFile>> Create(
+ const std::string& path, const aidl::com::android::server::art::FsPermission& fs_permission);
+
+ NewFile(const NewFile&) = delete;
+ NewFile& operator=(const NewFile&) = delete;
+ NewFile(NewFile&& other) noexcept
+ : fd_(std::exchange(other.fd_, -1)),
+ final_path_(std::move(other.final_path_)),
+ temp_path_(std::move(other.temp_path_)),
+ temp_id_(std::move(other.temp_id_)),
+ fs_permission_(other.fs_permission_) {}
+
+ // Deletes the file if it is not committed.
+ virtual ~NewFile();
+
+ int Fd() const { return fd_; }
+
+ // The path that the file will eventually be committed to.
+ const std::string& FinalPath() const { return final_path_; }
+
+ // The path to the new file.
+ const std::string& TempPath() const { return temp_path_; }
+
+ // The unique ID of the new file. Can be used by `BuildTempPath` for reconstructing the path to
+ // the file.
+ const std::string& TempId() const { return temp_id_; }
+
+ // Closes the new file, keeps it, moves the file to the final path, and overwrites any existing
+ // file at that path, or abandons the file on failure. The fd will be invalid after this function
+ // is called.
+ android::base::Result<void> CommitOrAbandon();
+
+ // Closes the new file and keeps it at the temporary location. The file will not be automatically
+ // cleaned up on object destruction. The file can be found at `TempPath()` (i.e.,
+ // `BuildTempPath(FinalPath(), TempId())`). The fd will be invalid after this function is called.
+ virtual android::base::Result<void> Keep();
+
+ // Unlinks and closes the new file if it is not committed. The fd will be invalid after this
+ // function is called.
+ void Cleanup();
+
+ // Commits all new files, replacing old files, and removes given files in addition. Or abandons
+ // new files and restores old files at best effort if any error occurs. The fds will be invalid
+ // after this function is called.
+ //
+ // Note: This function is NOT thread-safe. It is intended to be used in single-threaded code or in
+ // cases where some race condition is acceptable.
+ //
+ // Usage:
+ //
+ // Commit `file_1` and `file_2`, and remove the file at "path_3":
+ // CommitAllOrAbandon({file_1, file_2}, {"path_3"});
+ static android::base::Result<void> CommitAllOrAbandon(
+ const std::vector<NewFile*>& files_to_commit,
+ const std::vector<std::string_view>& files_to_remove = {});
+
+ // Returns the path to a temporary file. See `Keep`.
+ static std::string BuildTempPath(std::string_view final_path, const std::string& id);
+
+ private:
+ NewFile(const std::string& path,
+ const aidl::com::android::server::art::FsPermission& fs_permission)
+ : final_path_(path), fs_permission_(fs_permission) {}
+
+ android::base::Result<void> Init();
+
+ // Unlinks the new file. The fd will still be valid after this function is called.
+ void Unlink();
+
+ int fd_ = -1;
+ std::string final_path_;
+ std::string temp_path_;
+ std::string temp_id_;
+ aidl::com::android::server::art::FsPermission fs_permission_;
+ bool committed_ = false;
+};
+
+// Opens a file for reading.
+android::base::Result<std::unique_ptr<File>> OpenFileForReading(const std::string& path);
+
+// Converts FsPermission to Linux access mode for a file.
+mode_t FileFsPermissionToMode(const aidl::com::android::server::art::FsPermission& fs_permission);
+
+// Converts FsPermission to Linux access mode for a directory.
+mode_t DirFsPermissionToMode(const aidl::com::android::server::art::FsPermission& fs_permission);
+
+// Changes the owner based on FsPermission.
+android::base::Result<void> Chown(
+ const std::string& path, const aidl::com::android::server::art::FsPermission& fs_permission);
+
+} // namespace artd
+} // namespace art
+
+#endif // ART_ARTD_FILE_UTILS_H_
diff --git a/artd/file_utils_test.cc b/artd/file_utils_test.cc
new file mode 100644
index 0000000..8f79d5d
--- /dev/null
+++ b/artd/file_utils_test.cc
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 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 "file_utils.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <memory>
+#include <string>
+
+#include "aidl/com/android/server/art/FsPermission.h"
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "android-base/result-gmock.h"
+#include "android-base/result.h"
+#include "base/common_art_test.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace artd {
+namespace {
+
+using ::aidl::com::android::server::art::FsPermission;
+using ::android::base::Error;
+using ::android::base::ReadFileToString;
+using ::android::base::Result;
+using ::android::base::WriteStringToFd;
+using ::android::base::WriteStringToFile;
+using ::android::base::testing::HasError;
+using ::android::base::testing::HasValue;
+using ::android::base::testing::Ok;
+using ::android::base::testing::WithMessage;
+using ::testing::ContainsRegex;
+using ::testing::IsEmpty;
+using ::testing::NotNull;
+
+void CheckContent(const std::string& path, const std::string& expected_content) {
+ std::string actual_content;
+ ASSERT_TRUE(ReadFileToString(path, &actual_content));
+ EXPECT_EQ(actual_content, expected_content);
+}
+
+// A file that will always fail on `Commit`.
+class UncommittableFile : public NewFile {
+ public:
+ static Result<std::unique_ptr<UncommittableFile>> Create(const std::string& path,
+ const FsPermission& fs_permission) {
+ std::unique_ptr<NewFile> new_file = OR_RETURN(NewFile::Create(path, fs_permission));
+ return std::unique_ptr<UncommittableFile>(new UncommittableFile(std::move(*new_file)));
+ }
+
+ Result<void> Keep() override { return Error() << "Uncommittable file"; }
+
+ private:
+ explicit UncommittableFile(NewFile&& other) : NewFile(std::move(other)) {}
+};
+
+class FileUtilsTest : public CommonArtTest {
+ protected:
+ void SetUp() override {
+ CommonArtTest::SetUp();
+ scratch_dir_ = std::make_unique<ScratchDir>();
+ struct stat st;
+ ASSERT_EQ(stat(scratch_dir_->GetPath().c_str(), &st), 0);
+ fs_permission_ = FsPermission{.uid = static_cast<int32_t>(st.st_uid),
+ .gid = static_cast<int32_t>(st.st_gid)};
+ }
+
+ void TearDown() override {
+ scratch_dir_.reset();
+ CommonArtTest::TearDown();
+ }
+
+ FsPermission fs_permission_;
+ std::unique_ptr<ScratchDir> scratch_dir_;
+};
+
+TEST_F(FileUtilsTest, NewFileCreate) {
+ std::string path = scratch_dir_->GetPath() + "/file.tmp";
+
+ Result<std::unique_ptr<NewFile>> new_file = NewFile::Create(path, fs_permission_);
+ ASSERT_THAT(new_file, HasValue(NotNull()));
+ EXPECT_GE((*new_file)->Fd(), 0);
+ EXPECT_EQ((*new_file)->FinalPath(), path);
+ EXPECT_THAT((*new_file)->TempPath(), Not(IsEmpty()));
+ EXPECT_THAT((*new_file)->TempId(), Not(IsEmpty()));
+
+ EXPECT_FALSE(std::filesystem::exists((*new_file)->FinalPath()));
+ EXPECT_TRUE(std::filesystem::exists((*new_file)->TempPath()));
+}
+
+TEST_F(FileUtilsTest, NewFileCreateNonExistentDir) {
+ std::string path = scratch_dir_->GetPath() + "/non_existent_dir/file.tmp";
+
+ EXPECT_THAT(NewFile::Create(path, fs_permission_),
+ HasError(WithMessage(
+ ContainsRegex("Failed to create temp file for .*/non_existent_dir/file.tmp"))));
+}
+
+TEST_F(FileUtilsTest, NewFileExplicitCleanup) {
+ std::string path = scratch_dir_->GetPath() + "/file.tmp";
+ std::unique_ptr<NewFile> new_file = OR_FATAL(NewFile::Create(path, fs_permission_));
+ new_file->Cleanup();
+
+ EXPECT_FALSE(std::filesystem::exists(path));
+ EXPECT_FALSE(std::filesystem::exists(new_file->TempPath()));
+}
+
+TEST_F(FileUtilsTest, NewFileImplicitCleanup) {
+ std::string path = scratch_dir_->GetPath() + "/file.tmp";
+ std::string temp_path;
+
+ // Cleanup on object destruction.
+ {
+ std::unique_ptr<NewFile> new_file = OR_FATAL(NewFile::Create(path, fs_permission_));
+ temp_path = new_file->TempPath();
+ }
+
+ EXPECT_FALSE(std::filesystem::exists(path));
+ EXPECT_FALSE(std::filesystem::exists(temp_path));
+}
+
+TEST_F(FileUtilsTest, NewFileCommit) {
+ std::string path = scratch_dir_->GetPath() + "/file.tmp";
+ std::string temp_path;
+
+ {
+ std::unique_ptr<NewFile> new_file = OR_FATAL(NewFile::Create(path, fs_permission_));
+ temp_path = new_file->TempPath();
+ new_file->CommitOrAbandon();
+ }
+
+ EXPECT_TRUE(std::filesystem::exists(path));
+ EXPECT_FALSE(std::filesystem::exists(temp_path));
+}
+
+TEST_F(FileUtilsTest, NewFileCommitAllNoOldFile) {
+ std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
+ std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
+
+ std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
+ std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
+
+ ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
+ ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
+
+ EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}), Ok());
+
+ // New files are committed.
+ CheckContent(file_1_path, "new_file_1");
+ CheckContent(file_2_path, "new_file_2");
+
+ // New files are no longer at the temporary paths.
+ EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
+ EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
+}
+
+TEST_F(FileUtilsTest, NewFileCommitAllReplacesOldFiles) {
+ std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
+ std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
+
+ ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
+ ASSERT_TRUE(WriteStringToFile("old_file_2", file_2_path));
+
+ std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
+ std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
+
+ ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
+ ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
+
+ EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}), Ok());
+
+ // New files are committed.
+ CheckContent(file_1_path, "new_file_1");
+ CheckContent(file_2_path, "new_file_2");
+
+ // New files are no longer at the temporary paths.
+ EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
+ EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
+}
+
+TEST_F(FileUtilsTest, NewFileCommitAllReplacesLessOldFiles) {
+ std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
+ std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
+
+ ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path)); // No old_file_2.
+
+ std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
+ std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
+
+ ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
+ ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
+
+ EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}), Ok());
+
+ // New files are committed.
+ CheckContent(file_1_path, "new_file_1");
+ CheckContent(file_2_path, "new_file_2");
+
+ // New files are no longer at the temporary paths.
+ EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
+ EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
+}
+
+TEST_F(FileUtilsTest, NewFileCommitAllReplacesMoreOldFiles) {
+ std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
+ std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
+ std::string file_3_path = scratch_dir_->GetPath() + "/file_3";
+
+ ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
+ ASSERT_TRUE(WriteStringToFile("old_file_2", file_2_path));
+ ASSERT_TRUE(WriteStringToFile("old_file_3", file_3_path)); // Extra file.
+
+ std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
+ std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
+
+ ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
+ ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
+
+ EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}, {file_3_path}),
+ Ok());
+
+ // New files are committed.
+ CheckContent(file_1_path, "new_file_1");
+ CheckContent(file_2_path, "new_file_2");
+ EXPECT_FALSE(std::filesystem::exists(file_3_path)); // Extra file removed.
+
+ // New files are no longer at the temporary paths.
+ EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
+ EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
+}
+
+TEST_F(FileUtilsTest, NewFileCommitAllFailedToCommit) {
+ std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
+ std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
+ std::string file_3_path = scratch_dir_->GetPath() + "/file_3";
+
+ ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
+ ASSERT_TRUE(WriteStringToFile("old_file_2", file_2_path));
+ ASSERT_TRUE(WriteStringToFile("old_file_3", file_3_path)); // Extra file.
+
+ std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
+ // Uncommittable file.
+ std::unique_ptr<NewFile> new_file_2 =
+ OR_FATAL(UncommittableFile::Create(file_2_path, fs_permission_));
+
+ ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
+ ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
+
+ EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}, {file_3_path}),
+ HasError(WithMessage("Uncommittable file")));
+
+ // Old files are fine.
+ CheckContent(file_1_path, "old_file_1");
+ CheckContent(file_2_path, "old_file_2");
+ CheckContent(file_3_path, "old_file_3");
+
+ // New files are abandoned.
+ EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
+ EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
+}
+
+TEST_F(FileUtilsTest, NewFileCommitAllFailedToMoveOldFile) {
+ std::string file_1_path = scratch_dir_->GetPath() + "/file_1";
+ std::string file_2_path = scratch_dir_->GetPath() + "/file_2";
+ std::filesystem::create_directory(file_2_path);
+ std::string file_3_path = scratch_dir_->GetPath() + "/file_3";
+
+ ASSERT_TRUE(WriteStringToFile("old_file_1", file_1_path));
+ ASSERT_TRUE(WriteStringToFile("old_file_3", file_3_path)); // Extra file.
+
+ std::unique_ptr<NewFile> new_file_1 = OR_FATAL(NewFile::Create(file_1_path, fs_permission_));
+ std::unique_ptr<NewFile> new_file_2 = OR_FATAL(NewFile::Create(file_2_path, fs_permission_));
+
+ ASSERT_TRUE(WriteStringToFd("new_file_1", new_file_1->Fd()));
+ ASSERT_TRUE(WriteStringToFd("new_file_2", new_file_2->Fd()));
+
+ // file_2 is not movable because it is a directory.
+ EXPECT_THAT(NewFile::CommitAllOrAbandon({new_file_1.get(), new_file_2.get()}, {file_3_path}),
+ HasError(WithMessage(ContainsRegex("Old file '.*/file_2' is a directory"))));
+
+ // Old files are fine.
+ CheckContent(file_1_path, "old_file_1");
+ EXPECT_TRUE(std::filesystem::is_directory(file_2_path));
+ CheckContent(file_3_path, "old_file_3");
+
+ // New files are abandoned.
+ EXPECT_FALSE(std::filesystem::exists(new_file_1->TempPath()));
+ EXPECT_FALSE(std::filesystem::exists(new_file_2->TempPath()));
+}
+
+TEST_F(FileUtilsTest, BuildTempPath) {
+ EXPECT_EQ(NewFile::BuildTempPath("/a/b/original_path", "123456"),
+ "/a/b/original_path.123456.tmp");
+}
+
+TEST_F(FileUtilsTest, OpenFileForReading) {
+ std::string path = scratch_dir_->GetPath() + "/foo";
+ ASSERT_TRUE(WriteStringToFile("foo", path));
+
+ EXPECT_THAT(OpenFileForReading(path), HasValue(NotNull()));
+}
+
+TEST_F(FileUtilsTest, OpenFileForReadingFailed) {
+ std::string path = scratch_dir_->GetPath() + "/foo";
+
+ EXPECT_THAT(OpenFileForReading(path),
+ HasError(WithMessage(ContainsRegex("Failed to open file .*/foo"))));
+}
+
+TEST_F(FileUtilsTest, FileFsPermissionToMode) {
+ EXPECT_EQ(FileFsPermissionToMode(FsPermission{}), S_IRUSR | S_IWUSR | S_IRGRP);
+ EXPECT_EQ(FileFsPermissionToMode(FsPermission{.isOtherReadable = true}),
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ EXPECT_EQ(FileFsPermissionToMode(FsPermission{.isOtherExecutable = true}),
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IXOTH);
+ EXPECT_EQ(
+ FileFsPermissionToMode(FsPermission{.isOtherReadable = true, .isOtherExecutable = true}),
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | S_IXOTH);
+}
+
+TEST_F(FileUtilsTest, DirFsPermissionToMode) {
+ EXPECT_EQ(DirFsPermissionToMode(FsPermission{}), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
+ EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherReadable = true}),
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH);
+ EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherExecutable = true}),
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IXOTH);
+ EXPECT_EQ(DirFsPermissionToMode(FsPermission{.isOtherReadable = true, .isOtherExecutable = true}),
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+}
+
+} // namespace
+} // namespace artd
+} // namespace art
diff --git a/artd/path_utils.cc b/artd/path_utils.cc
new file mode 100644
index 0000000..295b023
--- /dev/null
+++ b/artd/path_utils.cc
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2022 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 "path_utils.h"
+
+#include <filesystem>
+
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "android-base/errors.h"
+#include "android-base/result.h"
+#include "android-base/strings.h"
+#include "arch/instruction_set.h"
+#include "base/file_utils.h"
+#include "file_utils.h"
+#include "fmt/format.h"
+#include "oat_file_assistant.h"
+
+namespace art {
+namespace artd {
+
+namespace {
+
+using ::aidl::com::android::server::art::ArtifactsPath;
+using ::aidl::com::android::server::art::DexMetadataPath;
+using ::aidl::com::android::server::art::ProfilePath;
+using ::aidl::com::android::server::art::VdexPath;
+using ::android::base::EndsWith;
+using ::android::base::Error;
+using ::android::base::Result;
+
+using ::fmt::literals::operator""_format; // NOLINT
+
+using PrebuiltProfilePath = ProfilePath::PrebuiltProfilePath;
+using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath;
+using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath;
+using SecondaryCurProfilePath = ProfilePath::SecondaryCurProfilePath;
+using SecondaryRefProfilePath = ProfilePath::SecondaryRefProfilePath;
+using TmpProfilePath = ProfilePath::TmpProfilePath;
+using WritableProfilePath = ProfilePath::WritableProfilePath;
+
+Result<void> ValidateAbsoluteNormalPath(const std::string& path_str) {
+ if (path_str.empty()) {
+ return Errorf("Path is empty");
+ }
+ if (path_str.find('\0') != std::string::npos) {
+ return Errorf("Path '{}' has invalid character '\\0'", path_str);
+ }
+ std::filesystem::path path(path_str);
+ if (!path.is_absolute()) {
+ return Errorf("Path '{}' is not an absolute path", path_str);
+ }
+ if (path.lexically_normal() != path_str) {
+ return Errorf("Path '{}' is not in normal form", path_str);
+ }
+ return {};
+}
+
+Result<void> ValidatePathElementSubstring(const std::string& path_element_substring,
+ const std::string& name) {
+ if (path_element_substring.empty()) {
+ return Errorf("{} is empty", name);
+ }
+ if (path_element_substring.find('/') != std::string::npos) {
+ return Errorf("{} '{}' has invalid character '/'", name, path_element_substring);
+ }
+ if (path_element_substring.find('\0') != std::string::npos) {
+ return Errorf("{} '{}' has invalid character '\\0'", name, path_element_substring);
+ }
+ return {};
+}
+
+Result<void> ValidatePathElement(const std::string& path_element, const std::string& name) {
+ OR_RETURN(ValidatePathElementSubstring(path_element, name));
+ if (path_element == "." || path_element == "..") {
+ return Errorf("Invalid {} '{}'", name, path_element);
+ }
+ return {};
+}
+
+Result<std::string> GetAndroidDataOrError() {
+ std::string error_msg;
+ std::string result = GetAndroidDataSafe(&error_msg);
+ if (!error_msg.empty()) {
+ return Error() << error_msg;
+ }
+ return result;
+}
+
+Result<std::string> GetArtRootOrError() {
+ std::string error_msg;
+ std::string result = GetArtRootSafe(&error_msg);
+ if (!error_msg.empty()) {
+ return Error() << error_msg;
+ }
+ return result;
+}
+
+} // namespace
+
+Result<void> ValidateDexPath(const std::string& dex_path) {
+ OR_RETURN(ValidateAbsoluteNormalPath(dex_path));
+ if (!EndsWith(dex_path, ".apk") && !EndsWith(dex_path, ".jar")) {
+ return Errorf("Dex path '{}' has an invalid extension", dex_path);
+ }
+ return {};
+}
+
+Result<std::string> BuildArtBinPath(const std::string& binary_name) {
+ return "{}/bin/{}"_format(OR_RETURN(GetArtRootOrError()), binary_name);
+}
+
+Result<std::string> BuildOatPath(const ArtifactsPath& artifacts_path) {
+ OR_RETURN(ValidateDexPath(artifacts_path.dexPath));
+
+ InstructionSet isa = GetInstructionSetFromString(artifacts_path.isa.c_str());
+ if (isa == InstructionSet::kNone) {
+ return Errorf("Instruction set '{}' is invalid", artifacts_path.isa);
+ }
+
+ std::string error_msg;
+ std::string path;
+ if (artifacts_path.isInDalvikCache) {
+ // Apps' OAT files are never in ART APEX data.
+ if (!OatFileAssistant::DexLocationToOatFilename(
+ artifacts_path.dexPath, isa, /*deny_art_apex_data_files=*/true, &path, &error_msg)) {
+ return Error() << error_msg;
+ }
+ return path;
+ } else {
+ if (!OatFileAssistant::DexLocationToOdexFilename(
+ artifacts_path.dexPath, isa, &path, &error_msg)) {
+ return Error() << error_msg;
+ }
+ return path;
+ }
+}
+
+Result<std::string> BuildPrimaryRefProfilePath(
+ const PrimaryRefProfilePath& primary_ref_profile_path) {
+ OR_RETURN(ValidatePathElement(primary_ref_profile_path.packageName, "packageName"));
+ OR_RETURN(ValidatePathElementSubstring(primary_ref_profile_path.profileName, "profileName"));
+ return "{}/misc/profiles/ref/{}/{}.prof"_format(OR_RETURN(GetAndroidDataOrError()),
+ primary_ref_profile_path.packageName,
+ primary_ref_profile_path.profileName);
+}
+
+Result<std::string> BuildPrebuiltProfilePath(const PrebuiltProfilePath& prebuilt_profile_path) {
+ OR_RETURN(ValidateDexPath(prebuilt_profile_path.dexPath));
+ return prebuilt_profile_path.dexPath + ".prof";
+}
+
+Result<std::string> BuildPrimaryCurProfilePath(
+ const PrimaryCurProfilePath& primary_cur_profile_path) {
+ OR_RETURN(ValidatePathElement(primary_cur_profile_path.packageName, "packageName"));
+ OR_RETURN(ValidatePathElementSubstring(primary_cur_profile_path.profileName, "profileName"));
+ return "{}/misc/profiles/cur/{}/{}/{}.prof"_format(OR_RETURN(GetAndroidDataOrError()),
+ primary_cur_profile_path.userId,
+ primary_cur_profile_path.packageName,
+ primary_cur_profile_path.profileName);
+}
+
+Result<std::string> BuildSecondaryRefProfilePath(
+ const SecondaryRefProfilePath& secondary_ref_profile_path) {
+ OR_RETURN(ValidateDexPath(secondary_ref_profile_path.dexPath));
+ std::filesystem::path dex_path(secondary_ref_profile_path.dexPath);
+ return "{}/oat/{}.prof"_format(dex_path.parent_path().string(), dex_path.filename().string());
+}
+
+Result<std::string> BuildSecondaryCurProfilePath(
+ const SecondaryCurProfilePath& secondary_cur_profile_path) {
+ OR_RETURN(ValidateDexPath(secondary_cur_profile_path.dexPath));
+ std::filesystem::path dex_path(secondary_cur_profile_path.dexPath);
+ return "{}/oat/{}.cur.prof"_format(dex_path.parent_path().string(), dex_path.filename().string());
+}
+
+Result<std::string> BuildFinalProfilePath(const TmpProfilePath& tmp_profile_path) {
+ const WritableProfilePath& final_path = tmp_profile_path.finalPath;
+ switch (final_path.getTag()) {
+ case WritableProfilePath::forPrimary:
+ return BuildPrimaryRefProfilePath(final_path.get<WritableProfilePath::forPrimary>());
+ case WritableProfilePath::forSecondary:
+ return BuildSecondaryRefProfilePath(final_path.get<WritableProfilePath::forSecondary>());
+ // No default. All cases should be explicitly handled, or the compilation will fail.
+ }
+ // This should never happen. Just in case we get a non-enumerator value.
+ LOG(FATAL) << "Unexpected writable profile path type {}"_format(final_path.getTag());
+}
+
+Result<std::string> BuildTmpProfilePath(const TmpProfilePath& tmp_profile_path) {
+ OR_RETURN(ValidatePathElementSubstring(tmp_profile_path.id, "id"));
+ return NewFile::BuildTempPath(OR_RETURN(BuildFinalProfilePath(tmp_profile_path)),
+ tmp_profile_path.id);
+}
+
+Result<std::string> BuildDexMetadataPath(const DexMetadataPath& dex_metadata_path) {
+ OR_RETURN(ValidateDexPath(dex_metadata_path.dexPath));
+ return ReplaceFileExtension(dex_metadata_path.dexPath, "dm");
+}
+
+Result<std::string> BuildProfileOrDmPath(const ProfilePath& profile_path) {
+ switch (profile_path.getTag()) {
+ case ProfilePath::primaryRefProfilePath:
+ return BuildPrimaryRefProfilePath(profile_path.get<ProfilePath::primaryRefProfilePath>());
+ case ProfilePath::prebuiltProfilePath:
+ return BuildPrebuiltProfilePath(profile_path.get<ProfilePath::prebuiltProfilePath>());
+ case ProfilePath::primaryCurProfilePath:
+ return BuildPrimaryCurProfilePath(profile_path.get<ProfilePath::primaryCurProfilePath>());
+ case ProfilePath::secondaryRefProfilePath:
+ return BuildSecondaryRefProfilePath(profile_path.get<ProfilePath::secondaryRefProfilePath>());
+ case ProfilePath::secondaryCurProfilePath:
+ return BuildSecondaryCurProfilePath(profile_path.get<ProfilePath::secondaryCurProfilePath>());
+ case ProfilePath::tmpProfilePath:
+ return BuildTmpProfilePath(profile_path.get<ProfilePath::tmpProfilePath>());
+ case ProfilePath::dexMetadataPath:
+ return BuildDexMetadataPath(profile_path.get<ProfilePath::dexMetadataPath>());
+ // No default. All cases should be explicitly handled, or the compilation will fail.
+ }
+ // This should never happen. Just in case we get a non-enumerator value.
+ LOG(FATAL) << "Unexpected profile path type {}"_format(profile_path.getTag());
+}
+
+Result<std::string> BuildVdexPath(const VdexPath& vdex_path) {
+ DCHECK(vdex_path.getTag() == VdexPath::artifactsPath);
+ return OatPathToVdexPath(OR_RETURN(BuildOatPath(vdex_path.get<VdexPath::artifactsPath>())));
+}
+
+} // namespace artd
+} // namespace art
diff --git a/artd/path_utils.h b/artd/path_utils.h
new file mode 100644
index 0000000..0cc017e
--- /dev/null
+++ b/artd/path_utils.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_ARTD_PATH_UTILS_H_
+#define ART_ARTD_PATH_UTILS_H_
+
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "android-base/result.h"
+#include "base/file_utils.h"
+
+namespace art {
+namespace artd {
+
+android::base::Result<void> ValidateDexPath(const std::string& dex_path);
+
+android::base::Result<std::string> BuildArtBinPath(const std::string& binary_name);
+
+// Returns the absolute path to the OAT file built from the `ArtifactsPath`.
+android::base::Result<std::string> BuildOatPath(
+ const aidl::com::android::server::art::ArtifactsPath& artifacts_path);
+
+// Returns the path to the VDEX file that corresponds to the OAT file.
+inline std::string OatPathToVdexPath(const std::string& oat_path) {
+ return ReplaceFileExtension(oat_path, "vdex");
+}
+
+// Returns the path to the ART file that corresponds to the OAT file.
+inline std::string OatPathToArtPath(const std::string& oat_path) {
+ return ReplaceFileExtension(oat_path, "art");
+}
+
+android::base::Result<std::string> BuildPrimaryRefProfilePath(
+ const aidl::com::android::server::art::ProfilePath::PrimaryRefProfilePath&
+ primary_ref_profile_path);
+
+android::base::Result<std::string> BuildPrebuiltProfilePath(
+ const aidl::com::android::server::art::ProfilePath::PrebuiltProfilePath& prebuilt_profile_path);
+
+android::base::Result<std::string> BuildPrimaryCurProfilePath(
+ const aidl::com::android::server::art::ProfilePath::PrimaryCurProfilePath&
+ primary_cur_profile_path);
+
+android::base::Result<std::string> BuildSecondaryRefProfilePath(
+ const aidl::com::android::server::art::ProfilePath::SecondaryRefProfilePath&
+ secondary_ref_profile_path);
+
+android::base::Result<std::string> BuildSecondaryCurProfilePath(
+ const aidl::com::android::server::art::ProfilePath::SecondaryCurProfilePath&
+ secondary_cur_profile_path);
+
+android::base::Result<std::string> BuildFinalProfilePath(
+ const aidl::com::android::server::art::ProfilePath::TmpProfilePath& tmp_profile_path);
+
+android::base::Result<std::string> BuildTmpProfilePath(
+ const aidl::com::android::server::art::ProfilePath::TmpProfilePath& tmp_profile_path);
+
+android::base::Result<std::string> BuildDexMetadataPath(
+ const aidl::com::android::server::art::DexMetadataPath& dex_metadata_path);
+
+android::base::Result<std::string> BuildProfileOrDmPath(
+ const aidl::com::android::server::art::ProfilePath& profile_path);
+
+android::base::Result<std::string> BuildVdexPath(
+ const aidl::com::android::server::art::VdexPath& vdex_path);
+
+} // namespace artd
+} // namespace art
+
+#endif // ART_ARTD_PATH_UTILS_H_
diff --git a/artd/path_utils_test.cc b/artd/path_utils_test.cc
new file mode 100644
index 0000000..b0e1a25
--- /dev/null
+++ b/artd/path_utils_test.cc
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2022 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 "path_utils.h"
+
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "android-base/result-gmock.h"
+#include "base/common_art_test.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace artd {
+namespace {
+
+using ::aidl::com::android::server::art::ArtifactsPath;
+using ::aidl::com::android::server::art::DexMetadataPath;
+using ::aidl::com::android::server::art::ProfilePath;
+using ::aidl::com::android::server::art::VdexPath;
+using ::android::base::testing::HasError;
+using ::android::base::testing::HasValue;
+using ::android::base::testing::WithMessage;
+
+using PrebuiltProfilePath = ProfilePath::PrebuiltProfilePath;
+using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath;
+using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath;
+using SecondaryCurProfilePath = ProfilePath::SecondaryCurProfilePath;
+using SecondaryRefProfilePath = ProfilePath::SecondaryRefProfilePath;
+using TmpProfilePath = ProfilePath::TmpProfilePath;
+
+using std::literals::operator""s; // NOLINT
+
+class PathUtilsTest : public CommonArtTest {};
+
+TEST_F(PathUtilsTest, BuildArtBinPath) {
+ auto scratch_dir = std::make_unique<ScratchDir>();
+ auto art_root_env = ScopedUnsetEnvironmentVariable("ANDROID_ART_ROOT");
+ setenv("ANDROID_ART_ROOT", scratch_dir->GetPath().c_str(), /*overwrite=*/1);
+ EXPECT_THAT(BuildArtBinPath("foo"), HasValue(scratch_dir->GetPath() + "/bin/foo"));
+}
+
+TEST_F(PathUtilsTest, BuildOatPath) {
+ EXPECT_THAT(
+ BuildOatPath(ArtifactsPath{.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}),
+ HasValue("/a/oat/arm64/b.odex"));
+}
+
+TEST_F(PathUtilsTest, BuildOatPathDalvikCache) {
+ EXPECT_THAT(
+ BuildOatPath(ArtifactsPath{.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = true}),
+ HasValue(android_data_ + "/dalvik-cache/arm64/a@b.apk@classes.dex"));
+}
+
+TEST_F(PathUtilsTest, BuildOatPathEmptyDexPath) {
+ EXPECT_THAT(BuildOatPath(ArtifactsPath{.dexPath = "", .isa = "arm64", .isInDalvikCache = false}),
+ HasError(WithMessage("Path is empty")));
+}
+
+TEST_F(PathUtilsTest, BuildOatPathRelativeDexPath) {
+ EXPECT_THAT(
+ BuildOatPath(ArtifactsPath{.dexPath = "a/b.apk", .isa = "arm64", .isInDalvikCache = false}),
+ HasError(WithMessage("Path 'a/b.apk' is not an absolute path")));
+}
+
+TEST_F(PathUtilsTest, BuildOatPathNonNormalDexPath) {
+ EXPECT_THAT(BuildOatPath(ArtifactsPath{
+ .dexPath = "/a/c/../b.apk", .isa = "arm64", .isInDalvikCache = false}),
+ HasError(WithMessage("Path '/a/c/../b.apk' is not in normal form")));
+}
+
+TEST_F(PathUtilsTest, BuildOatPathNul) {
+ EXPECT_THAT(BuildOatPath(ArtifactsPath{
+ .dexPath = "/a/\0/b.apk"s, .isa = "arm64", .isInDalvikCache = false}),
+ HasError(WithMessage("Path '/a/\0/b.apk' has invalid character '\\0'"s)));
+}
+
+TEST_F(PathUtilsTest, BuildOatPathInvalidDexExtension) {
+ EXPECT_THAT(BuildOatPath(ArtifactsPath{
+ .dexPath = "/a/b.invalid", .isa = "arm64", .isInDalvikCache = false}),
+ HasError(WithMessage("Dex path '/a/b.invalid' has an invalid extension")));
+}
+
+TEST_F(PathUtilsTest, BuildOatPathInvalidIsa) {
+ EXPECT_THAT(BuildOatPath(
+ ArtifactsPath{.dexPath = "/a/b.apk", .isa = "invalid", .isInDalvikCache = false}),
+ HasError(WithMessage("Instruction set 'invalid' is invalid")));
+}
+
+TEST_F(PathUtilsTest, OatPathToVdexPath) {
+ EXPECT_EQ(OatPathToVdexPath("/a/oat/arm64/b.odex"), "/a/oat/arm64/b.vdex");
+}
+
+TEST_F(PathUtilsTest, OatPathToArtPath) {
+ EXPECT_EQ(OatPathToArtPath("/a/oat/arm64/b.odex"), "/a/oat/arm64/b.art");
+}
+
+TEST_F(PathUtilsTest, BuildPrimaryRefProfilePath) {
+ EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{.packageName = "com.android.foo",
+ .profileName = "primary"}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathPackageNameOk) {
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "...", .profileName = "primary"}),
+ HasValue(android_data_ + "/misc/profiles/ref/.../primary.prof"));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "!@#$%^&*()_+-=", .profileName = "primary"}),
+ HasValue(android_data_ + "/misc/profiles/ref/!@#$%^&*()_+-=/primary.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathPackageNameWrong) {
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "", .profileName = "primary"}),
+ HasError(WithMessage("packageName is empty")));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = ".", .profileName = "primary"}),
+ HasError(WithMessage("Invalid packageName '.'")));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "..", .profileName = "primary"}),
+ HasError(WithMessage("Invalid packageName '..'")));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "a/b", .profileName = "primary"}),
+ HasError(WithMessage("packageName 'a/b' has invalid character '/'")));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "a\0b"s, .profileName = "primary"}),
+ HasError(WithMessage("packageName 'a\0b' has invalid character '\\0'"s)));
+}
+
+TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathProfileNameOk) {
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "."}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/..prof"));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = ".."}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/...prof"));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(PrimaryRefProfilePath{.packageName = "com.android.foo",
+ .profileName = "!@#$%^&*()_+-="}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/!@#$%^&*()_+-=.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildPrimaryRefProfilePathProfileNameWrong) {
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = ""}),
+ HasError(WithMessage("profileName is empty")));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "a/b"}),
+ HasError(WithMessage("profileName 'a/b' has invalid character '/'")));
+ EXPECT_THAT(BuildPrimaryRefProfilePath(
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "a\0b"s}),
+ HasError(WithMessage("profileName 'a\0b' has invalid character '\\0'"s)));
+}
+
+TEST_F(PathUtilsTest, BuildFinalProfilePathForPrimary) {
+ EXPECT_THAT(BuildFinalProfilePath(TmpProfilePath{
+ .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo",
+ .profileName = "primary"},
+ .id = "12345"}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildFinalProfilePathForSecondary) {
+ EXPECT_THAT(BuildFinalProfilePath(TmpProfilePath{
+ .finalPath = SecondaryRefProfilePath{.dexPath = android_data_ +
+ "/user/0/com.android.foo/a.apk"},
+ .id = "12345"}),
+ HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildTmpProfilePathForPrimary) {
+ EXPECT_THAT(
+ BuildTmpProfilePath(TmpProfilePath{
+ .finalPath =
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"},
+ .id = "12345"}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof.12345.tmp"));
+}
+
+TEST_F(PathUtilsTest, BuildTmpProfilePathForSecondary) {
+ EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{
+ .finalPath = SecondaryRefProfilePath{.dexPath = android_data_ +
+ "/user/0/com.android.foo/a.apk"},
+ .id = "12345"}),
+ HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof.12345.tmp"));
+}
+
+TEST_F(PathUtilsTest, BuildTmpProfilePathIdWrong) {
+ EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{
+ .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo",
+ .profileName = "primary"},
+ .id = ""}),
+ HasError(WithMessage("id is empty")));
+ EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{
+ .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo",
+ .profileName = "primary"},
+ .id = "123/45"}),
+ HasError(WithMessage("id '123/45' has invalid character '/'")));
+ EXPECT_THAT(BuildTmpProfilePath(TmpProfilePath{
+ .finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo",
+ .profileName = "primary"},
+ .id = "123\0a"s}),
+ HasError(WithMessage("id '123\0a' has invalid character '\\0'"s)));
+}
+
+TEST_F(PathUtilsTest, BuildPrebuiltProfilePath) {
+ EXPECT_THAT(BuildPrebuiltProfilePath(PrebuiltProfilePath{.dexPath = "/a/b.apk"}),
+ HasValue("/a/b.apk.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildPrimaryCurProfilePath) {
+ EXPECT_THAT(BuildPrimaryCurProfilePath(PrimaryCurProfilePath{
+ .userId = 1, .packageName = "com.android.foo", .profileName = "primary"}),
+ HasValue(android_data_ + "/misc/profiles/cur/1/com.android.foo/primary.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildSecondaryRefProfilePath) {
+ EXPECT_THAT(BuildSecondaryRefProfilePath(SecondaryRefProfilePath{
+ .dexPath = android_data_ + "/user/0/com.android.foo/a.apk"}),
+ HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildSecondaryCurProfilePath) {
+ EXPECT_THAT(BuildSecondaryCurProfilePath(SecondaryCurProfilePath{
+ .dexPath = android_data_ + "/user/0/com.android.foo/a.apk"}),
+ HasValue(android_data_ + "/user/0/com.android.foo/oat/a.apk.cur.prof"));
+}
+
+TEST_F(PathUtilsTest, BuildDexMetadataPath) {
+ EXPECT_THAT(BuildDexMetadataPath(DexMetadataPath{.dexPath = "/a/b.apk"}), HasValue("/a/b.dm"));
+}
+
+TEST_F(PathUtilsTest, BuildProfilePath) {
+ EXPECT_THAT(BuildProfileOrDmPath(PrimaryRefProfilePath{.packageName = "com.android.foo",
+ .profileName = "primary"}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof"));
+ EXPECT_THAT(
+ BuildProfileOrDmPath(TmpProfilePath{
+ .finalPath =
+ PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"},
+ .id = "12345"}),
+ HasValue(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof.12345.tmp"));
+ EXPECT_THAT(BuildProfileOrDmPath(PrebuiltProfilePath{.dexPath = "/a/b.apk"}),
+ HasValue("/a/b.apk.prof"));
+ EXPECT_THAT(BuildProfileOrDmPath(PrimaryCurProfilePath{
+ .userId = 1, .packageName = "com.android.foo", .profileName = "primary"}),
+ HasValue(android_data_ + "/misc/profiles/cur/1/com.android.foo/primary.prof"));
+ EXPECT_THAT(BuildProfileOrDmPath(DexMetadataPath{.dexPath = "/a/b.apk"}), HasValue("/a/b.dm"));
+}
+
+TEST_F(PathUtilsTest, BuildVdexPath) {
+ EXPECT_THAT(
+ BuildVdexPath(ArtifactsPath{.dexPath = "/a/b.apk", .isa = "arm64", .isInDalvikCache = false}),
+ HasValue("/a/oat/arm64/b.vdex"));
+}
+
+} // namespace
+} // namespace artd
+} // namespace art
diff --git a/artd/testing.h b/artd/testing.h
new file mode 100644
index 0000000..df01a9a
--- /dev/null
+++ b/artd/testing.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_ARTD_TESTING_H_
+#define ART_ARTD_TESTING_H_
+
+// Returns the value of the given `android::base::Result`, or reports the error as a gMock matcher
+// mismatch. This is only to be used in a gMock matcher.
+#define OR_MISMATCH(expr) \
+ ({ \
+ decltype(expr)&& tmp__ = (expr); \
+ if (!tmp__.ok()) { \
+ *result_listener << tmp__.error().message(); \
+ return false; \
+ } \
+ std::move(tmp__).value(); \
+ })
+
+// Returns the value of the given `android::base::Result`, or fails the GoogleTest.
+#define OR_FAIL(expr) \
+ ({ \
+ decltype(expr)&& tmp__ = (expr); \
+ ASSERT_TRUE(tmp__.ok()) << tmp__.error().message(); \
+ std::move(tmp__).value(); \
+ })
+
+#endif // ART_ARTD_TESTING_H_
diff --git a/artd/tests/src/com/android/art/ArtdIntegrationTests.java b/artd/tests/src/com/android/art/ArtdIntegrationTests.java
index 7d40adb..2a32972 100644
--- a/artd/tests/src/com/android/art/ArtdIntegrationTests.java
+++ b/artd/tests/src/com/android/art/ArtdIntegrationTests.java
@@ -16,11 +16,12 @@
package com.android.art;
-import android.os.IArtd;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import com.android.server.art.IArtd;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
index d781f84..c7f9f6b 100644
--- a/benchmark/Android.bp
+++ b/benchmark/Android.bp
@@ -51,6 +51,7 @@
// TODO(ngeoffray): find a way to link against the libraries in the apex.
shared_libs: [
"libart",
+ "libartbase",
"libbase",
],
}
@@ -82,3 +83,30 @@
},
},
}
+
+art_cc_library {
+ name: "libgolemtiagent",
+ host_supported: true,
+ defaults: ["art_defaults"],
+ srcs: [
+ "golem-tiagent/golem-tiagent.cc",
+ ],
+ target: {
+ // This has to be duplicated for android and host to make sure it
+ // comes after the -Wframe-larger-than warnings inserted by art.go
+ // target-specific properties
+ android: {
+ cflags: ["-Wno-frame-larger-than="],
+ },
+ host: {
+ cflags: ["-Wno-frame-larger-than="],
+ },
+ },
+ header_libs: [
+ "libnativehelper_header_only",
+ "libopenjdkjvmti_headers"
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/benchmark/golem-tiagent/golem-tiagent.cc b/benchmark/golem-tiagent/golem-tiagent.cc
new file mode 100644
index 0000000..be2c727
--- /dev/null
+++ b/benchmark/golem-tiagent/golem-tiagent.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 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 "android-base/macros.h"
+#include "android-base/logging.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace art {
+
+jvmtiEnv* jvmti_env = nullptr;
+
+void CheckJvmtiError(jvmtiEnv* env, jvmtiError error) {
+ if (error != JVMTI_ERROR_NONE) {
+ char* error_name;
+ jvmtiError name_error = env->GetErrorName(error, &error_name);
+ if (name_error != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Unable to get error name for " << error;
+ }
+ LOG(FATAL) << "Unexpected error: " << error_name;
+ }
+}
+
+static void JNICALL VMInitCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env,
+ jthread thread ATTRIBUTE_UNUSED) {
+ // Set a breakpoint on a rare method that we won't expect to be hit.
+ // java.lang.Thread.stop is deprecated and not expected to be used.
+ jclass cl = jni_env->FindClass("java/lang/Thread");
+ if (cl == nullptr) {
+ LOG(FATAL) << "Cannot find class java/lang/Thread to set a breakpoint";
+ }
+
+ jmethodID method = jni_env->GetMethodID(cl, "stop", "()V");
+ if (method == nullptr) {
+ LOG(FATAL) << "Cannot find method to set a breapoint";
+ }
+
+ jlong start = 0;
+ jlong end;
+ CheckJvmtiError(jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
+ CheckJvmtiError(jvmti_env, jvmti_env->SetBreakpoint(method, start));
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ // Setup jvmti_env
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0) != 0) {
+ LOG(ERROR) << "Unable to get jvmti env!";
+ return 1;
+ }
+
+ // Enable breakpoint capability
+ jvmtiCapabilities capabilities;
+ memset(&capabilities, 0, sizeof(capabilities));
+ capabilities.can_generate_breakpoint_events = 1;
+ CheckJvmtiError(jvmti_env, jvmti_env->AddCapabilities(&capabilities));
+
+ // Set a callback for VM_INIT phase so we can set a breakpoint. We cannot just
+ // set a breakpoint here since vm isn't fully initialized here.
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.VMInit = VMInitCallback;
+ CheckJvmtiError(jvmti_env, jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)));
+ CheckJvmtiError(jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr));
+
+ return 0;
+}
+
+} // namespace art
diff --git a/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java b/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java
index 1550e81..d710e34 100644
--- a/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java
+++ b/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java
@@ -20,6 +20,10 @@
public static String longString1 = "This is a long string 1";
public static String longString2 = "This is a long string 2";
public static int int1 = 42;
+ public static double double1 = 42.0;
+ public static double double2 = 1.0E308;
+ public static float float1 = 42.0f;
+ public static float float2 = 1.0E38f;
public void timeAppendStrings(int count) {
String s1 = string1;
@@ -59,4 +63,74 @@
throw new AssertionError();
}
}
+
+ public void timeAppendStringAndDouble(int count) {
+ String s1 = string1;
+ double d1 = double1;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + d1;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Double.toString(d1).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringAndHugeDouble(int count) {
+ String s1 = string1;
+ double d2 = double2;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + d2;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Double.toString(d2).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringAndFloat(int count) {
+ String s1 = string1;
+ float f1 = float1;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + f1;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Float.toString(f1).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringAndHugeFloat(int count) {
+ String s1 = string1;
+ float f2 = float2;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + f2;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Float.toString(f2).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringDoubleStringAndFloat(int count) {
+ String s1 = string1;
+ String s2 = string2;
+ double d1 = double1;
+ float f1 = float1;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + d1 + s2 + f1;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() +
+ Double.toString(d1).length() +
+ s2.length() +
+ Float.toString(f1).length())) {
+ throw new AssertionError();
+ }
+ }
}
diff --git a/build/Android.bp b/build/Android.bp
index d2545a4..49a4363 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -28,27 +28,24 @@
}
art_clang_tidy_errors = [
- "android-cloexec-open",
- "bugprone-lambda-function-name",
- "bugprone-unused-raii", // Protect scoped things like MutexLock.
- "bugprone-virtual-near-miss",
- "modernize-use-bool-literals",
- "performance-implicit-conversion-in-loop",
- "performance-unnecessary-copy-initialization",
-]
-
-art_clang_tidy_allowed = [
- // Many files have these warnings. Move them to art_clang_tidy_errors
- // when all files are free of these warnings.
"android-cloexec-dup",
+ "android-cloexec-open",
"bugprone-argument-comment",
+ "bugprone-lambda-function-name",
+ "bugprone-macro-parentheses",
+ "bugprone-unused-raii", // Protect scoped things like MutexLock.
"bugprone-unused-return-value",
+ "bugprone-virtual-near-miss",
"misc-unused-using-decls",
+ "modernize-use-bool-literals",
"modernize-use-nullptr",
- "modernize-use-using",
"performance-faster-string-find",
"performance-for-range-copy",
+ "performance-implicit-conversion-in-loop",
+ "performance-inefficient-vector-operation",
+ "performance-no-automatic-move",
"performance-noexcept-move-constructor",
+ "performance-unnecessary-copy-initialization",
"performance-unnecessary-value-param",
]
@@ -66,6 +63,9 @@
"-modernize-return-braced-init-list",
"-modernize-use-default-member-init",
"-modernize-pass-by-value",
+ // The only two remaining offenders are art/openjdkjvmti/include/jvmti.h and
+ // libcore/ojluni/src/main/native/jvm.h, which are both external files by Oracle
+ "-modernize-use-using",
]
soong_config_module_type_import {
@@ -203,6 +203,12 @@
// TODO(b/172480617): Clean up with that.
enabled: false,
},
+ not_windows: {
+ // Don't export symbols of statically linked libziparchive.
+ // Workaround to fix ODR violations (b/264235288) in gtests.
+ // `--exclude-libs` flags is not supported on windows.
+ ldflags: ["-Wl,--exclude-libs=libziparchive.a"],
+ },
host: {
cflags: [
// Bug: 15446488. We don't omit the frame pointer to work around
@@ -246,7 +252,7 @@
},
},
- tidy_checks: art_clang_tidy_errors + art_clang_tidy_allowed + art_clang_tidy_disabled,
+ tidy_checks: art_clang_tidy_errors + art_clang_tidy_disabled,
tidy_checks_as_errors: art_clang_tidy_errors,
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index def3190..11bdf4c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -27,7 +27,7 @@
# Manually add system libraries that we need to run the host ART tools.
my_files += \
- $(foreach lib, libbacktrace libbase libc++ libicu libicu_jni liblog libsigchain libunwindstack \
+ $(foreach lib, libbase libc++ libicu libicu_jni liblog libsigchain libunwindstack \
libziparchive libjavacore libandroidio libopenjdkd liblz4 liblzma, \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST)/$(lib).so:lib64/$(lib).so \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST,,2ND)/$(lib).so:lib/$(lib).so) \
@@ -107,13 +107,13 @@
art_cmdline_tests \
art_compiler_host_tests \
art_compiler_tests \
- art_dex2oat_tests \
art_dexanalyze_tests \
art_dexdiag_tests \
art_dexdump_tests \
art_dexlayout_tests \
art_dexlist_tests \
art_dexoptanalyzer_tests \
+ art_disassembler_tests \
art_hiddenapi_tests \
art_imgdiag_tests \
art_libartbase_tests \
@@ -127,13 +127,28 @@
art_libprofile_tests \
art_oatdump_tests \
art_profman_tests \
- art_runtime_compiler_tests \
art_runtime_tests \
- art_sigchain_tests \
-ART_TEST_MODULES_TARGET := $(ART_TEST_MODULES_COMMON) art_odrefresh_tests
+# b/258770641 Temporarily disable sigchain and dex2oat tests on ASAN configuration while we
+# investigate the failures.
+ifeq (,$(SANITIZE_HOST))
+ ART_TEST_MODULES_COMMON += art_sigchain_tests
+ ART_TEST_MODULES_COMMON += art_dex2oat_tests
+endif
+
+ART_TEST_MODULES_TARGET := $(ART_TEST_MODULES_COMMON) \
+ art_artd_tests \
+ art_odrefresh_tests \
+
ART_TEST_MODULES_HOST := $(ART_TEST_MODULES_COMMON)
+ifneq (,$(wildcard frameworks/native/libs/binder))
+ # Only include the artd host tests if we have the binder sources available and
+ # can build the libbinder_ndk dependency. It is not available as a prebuilt on
+ # master-art.
+ ART_TEST_MODULES_HOST += art_artd_tests
+endif
+
ART_TARGET_GTEST_NAMES := $(foreach tm,$(ART_TEST_MODULES_TARGET),\
$(foreach path,$(ART_TEST_LIST_device_$(TARGET_ARCH)_$(tm)),\
$(notdir $(path))\
diff --git a/build/README.md b/build/README.md
index 22726ee..a94d0fc 100644
--- a/build/README.md
+++ b/build/README.md
@@ -31,13 +31,13 @@
See the [Android source access
instructions](https://source.android.com/setup/build/downloading) for
- further details.
+ further details. Google internal users please see [go/sync](http://go/sync).
2. Set up the development environment:
```
banchan com.android.art <arch>
- export SOONG_ALLOW_MISSING_DEPENDENCIES=true
+ export SOONG_ALLOW_MISSING_DEPENDENCIES=true BUILD_BROKEN_DISABLE_BAZEL=true
```
For Google internal builds on the internal master-art branch, specify
@@ -45,7 +45,7 @@
```
banchan com.google.android.art mainline_modules_<arch>
- export SOONG_ALLOW_MISSING_DEPENDENCIES=true
+ export SOONG_ALLOW_MISSING_DEPENDENCIES=true BUILD_BROKEN_DISABLE_BAZEL=true
```
`<arch>` is the device architecture, one of `arm`, `arm64`, `x86`, or
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 898c4b0..2a76057 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -59,7 +59,6 @@
// TODO(b/124476339): Clean up the following libraries once "required"
// dependencies work with APEX libraries.
"libart-compiler",
- "libartservice",
"libdt_fd_forward",
"libdt_socket",
"libjdwp",
@@ -167,6 +166,8 @@
]
// Core Java libraries.
+// This list must be the same as art-bootclasspath-fragment because it's that which is pulled in
+// through bootclasspath_fragments below. (com.android.art-device-defaults-minus-odrefresh)
libcore_java_libs = [
"core-oj",
"core-libart",
@@ -263,11 +264,14 @@
bootclasspath_fragments: ["art-bootclasspath-fragment"],
systemserverclasspath_fragments: ["art-systemserverclasspath-fragment"],
compat_configs: ["libcore-platform-compat-config"],
- java_libs: libcore_java_libs,
native_shared_libs: art_runtime_base_native_shared_libs +
art_runtime_base_native_device_only_shared_libs +
libcore_native_shared_libs,
+ jni_libs: [
+ "libartservice",
+ ],
binaries: [
+ "art_exec",
"artd",
],
multilib: {
@@ -298,9 +302,6 @@
enabled: false,
},
},
- // Indicates that pre-installed version of this apex can be compressed.
- // Whether it actually will be compressed is controlled on per-device basis.
- compressible: true,
}
apex_defaults {
@@ -321,6 +322,9 @@
art_runtime_run_test_libs +
art_runtime_debug_native_shared_libs +
libcore_debug_native_shared_libs,
+ jni_libs: [
+ "libartserviced",
+ ],
multilib: {
both: {
binaries: art_tools_debug_binaries_both +
@@ -358,6 +362,7 @@
file_contexts: ":com.android.art-file_contexts",
certificate: ":com.android.art.certificate",
installable: false,
+ compressible: false,
}
apex_test {
@@ -395,6 +400,7 @@
// ART gtests with dependencies on internal ART APEX libraries.
art_gtests = [
+ "art_artd_tests",
"art_cmdline_tests",
"art_compiler_tests",
"art_dex2oat_tests",
@@ -403,6 +409,7 @@
"art_dexdump_tests",
"art_dexlayout_tests",
"art_dexlist_tests",
+ "art_disassembler_tests",
"art_dexoptanalyzer_tests",
"art_imgdiag_tests",
"art_libartbase_tests",
@@ -415,7 +422,6 @@
"art_oatdump_tests",
"art_odrefresh_tests",
"art_profman_tests",
- "art_runtime_compiler_tests",
"art_runtime_tests",
"art_sigchain_tests",
]
@@ -430,7 +436,21 @@
certificate: ":com.android.art.certificate",
tests: art_gtests,
binaries: ["signal_dumper"], // Need signal_dumper for run-tests.
+ // Mark this test APEX as non-updatable, as its contains
+ // additional files (used only for testing) that would not pass
+ // dependency checks performed on updatable APEXes (see
+ // go/apex-allowed-deps-error).
updatable: false,
+ // Because this APEX is non-updatable, some of its native shared
+ // libraries (implicitly added as dependencies) are eligible to
+ // the symlink optimization. As we want this APEX to be
+ // self-contained (for testing purposes), we want to package
+ // these dependencies in this APEX, instead of symbolic links to
+ // their counterparts on the `system` partition, which may not
+ // even exist, as in the case of `libbacktrace` (see b/232790938
+ // and b/233357459). Marking this APEX as "future updatable"
+ // disables all symlink optimizations for it.
+ future_updatable: true,
}
// TODO: Do this better. art_apex_test_host will disable host builds when
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index b7d5e24..b56c501 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -219,6 +219,8 @@
return False, 'Could not find %s'
if fs_object.is_dir:
return False, '%s is a directory'
+ if fs_object.is_symlink:
+ return False, '%s is a symlink'
return True, ''
def is_dir(self, path):
@@ -274,17 +276,22 @@
def check_art_test_executable(self, filename, multilib=None):
dirs = self.arch_dirs_for_path(ART_TEST_DIR, multilib)
if not dirs:
- self.fail('ART test binary missing: %s', filename)
+ self.fail('Directories for ART test binary missing: %s', filename)
+ return
for dir in dirs:
test_path = '%s/%s' % (dir, filename)
self._expected_file_globs.add(test_path)
- if not self._provider.get(test_path).is_exec:
+ file_obj = self._provider.get(test_path)
+ if not file_obj:
+ self.fail('ART test binary missing: %s', test_path)
+ elif not file_obj.is_exec:
self.fail('%s is not executable', test_path)
def check_art_test_data(self, filename):
dirs = self.arch_dirs_for_path(ART_TEST_DIR)
if not dirs:
- self.fail('ART test data missing: %s', filename)
+ self.fail('Directories for ART test data missing: %s', filename)
+ return
for dir in dirs:
if not self.check_file('%s/%s' % (dir, filename)):
return
@@ -473,7 +480,6 @@
self._checker.check_native_library('libart-disassembler')
self._checker.check_native_library('libartbase')
self._checker.check_native_library('libartpalette')
- self._checker.check_native_library('libartservice')
self._checker.check_native_library('libarttools')
self._checker.check_native_library('libdt_fd_forward')
self._checker.check_native_library('libopenjdkjvm')
@@ -506,7 +512,6 @@
# catch invalid dependencies on /system or other APEXes that should go
# through an exported library with stubs (b/128708192 tracks implementing a
# better approach for that).
- self._checker.check_native_library('libbacktrace')
self._checker.check_native_library('libbase')
self._checker.check_native_library('libc++')
self._checker.check_native_library('libdt_socket')
@@ -515,7 +520,6 @@
self._checker.check_native_library('liblzma')
self._checker.check_native_library('libnpt')
self._checker.check_native_library('libunwindstack')
- self._checker.check_native_library('libziparchive')
# Allow extra dependencies that appear in ASAN builds.
self._checker.check_optional_native_library('libclang_rt.asan*')
@@ -546,14 +550,15 @@
# removed in Android R.
# Check binaries for ART.
+ self._checker.check_executable('art_exec')
self._checker.check_executable('artd')
self._checker.check_executable('oatdump')
self._checker.check_executable("odrefresh")
self._checker.check_symlinked_multilib_executable('dex2oat')
# Check internal libraries for ART.
+ self._checker.check_native_library('libartservice')
self._checker.check_native_library('libperfetto_hprof')
- self._checker.check_prefer64_library('artd-aidl-ndk')
# Check internal Java libraries
self._checker.check_java_library("service-art")
@@ -639,6 +644,7 @@
self._checker.check_symlinked_multilib_executable('dex2oatd')
# Check ART internal libraries.
+ self._checker.check_native_library('libartserviced')
self._checker.check_native_library('libperfetto_hprofd')
# Check internal native library dependencies.
@@ -666,6 +672,7 @@
def run(self):
# Check ART test binaries.
+ self._checker.check_art_test_executable('art_artd_tests')
self._checker.check_art_test_executable('art_cmdline_tests')
self._checker.check_art_test_executable('art_compiler_tests')
self._checker.check_art_test_executable('art_dex2oat_tests')
@@ -675,6 +682,7 @@
self._checker.check_art_test_executable('art_dexlayout_tests')
self._checker.check_art_test_executable('art_dexlist_tests')
self._checker.check_art_test_executable('art_dexoptanalyzer_tests')
+ self._checker.check_art_test_executable('art_disassembler_tests')
self._checker.check_art_test_executable('art_imgdiag_tests')
self._checker.check_art_test_executable('art_libartbase_tests')
self._checker.check_art_test_executable('art_libartpalette_tests')
@@ -686,7 +694,6 @@
self._checker.check_art_test_executable('art_oatdump_tests')
self._checker.check_art_test_executable('art_odrefresh_tests')
self._checker.check_art_test_executable('art_profman_tests')
- self._checker.check_art_test_executable('art_runtime_compiler_tests')
self._checker.check_art_test_executable('art_runtime_tests')
self._checker.check_art_test_executable('art_sigchain_tests')
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index 290f121..cdbb7c0 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -59,14 +59,27 @@
export TARGET_BUILD_UNBUNDLED=true
fi
-have_deapexer_p=false
+deapex_binaries=(
+ blkid_static
+ deapexer
+ debugfs_static
+ fsck.erofs
+)
+
+have_deapex_binaries=false
if [[ "$TARGET_FLATTEN_APEX" != true ]]; then
- if [ ! -e "$HOST_OUT/bin/deapexer" -o ! -e "$HOST_OUT/bin/debugfs_static" ] ; then
- say "Could not find deapexer and/or debugfs_static, building now."
- build/soong/soong_ui.bash --make-mode deapexer debugfs_static-host || \
- die "Cannot build deapexer and debugfs_static"
+ have_deapex_binaries=true
+ for f in ${deapex_binaries[@]}; do
+ if [ ! -e "$HOST_OUT/bin/$f" ]; then
+ have_deapex_binaries=false
+ fi
+ done
+ if $have_deapex_binaries; then :; else
+ deapex_targets=( ${deapex_binaries[@]/%/-host} )
+ say "Building host binaries for deapexer: ${deapex_targets[*]}"
+ build/soong/soong_ui.bash --make-mode ${deapex_targets[@]} || \
+ die "Failed to build: ${deapex_targets[*]}"
fi
- have_deapexer_p=true
fi
# Fail early.
@@ -203,7 +216,7 @@
apex_path="$PRODUCT_OUT/system/apex/${apex_module}.apex"
fi
fi
- if $have_deapexer_p; then
+ if $have_deapex_binaries; then
art_apex_test_args="$art_apex_test_args --deapexer $HOST_OUT/bin/deapexer"
art_apex_test_args="$art_apex_test_args --debugfs $HOST_OUT/bin/debugfs_static"
art_apex_test_args="$art_apex_test_args --fsckerofs $HOST_OUT/bin/fsck.erofs"
diff --git a/build/art.go b/build/art.go
index 6014148..cc53719 100644
--- a/build/art.go
+++ b/build/art.go
@@ -94,14 +94,16 @@
// the debug version. So make the gap consistent (and adjust for the worst).
if len(ctx.Config().SanitizeDevice()) > 0 || len(ctx.Config().SanitizeHost()) > 0 {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm=16384",
"-DART_STACK_OVERFLOW_GAP_arm64=16384",
+ "-DART_STACK_OVERFLOW_GAP_riscv64=16384",
"-DART_STACK_OVERFLOW_GAP_x86=16384",
"-DART_STACK_OVERFLOW_GAP_x86_64=20480")
} else {
cflags = append(cflags,
"-DART_STACK_OVERFLOW_GAP_arm=8192",
"-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_riscv64=8192",
"-DART_STACK_OVERFLOW_GAP_x86=8192",
"-DART_STACK_OVERFLOW_GAP_x86_64=8192")
}
@@ -140,7 +142,7 @@
)
cflags = append(cflags, "-DART_BASE_ADDRESS="+ctx.Config().LibartImgDeviceBaseAddress())
- minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA", "-0x1000000")
+ minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA", "(-0x1000000)")
maxDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA", "0x1000000")
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
@@ -154,7 +156,11 @@
if len(ctx.Config().SanitizeHost()) > 0 {
// art/test/137-cfi/cfi.cc
// error: stack frame size of 1944 bytes in function 'Java_Main_unwindInProcess'
- hostFrameSizeLimit = 6400
+ // b/249586057, need larger stack frame for newer clang compilers
+ hostFrameSizeLimit = 10000
+ // cannot add "-fsanitize-address-use-after-return=never" everywhere,
+ // or some file like compiler_driver.o can have stack frame of 30072 bytes.
+ // cflags = append(cflags, "-fsanitize-address-use-after-return=never")
}
cflags = append(cflags,
fmt.Sprintf("-Wframe-larger-than=%d", hostFrameSizeLimit),
@@ -162,7 +168,7 @@
)
cflags = append(cflags, "-DART_BASE_ADDRESS="+ctx.Config().LibartImgHostBaseAddress())
- minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA", "-0x1000000")
+ minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA", "(-0x1000000)")
maxDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA", "0x1000000")
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
@@ -173,7 +179,7 @@
}
clang_path := filepath.Join(config.ClangDefaultBase, ctx.Config().PrebuiltOS(), config.ClangDefaultVersion)
- cflags = append(cflags, "-DART_CLANG_PATH=\""+clang_path+"\"")
+ cflags = append(cflags, fmt.Sprintf("-DART_CLANG_PATH=\"%s\"", clang_path))
return cflags
}
@@ -313,7 +319,7 @@
// The 'key' is the file in testcases and 'value' is the path to copy it from.
// The actual copy will be done in make since soong does not do installations.
func addTestcasesFile(ctx android.InstallHookContext) {
- if ctx.Os() != ctx.Config().BuildOS || ctx.Module().IsSkipInstall() {
+ if ctx.Os() != ctx.Config().BuildOS || ctx.Target().HostCross || ctx.Module().IsSkipInstall() {
return
}
@@ -464,7 +470,8 @@
}
func artTest() android.Module {
- module := cc.TestFactory()
+ // Disable bp2build.
+ module := cc.NewTest(android.HostAndDeviceSupported, false /* bazelable */).Init()
installCodegenCustomizer(module, binary)
diff --git a/build/boot/boot-image-profile.txt b/build/boot/boot-image-profile.txt
index a842df1..34b919b 100644
--- a/build/boot/boot-image-profile.txt
+++ b/build/boot/boot-image-profile.txt
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+HSPLandroid/compat/Compatibility$BehaviorChangeDelegate;->isChangeEnabled(J)Z
HSPLandroid/compat/Compatibility;->isChangeEnabled(J)Z
HSPLandroid/compat/Compatibility;->setBehaviorChangeDelegate(Landroid/compat/Compatibility$BehaviorChangeDelegate;)V
HSPLandroid/system/ErrnoException;-><init>(Ljava/lang/String;I)V
@@ -658,7 +659,7 @@
HSPLcom/android/okhttp/okio/AsyncTimeout;->exit(Ljava/io/IOException;)Ljava/io/IOException;
HSPLcom/android/okhttp/okio/AsyncTimeout;->exit(Z)V
HSPLcom/android/okhttp/okio/AsyncTimeout;->remainingNanos(J)J
-HSPLcom/android/okhttp/okio/AsyncTimeout;->scheduleTimeout(Lcom/android/okhttp/okio/AsyncTimeout;JZ)V+]Ljava/lang/Object;Ljava/lang/Class;
+HSPLcom/android/okhttp/okio/AsyncTimeout;->scheduleTimeout(Lcom/android/okhttp/okio/AsyncTimeout;JZ)V
HSPLcom/android/okhttp/okio/AsyncTimeout;->sink(Lcom/android/okhttp/okio/Sink;)Lcom/android/okhttp/okio/Sink;
HSPLcom/android/okhttp/okio/AsyncTimeout;->source(Lcom/android/okhttp/okio/Source;)Lcom/android/okhttp/okio/Source;
HSPLcom/android/okhttp/okio/Buffer;-><init>()V
@@ -684,7 +685,7 @@
HSPLcom/android/okhttp/okio/Buffer;->readUtf8(J)Ljava/lang/String;
HSPLcom/android/okhttp/okio/Buffer;->readUtf8Line(J)Ljava/lang/String;
HSPLcom/android/okhttp/okio/Buffer;->size()J
-HSPLcom/android/okhttp/okio/Buffer;->skip(J)V+]Lcom/android/okhttp/okio/Segment;Lcom/android/okhttp/okio/Segment;
+HSPLcom/android/okhttp/okio/Buffer;->skip(J)V
HSPLcom/android/okhttp/okio/Buffer;->writableSegment(I)Lcom/android/okhttp/okio/Segment;
HSPLcom/android/okhttp/okio/Buffer;->write(Lcom/android/okhttp/okio/Buffer;J)V+]Lcom/android/okhttp/okio/Segment;Lcom/android/okhttp/okio/Segment;
HSPLcom/android/okhttp/okio/Buffer;->write([BII)Lcom/android/okhttp/okio/Buffer;
@@ -717,7 +718,7 @@
HSPLcom/android/okhttp/okio/Okio$1;->flush()V
HSPLcom/android/okhttp/okio/Okio$1;->write(Lcom/android/okhttp/okio/Buffer;J)V
HSPLcom/android/okhttp/okio/Okio$2;-><init>(Lcom/android/okhttp/okio/Timeout;Ljava/io/InputStream;)V
-HSPLcom/android/okhttp/okio/Okio$2;->read(Lcom/android/okhttp/okio/Buffer;J)J+]Lcom/android/okhttp/okio/Buffer;Lcom/android/okhttp/okio/Buffer;]Lcom/android/okhttp/okio/Timeout;Lcom/android/okhttp/okio/Okio$3;]Ljava/io/InputStream;Lcom/android/org/conscrypt/ConscryptEngineSocket$SSLInputStream;
+HSPLcom/android/okhttp/okio/Okio$2;->read(Lcom/android/okhttp/okio/Buffer;J)J+]Lcom/android/okhttp/okio/Buffer;Lcom/android/okhttp/okio/Buffer;]Lcom/android/okhttp/okio/Timeout;Lcom/android/okhttp/okio/Okio$3;
HSPLcom/android/okhttp/okio/Okio$3;-><init>(Ljava/net/Socket;)V
HSPLcom/android/okhttp/okio/Okio$3;->newTimeoutException(Ljava/io/IOException;)Ljava/io/IOException;
HSPLcom/android/okhttp/okio/Okio$3;->timedOut()V
@@ -731,7 +732,7 @@
HSPLcom/android/okhttp/okio/RealBufferedSink$1;-><init>(Lcom/android/okhttp/okio/RealBufferedSink;)V
HSPLcom/android/okhttp/okio/RealBufferedSink$1;->close()V
HSPLcom/android/okhttp/okio/RealBufferedSink$1;->flush()V
-HSPLcom/android/okhttp/okio/RealBufferedSink$1;->write([BII)V+]Lcom/android/okhttp/okio/Buffer;Lcom/android/okhttp/okio/Buffer;]Lcom/android/okhttp/okio/RealBufferedSink;Lcom/android/okhttp/okio/RealBufferedSink;
+HSPLcom/android/okhttp/okio/RealBufferedSink$1;->write([BII)V
HSPLcom/android/okhttp/okio/RealBufferedSink;-><init>(Lcom/android/okhttp/okio/Sink;)V
HSPLcom/android/okhttp/okio/RealBufferedSink;-><init>(Lcom/android/okhttp/okio/Sink;Lcom/android/okhttp/okio/Buffer;)V
HSPLcom/android/okhttp/okio/RealBufferedSink;->access$000(Lcom/android/okhttp/okio/RealBufferedSink;)Z
@@ -801,14 +802,14 @@
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;-><init>(Ljava/io/InputStream;I)V
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;-><init>(Ljava/io/InputStream;IZ)V
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;-><init>([B)V
-HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->buildObject(III)Lcom/android/org/bouncycastle/asn1/ASN1Primitive;
+HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->buildObject(III)Lcom/android/org/bouncycastle/asn1/ASN1Primitive;+]Lcom/android/org/bouncycastle/asn1/ASN1InputStream;Lcom/android/org/bouncycastle/asn1/ASN1InputStream;]Lcom/android/org/bouncycastle/asn1/ASN1StreamParser;Lcom/android/org/bouncycastle/asn1/ASN1StreamParser;
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->createPrimitiveDERObject(ILcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;[[B)Lcom/android/org/bouncycastle/asn1/ASN1Primitive;
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->getBuffer(Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;[[B)[B
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->readLength()I
-HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->readLength(Ljava/io/InputStream;IZ)I+]Ljava/io/InputStream;Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;,Lcom/android/org/bouncycastle/asn1/ASN1InputStream;
+HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->readLength(Ljava/io/InputStream;IZ)I
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->readObject()Lcom/android/org/bouncycastle/asn1/ASN1Primitive;
HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->readTagNumber(Ljava/io/InputStream;I)I
-HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->readVector(Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;)Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;+]Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;]Lcom/android/org/bouncycastle/asn1/ASN1InputStream;Lcom/android/org/bouncycastle/asn1/ASN1InputStream;]Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;
+HSPLcom/android/org/bouncycastle/asn1/ASN1InputStream;->readVector(Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;)Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;
HSPLcom/android/org/bouncycastle/asn1/ASN1Integer;-><init>(Ljava/math/BigInteger;)V
HSPLcom/android/org/bouncycastle/asn1/ASN1Integer;-><init>([BZ)V
HSPLcom/android/org/bouncycastle/asn1/ASN1Integer;->encode(Lcom/android/org/bouncycastle/asn1/ASN1OutputStream;Z)V
@@ -874,13 +875,13 @@
HSPLcom/android/org/bouncycastle/asn1/DERSequence;->getBodyLength()I
HSPLcom/android/org/bouncycastle/asn1/DERSequence;->toDERObject()Lcom/android/org/bouncycastle/asn1/ASN1Primitive;
HSPLcom/android/org/bouncycastle/asn1/DLFactory;-><clinit>()V
-HSPLcom/android/org/bouncycastle/asn1/DLFactory;->createSequence(Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;)Lcom/android/org/bouncycastle/asn1/ASN1Sequence;+]Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;
+HSPLcom/android/org/bouncycastle/asn1/DLFactory;->createSequence(Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;)Lcom/android/org/bouncycastle/asn1/ASN1Sequence;
HSPLcom/android/org/bouncycastle/asn1/DLSequence;-><init>()V
HSPLcom/android/org/bouncycastle/asn1/DLSequence;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;)V
HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->getRemaining()I
-HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->read()I
-HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->read([BII)I
-HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->readAllIntoByteArray([B)V+]Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;
+HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->read()I+]Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;]Ljava/io/InputStream;Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;,Lcom/android/org/bouncycastle/asn1/ASN1InputStream;
+HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->read([BII)I+]Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;]Ljava/io/InputStream;Lcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;,Lcom/android/org/bouncycastle/asn1/ASN1InputStream;
+HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->readAllIntoByteArray([B)V
HSPLcom/android/org/bouncycastle/asn1/DefiniteLengthInputStream;->toByteArray()[B
HSPLcom/android/org/bouncycastle/asn1/LimitedInputStream;-><init>(Ljava/io/InputStream;I)V
HSPLcom/android/org/bouncycastle/asn1/LimitedInputStream;->getLimit()I
@@ -911,11 +912,11 @@
HSPLcom/android/org/bouncycastle/crypto/digests/AndroidDigestFactoryOpenSSL;->getSHA1()Lcom/android/org/bouncycastle/crypto/Digest;
HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest$SHA1;-><init>()V
HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;-><init>(Ljava/lang/String;I)V
-HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;->doFinal([BI)I+]Ljava/security/MessageDigest;Ljava/security/MessageDigest$Delegate;
+HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;->doFinal([BI)I
HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;->getByteLength()I
HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;->getDigestSize()I
HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;->reset()V
-HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;->update([BII)V+]Ljava/security/MessageDigest;Ljava/security/MessageDigest$Delegate;
+HSPLcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest;->update([BII)V
HSPLcom/android/org/bouncycastle/crypto/engines/AESEngine;-><init>()V
HSPLcom/android/org/bouncycastle/crypto/engines/AESEngine;->generateWorkingKey([BZ)[[I
HSPLcom/android/org/bouncycastle/crypto/engines/AESEngine;->getAlgorithmName()Ljava/lang/String;
@@ -932,10 +933,10 @@
HSPLcom/android/org/bouncycastle/crypto/engines/DESEngine;->generateWorkingKey(Z[B)[I
HSPLcom/android/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator;-><init>(Lcom/android/org/bouncycastle/crypto/Digest;)V
HSPLcom/android/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator;->adjust([BI[B)V
-HSPLcom/android/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator;->generateDerivedKey(II)[B+]Lcom/android/org/bouncycastle/crypto/Digest;Lcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest$SHA1;,Lcom/android/org/bouncycastle/crypto/digests/SHA1Digest;
+HSPLcom/android/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator;->generateDerivedKey(II)[B
HSPLcom/android/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator;-><init>(Lcom/android/org/bouncycastle/crypto/Digest;)V
-HSPLcom/android/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator;->F([BI[B[BI)V+]Lcom/android/org/bouncycastle/crypto/Mac;Lcom/android/org/bouncycastle/crypto/macs/HMac;
-HSPLcom/android/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator;->generateDerivedKey(I)[B+]Lcom/android/org/bouncycastle/crypto/Mac;Lcom/android/org/bouncycastle/crypto/macs/HMac;
+HSPLcom/android/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator;->F([BI[B[BI)V
+HSPLcom/android/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator;->generateDerivedKey(I)[B
HSPLcom/android/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator;->generateDerivedMacParameters(I)Lcom/android/org/bouncycastle/crypto/CipherParameters;
HSPLcom/android/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator;->generateDerivedParameters(I)Lcom/android/org/bouncycastle/crypto/CipherParameters;
HSPLcom/android/org/bouncycastle/crypto/macs/HMac;-><clinit>()V
@@ -956,11 +957,11 @@
HSPLcom/android/org/bouncycastle/crypto/paddings/PKCS7Padding;->padCount([B)I
HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;-><init>(Lcom/android/org/bouncycastle/crypto/BlockCipher;)V
HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;-><init>(Lcom/android/org/bouncycastle/crypto/BlockCipher;Lcom/android/org/bouncycastle/crypto/paddings/BlockCipherPadding;)V
-HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;->doFinal([BI)I+]Lcom/android/org/bouncycastle/crypto/paddings/BlockCipherPadding;Lcom/android/org/bouncycastle/crypto/paddings/PKCS7Padding;]Lcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;Lcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;]Lcom/android/org/bouncycastle/crypto/BlockCipher;Lcom/android/org/bouncycastle/crypto/modes/CBCBlockCipher;
+HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;->doFinal([BI)I
HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;->getOutputSize(I)I
HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;->getUpdateOutputSize(I)I
HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;->init(ZLcom/android/org/bouncycastle/crypto/CipherParameters;)V
-HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;->processBytes([BII[BI)I+]Lcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;Lcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;]Lcom/android/org/bouncycastle/crypto/BlockCipher;Lcom/android/org/bouncycastle/crypto/engines/AESEngine;,Lcom/android/org/bouncycastle/crypto/modes/CBCBlockCipher;
+HSPLcom/android/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher;->processBytes([BII[BI)I
HSPLcom/android/org/bouncycastle/crypto/params/AsymmetricKeyParameter;-><init>(Z)V
HSPLcom/android/org/bouncycastle/crypto/params/DSAKeyParameters;-><init>(ZLcom/android/org/bouncycastle/crypto/params/DSAParameters;)V
HSPLcom/android/org/bouncycastle/crypto/params/DSAParameters;-><init>(Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;)V
@@ -1047,7 +1048,7 @@
HSPLcom/android/org/kxml2/io/KXmlParser;->getLineNumber()I
HSPLcom/android/org/kxml2/io/KXmlParser;->getName()Ljava/lang/String;
HSPLcom/android/org/kxml2/io/KXmlParser;->getNamespace()Ljava/lang/String;
-HSPLcom/android/org/kxml2/io/KXmlParser;->getNamespace(Ljava/lang/String;)Ljava/lang/String;+]Lcom/android/org/kxml2/io/KXmlParser;Lcom/android/org/kxml2/io/KXmlParser;
+HSPLcom/android/org/kxml2/io/KXmlParser;->getNamespace(Ljava/lang/String;)Ljava/lang/String;
HSPLcom/android/org/kxml2/io/KXmlParser;->getNamespaceCount(I)I
HSPLcom/android/org/kxml2/io/KXmlParser;->getText()Ljava/lang/String;
HSPLcom/android/org/kxml2/io/KXmlParser;->keepNamespaceAttributes()V
@@ -1074,7 +1075,7 @@
HSPLcom/android/org/kxml2/io/KXmlParser;->skip()V
HSPLcom/android/org/kxml2/io/KXmlSerializer;->append(C)V
HSPLcom/android/org/kxml2/io/KXmlSerializer;->append(Ljava/lang/String;)V
-HSPLcom/android/org/kxml2/io/KXmlSerializer;->append(Ljava/lang/String;II)V+]Ljava/lang/String;Ljava/lang/String;
+HSPLcom/android/org/kxml2/io/KXmlSerializer;->append(Ljava/lang/String;II)V
HSPLcom/android/org/kxml2/io/KXmlSerializer;->attribute(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/xmlpull/v1/XmlSerializer;
HSPLcom/android/org/kxml2/io/KXmlSerializer;->check(Z)V
HSPLcom/android/org/kxml2/io/KXmlSerializer;->endDocument()V
@@ -1111,17 +1112,17 @@
HSPLdalvik/system/BlockGuard$3;->initialValue()Ljava/lang/Object;
HSPLdalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;+]Ljava/lang/ThreadLocal;Ldalvik/system/BlockGuard$3;
HSPLdalvik/system/BlockGuard;->getVmPolicy()Ldalvik/system/BlockGuard$VmPolicy;
-HSPLdalvik/system/BlockGuard;->setThreadPolicy(Ldalvik/system/BlockGuard$Policy;)V
+HSPLdalvik/system/BlockGuard;->setThreadPolicy(Ldalvik/system/BlockGuard$Policy;)V+]Ljava/lang/ThreadLocal;Ldalvik/system/BlockGuard$3;
HSPLdalvik/system/BlockGuard;->setVmPolicy(Ldalvik/system/BlockGuard$VmPolicy;)V
HSPLdalvik/system/CloseGuard;-><init>()V
HSPLdalvik/system/CloseGuard;->close()V
HSPLdalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
HSPLdalvik/system/CloseGuard;->getReporter()Ldalvik/system/CloseGuard$Reporter;
-HSPLdalvik/system/CloseGuard;->open(Ljava/lang/String;)V+]Ldalvik/system/CloseGuard;Ldalvik/system/CloseGuard;
-HSPLdalvik/system/CloseGuard;->openWithCallSite(Ljava/lang/String;Ljava/lang/String;)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLdalvik/system/CloseGuard;->open(Ljava/lang/String;)V
+HSPLdalvik/system/CloseGuard;->openWithCallSite(Ljava/lang/String;Ljava/lang/String;)V
HSPLdalvik/system/CloseGuard;->setEnabled(Z)V
HSPLdalvik/system/CloseGuard;->setReporter(Ldalvik/system/CloseGuard$Reporter;)V
-HSPLdalvik/system/CloseGuard;->warnIfOpen()V+]Ldalvik/system/CloseGuard$Reporter;Landroid/os/StrictMode$AndroidCloseGuardReporter;
+HSPLdalvik/system/CloseGuard;->warnIfOpen()V
HSPLdalvik/system/DelegateLastClassLoader;-><init>(Ljava/lang/String;Ljava/lang/ClassLoader;)V
HSPLdalvik/system/DelegateLastClassLoader;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
HSPLdalvik/system/DelegateLastClassLoader;->loadClass(Ljava/lang/String;Z)Ljava/lang/Class;
@@ -1140,7 +1141,7 @@
HSPLdalvik/system/DexPathList$Element;->findClass(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
HSPLdalvik/system/DexPathList$Element;->findResource(Ljava/lang/String;)Ljava/net/URL;
HSPLdalvik/system/DexPathList$Element;->maybeInit()V
-HSPLdalvik/system/DexPathList$Element;->toString()Ljava/lang/String;
+HSPLdalvik/system/DexPathList$Element;->toString()Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/Boolean;Ljava/lang/Boolean;
HSPLdalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;)V
HSPLdalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;Ljava/lang/String;)V
HSPLdalvik/system/DexPathList$NativeLibraryElement;->equals(Ljava/lang/Object;)Z
@@ -1173,8 +1174,10 @@
HSPLdalvik/system/SocketTagger;->set(Ldalvik/system/SocketTagger;)V
HSPLdalvik/system/SocketTagger;->tag(Ljava/net/Socket;)V
HSPLdalvik/system/SocketTagger;->untag(Ljava/net/Socket;)V
+HSPLdalvik/system/VMRuntime$SdkVersionContainer;->-$$Nest$sfgetsdkVersion()I
HSPLdalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
HSPLdalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
+HSPLdalvik/system/VMRuntime;->getSdkVersion()I
HSPLdalvik/system/VMRuntime;->getTargetSdkVersion()I
HSPLdalvik/system/VMRuntime;->hiddenApiUsed(ILjava/lang/String;Ljava/lang/String;IZ)V
HSPLdalvik/system/VMRuntime;->notifyNativeAllocation()V+]Ldalvik/system/VMRuntime;Ldalvik/system/VMRuntime;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
@@ -1186,6 +1189,8 @@
HSPLdalvik/system/VMRuntime;->setHiddenApiUsageLogger(Ldalvik/system/VMRuntime$HiddenApiUsageLogger;)V
HSPLdalvik/system/VMRuntime;->setNonSdkApiUsageConsumer(Ljava/util/function/Consumer;)V
HSPLdalvik/system/VMRuntime;->setTargetSdkVersion(I)V
+HSPLdalvik/system/ZipPathValidator;->getInstance()Ldalvik/system/ZipPathValidator$Callback;
+HSPLdalvik/system/ZipPathValidator;->setCallback(Ldalvik/system/ZipPathValidator$Callback;)V
HSPLdalvik/system/ZygoteHooks;->cleanLocaleCaches()V
HSPLdalvik/system/ZygoteHooks;->gcAndFinalize()V
HSPLdalvik/system/ZygoteHooks;->isIndefiniteThreadSuspensionSafe()Z
@@ -1206,18 +1211,17 @@
HSPLjava/io/Bits;->putInt([BII)V
HSPLjava/io/Bits;->putLong([BIJ)V
HSPLjava/io/Bits;->putShort([BIS)V
-HSPLjava/io/BufferedInputStream$$ExternalSyntheticBackportWithForwarding0;->m(Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
HSPLjava/io/BufferedInputStream;-><init>(Ljava/io/InputStream;)V
HSPLjava/io/BufferedInputStream;-><init>(Ljava/io/InputStream;I)V
HSPLjava/io/BufferedInputStream;->available()I
HSPLjava/io/BufferedInputStream;->close()V
-HSPLjava/io/BufferedInputStream;->fill()V+]Ljava/io/InputStream;Ljava/io/FileInputStream;
+HSPLjava/io/BufferedInputStream;->fill()V
HSPLjava/io/BufferedInputStream;->getBufIfOpen()[B
HSPLjava/io/BufferedInputStream;->getInIfOpen()Ljava/io/InputStream;
HSPLjava/io/BufferedInputStream;->mark(I)V
HSPLjava/io/BufferedInputStream;->markSupported()Z
HSPLjava/io/BufferedInputStream;->read()I
-HSPLjava/io/BufferedInputStream;->read([BII)I+]Ljava/io/InputStream;Ljava/io/FileInputStream;
+HSPLjava/io/BufferedInputStream;->read([BII)I
HSPLjava/io/BufferedInputStream;->read1([BII)I
HSPLjava/io/BufferedInputStream;->reset()V
HSPLjava/io/BufferedInputStream;->skip(J)J
@@ -1235,18 +1239,18 @@
HSPLjava/io/BufferedReader;->read()I
HSPLjava/io/BufferedReader;->read([CII)I
HSPLjava/io/BufferedReader;->read1([CII)I
-HSPLjava/io/BufferedReader;->readLine()Ljava/lang/String;+]Ljava/io/BufferedReader;Ljava/io/BufferedReader;
+HSPLjava/io/BufferedReader;->readLine()Ljava/lang/String;
HSPLjava/io/BufferedReader;->readLine(Z)Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/io/BufferedWriter;-><init>(Ljava/io/Writer;)V
HSPLjava/io/BufferedWriter;-><init>(Ljava/io/Writer;I)V
HSPLjava/io/BufferedWriter;->close()V
HSPLjava/io/BufferedWriter;->ensureOpen()V
-HSPLjava/io/BufferedWriter;->flush()V
+HSPLjava/io/BufferedWriter;->flush()V+]Ljava/io/Writer;Ljava/io/OutputStreamWriter;]Ljava/io/BufferedWriter;Ljava/io/BufferedWriter;
HSPLjava/io/BufferedWriter;->flushBuffer()V
HSPLjava/io/BufferedWriter;->min(II)I
HSPLjava/io/BufferedWriter;->newLine()V
HSPLjava/io/BufferedWriter;->write(I)V
-HSPLjava/io/BufferedWriter;->write(Ljava/lang/String;II)V
+HSPLjava/io/BufferedWriter;->write(Ljava/lang/String;II)V+]Ljava/lang/String;Ljava/lang/String;]Ljava/io/BufferedWriter;Ljava/io/BufferedWriter;
HSPLjava/io/BufferedWriter;->write([CII)V
HSPLjava/io/ByteArrayInputStream;-><init>([B)V
HSPLjava/io/ByteArrayInputStream;-><init>([BII)V
@@ -1283,27 +1287,27 @@
HSPLjava/io/DataInputStream;->read([B)I
HSPLjava/io/DataInputStream;->read([BII)I
HSPLjava/io/DataInputStream;->readBoolean()Z
-HSPLjava/io/DataInputStream;->readByte()B+]Ljava/io/InputStream;Ljava/io/ByteArrayInputStream;
+HSPLjava/io/DataInputStream;->readByte()B
HSPLjava/io/DataInputStream;->readFully([B)V
-HSPLjava/io/DataInputStream;->readFully([BII)V+]Ljava/io/InputStream;missing_types
+HSPLjava/io/DataInputStream;->readFully([BII)V
HSPLjava/io/DataInputStream;->readInt()I
HSPLjava/io/DataInputStream;->readLong()J
-HSPLjava/io/DataInputStream;->readShort()S+]Ljava/io/DataInputStream;Ljava/io/DataInputStream;
+HSPLjava/io/DataInputStream;->readShort()S
HSPLjava/io/DataInputStream;->readUTF()Ljava/lang/String;
-HSPLjava/io/DataInputStream;->readUTF(Ljava/io/DataInput;)Ljava/lang/String;+]Ljava/io/DataInput;Ljava/io/DataInputStream;
-HSPLjava/io/DataInputStream;->readUnsignedByte()I+]Ljava/io/InputStream;Ljava/io/ByteArrayInputStream;
-HSPLjava/io/DataInputStream;->readUnsignedShort()I+]Ljava/io/DataInputStream;Ljava/io/DataInputStream;
+HSPLjava/io/DataInputStream;->readUTF(Ljava/io/DataInput;)Ljava/lang/String;
+HSPLjava/io/DataInputStream;->readUnsignedByte()I
+HSPLjava/io/DataInputStream;->readUnsignedShort()I
HSPLjava/io/DataInputStream;->skipBytes(I)I
HSPLjava/io/DataOutputStream;-><init>(Ljava/io/OutputStream;)V
HSPLjava/io/DataOutputStream;->flush()V
HSPLjava/io/DataOutputStream;->incCount(I)V
-HSPLjava/io/DataOutputStream;->write(I)V+]Ljava/io/OutputStream;Ljava/io/ByteArrayOutputStream;
+HSPLjava/io/DataOutputStream;->write(I)V
HSPLjava/io/DataOutputStream;->write([BII)V
HSPLjava/io/DataOutputStream;->writeBoolean(Z)V
HSPLjava/io/DataOutputStream;->writeByte(I)V
-HSPLjava/io/DataOutputStream;->writeInt(I)V+]Ljava/io/OutputStream;Ljava/io/ByteArrayOutputStream;
+HSPLjava/io/DataOutputStream;->writeInt(I)V
HSPLjava/io/DataOutputStream;->writeLong(J)V
-HSPLjava/io/DataOutputStream;->writeShort(I)V+]Ljava/io/OutputStream;Ljava/io/ByteArrayOutputStream;
+HSPLjava/io/DataOutputStream;->writeShort(I)V
HSPLjava/io/DataOutputStream;->writeUTF(Ljava/lang/String;)V
HSPLjava/io/DataOutputStream;->writeUTF(Ljava/lang/String;Ljava/io/DataOutput;)I
HSPLjava/io/EOFException;-><init>()V
@@ -1311,9 +1315,9 @@
HSPLjava/io/ExpiringCache;->clear()V
HSPLjava/io/File$TempDirectory;->generateFile(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;
HSPLjava/io/File;-><init>(Ljava/io/File;Ljava/lang/String;)V+]Ljava/io/FileSystem;Ljava/io/UnixFileSystem;
-HSPLjava/io/File;-><init>(Ljava/lang/String;)V
+HSPLjava/io/File;-><init>(Ljava/lang/String;)V+]Ljava/io/FileSystem;Ljava/io/UnixFileSystem;
HSPLjava/io/File;-><init>(Ljava/lang/String;I)V
-HSPLjava/io/File;-><init>(Ljava/lang/String;Ljava/io/File;)V+]Ljava/io/FileSystem;Ljava/io/UnixFileSystem;
+HSPLjava/io/File;-><init>(Ljava/lang/String;Ljava/io/File;)V
HSPLjava/io/File;-><init>(Ljava/lang/String;Ljava/lang/String;)V
HSPLjava/io/File;->canExecute()Z
HSPLjava/io/File;->canRead()Z
@@ -1324,7 +1328,7 @@
HSPLjava/io/File;->createTempFile(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;
HSPLjava/io/File;->delete()Z
HSPLjava/io/File;->equals(Ljava/lang/Object;)Z+]Ljava/io/File;Ljava/io/File;
-HSPLjava/io/File;->exists()Z
+HSPLjava/io/File;->exists()Z+]Ljava/io/File;Ljava/io/File;]Ljava/io/FileSystem;Ljava/io/UnixFileSystem;
HSPLjava/io/File;->getAbsoluteFile()Ljava/io/File;
HSPLjava/io/File;->getAbsolutePath()Ljava/lang/String;
HSPLjava/io/File;->getCanonicalFile()Ljava/io/File;
@@ -1350,7 +1354,7 @@
HSPLjava/io/File;->listFiles(Ljava/io/FileFilter;)[Ljava/io/File;
HSPLjava/io/File;->listFiles(Ljava/io/FilenameFilter;)[Ljava/io/File;
HSPLjava/io/File;->mkdir()Z
-HSPLjava/io/File;->mkdirs()Z
+HSPLjava/io/File;->mkdirs()Z+]Ljava/io/File;Ljava/io/File;
HSPLjava/io/File;->renameTo(Ljava/io/File;)Z
HSPLjava/io/File;->setExecutable(Z)Z
HSPLjava/io/File;->setExecutable(ZZ)Z
@@ -1373,7 +1377,7 @@
HSPLjava/io/FileDescriptor;->setInt$(I)V
HSPLjava/io/FileDescriptor;->setOwnerId$(J)V
HSPLjava/io/FileDescriptor;->valid()Z
-HSPLjava/io/FileInputStream;-><init>(Ljava/io/File;)V
+HSPLjava/io/FileInputStream;-><init>(Ljava/io/File;)V+]Ljava/io/File;Ljava/io/File;]Ldalvik/system/CloseGuard;Ldalvik/system/CloseGuard;
HSPLjava/io/FileInputStream;-><init>(Ljava/io/FileDescriptor;)V
HSPLjava/io/FileInputStream;-><init>(Ljava/io/FileDescriptor;Z)V
HSPLjava/io/FileInputStream;-><init>(Ljava/lang/String;)V
@@ -1383,12 +1387,12 @@
HSPLjava/io/FileInputStream;->getChannel()Ljava/nio/channels/FileChannel;
HSPLjava/io/FileInputStream;->getFD()Ljava/io/FileDescriptor;
HSPLjava/io/FileInputStream;->read()I
-HSPLjava/io/FileInputStream;->read([B)I+]Ljava/io/FileInputStream;Ljava/io/FileInputStream;,Landroid/os/ParcelFileDescriptor$AutoCloseInputStream;
+HSPLjava/io/FileInputStream;->read([B)I+]Ljava/io/FileInputStream;Ljava/io/FileInputStream;
HSPLjava/io/FileInputStream;->read([BII)I+]Llibcore/io/IoTracker;Llibcore/io/IoTracker;
-HSPLjava/io/FileInputStream;->skip(J)J+]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;
+HSPLjava/io/FileInputStream;->skip(J)J
HSPLjava/io/FileNotFoundException;-><init>(Ljava/lang/String;)V
HSPLjava/io/FileOutputStream;-><init>(Ljava/io/File;)V
-HSPLjava/io/FileOutputStream;-><init>(Ljava/io/File;Z)V+]Ljava/io/File;Ljava/io/File;]Ldalvik/system/CloseGuard;Ldalvik/system/CloseGuard;
+HSPLjava/io/FileOutputStream;-><init>(Ljava/io/File;Z)V
HSPLjava/io/FileOutputStream;-><init>(Ljava/io/FileDescriptor;)V
HSPLjava/io/FileOutputStream;-><init>(Ljava/io/FileDescriptor;Z)V
HSPLjava/io/FileOutputStream;-><init>(Ljava/lang/String;)V
@@ -1399,7 +1403,7 @@
HSPLjava/io/FileOutputStream;->getFD()Ljava/io/FileDescriptor;
HSPLjava/io/FileOutputStream;->write(I)V
HSPLjava/io/FileOutputStream;->write([B)V
-HSPLjava/io/FileOutputStream;->write([BII)V+]Llibcore/io/IoTracker;Llibcore/io/IoTracker;
+HSPLjava/io/FileOutputStream;->write([BII)V
HSPLjava/io/FileReader;-><init>(Ljava/io/File;)V
HSPLjava/io/FileReader;-><init>(Ljava/lang/String;)V
HSPLjava/io/FileWriter;-><init>(Ljava/io/File;)V
@@ -1409,9 +1413,9 @@
HSPLjava/io/FilterInputStream;->close()V
HSPLjava/io/FilterInputStream;->mark(I)V
HSPLjava/io/FilterInputStream;->markSupported()Z
-HSPLjava/io/FilterInputStream;->read()I+]Ljava/io/InputStream;Ljava/io/BufferedInputStream;,Ljava/io/ByteArrayInputStream;,Ljava/util/jar/JarVerifier$VerifierStream;,Ljava/io/PushbackInputStream;,Ljava/util/zip/InflaterInputStream;
-HSPLjava/io/FilterInputStream;->read([B)I+]Ljava/io/FilterInputStream;Ljava/security/DigestInputStream;,Ljava/util/zip/InflaterInputStream;
-HSPLjava/io/FilterInputStream;->read([BII)I+]Ljava/io/InputStream;Ljava/io/BufferedInputStream;
+HSPLjava/io/FilterInputStream;->read()I+]Ljava/io/InputStream;Ljava/io/BufferedInputStream;,Ljava/io/ByteArrayInputStream;,Ljava/io/PushbackInputStream;,Landroid/content/res/AssetManager$AssetInputStream;
+HSPLjava/io/FilterInputStream;->read([B)I
+HSPLjava/io/FilterInputStream;->read([BII)I+]Ljava/io/InputStream;Ljava/io/BufferedInputStream;,Ljava/io/FileInputStream;,Ljava/io/ByteArrayInputStream;,Ljava/io/PushbackInputStream;
HSPLjava/io/FilterInputStream;->reset()V
HSPLjava/io/FilterInputStream;->skip(J)J
HSPLjava/io/FilterOutputStream;-><init>(Ljava/io/OutputStream;)V
@@ -1444,35 +1448,36 @@
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->close()V+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->currentBlockRemaining()I
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->getBlockDataMode()Z
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->peek()I+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->peekByte()B+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->read()I+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->peek()I
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->peekByte()B
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->read()I
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->read([BII)I
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->read([BIIZ)I+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readBlockHeader(Z)I+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readBlockHeader(Z)I
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readBoolean()Z
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readByte()B+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readByte()B
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readFloat()F
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readFully([BIIZ)V
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readInt()I+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;]Ljava/io/DataInputStream;Ljava/io/DataInputStream;
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readLong()J+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readInt()I
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readLong()J
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readShort()S+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readUTF()Ljava/lang/String;
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readUTFBody(J)Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readUTFChar(Ljava/lang/StringBuilder;J)I
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readUTFSpan(Ljava/lang/StringBuilder;J)J+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
-HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readUnsignedShort()I+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
+HSPLjava/io/ObjectInputStream$BlockDataInputStream;->readUnsignedShort()I
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->refill()V+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->setBlockDataMode(Z)Z
HSPLjava/io/ObjectInputStream$BlockDataInputStream;->skipBlockData()V
HSPLjava/io/ObjectInputStream$GetField;-><init>()V
-HSPLjava/io/ObjectInputStream$GetFieldImpl;-><init>(Ljava/io/ObjectInputStream;Ljava/io/ObjectStreamClass;)V+]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;
+HSPLjava/io/ObjectInputStream$GetFieldImpl;-><init>(Ljava/io/ObjectInputStream;Ljava/io/ObjectStreamClass;)V
+HSPLjava/io/ObjectInputStream$GetFieldImpl;->get(Ljava/lang/String;D)D
HSPLjava/io/ObjectInputStream$GetFieldImpl;->get(Ljava/lang/String;I)I
HSPLjava/io/ObjectInputStream$GetFieldImpl;->get(Ljava/lang/String;J)J
-HSPLjava/io/ObjectInputStream$GetFieldImpl;->get(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/io/ObjectInputStream$HandleTable;Ljava/io/ObjectInputStream$HandleTable;
+HSPLjava/io/ObjectInputStream$GetFieldImpl;->get(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/io/ObjectInputStream$GetFieldImpl;->get(Ljava/lang/String;Z)Z
-HSPLjava/io/ObjectInputStream$GetFieldImpl;->getFieldOffset(Ljava/lang/String;Ljava/lang/Class;)I+]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamField;
-HSPLjava/io/ObjectInputStream$GetFieldImpl;->readFields()V+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamField;
+HSPLjava/io/ObjectInputStream$GetFieldImpl;->getFieldOffset(Ljava/lang/String;Ljava/lang/Class;)I
+HSPLjava/io/ObjectInputStream$GetFieldImpl;->readFields()V
HSPLjava/io/ObjectInputStream$HandleTable$HandleList;-><init>()V
HSPLjava/io/ObjectInputStream$HandleTable$HandleList;->add(I)V
HSPLjava/io/ObjectInputStream$HandleTable;-><init>(I)V
@@ -1488,16 +1493,16 @@
HSPLjava/io/ObjectInputStream$PeekInputStream;-><init>(Ljava/io/InputStream;)V
HSPLjava/io/ObjectInputStream$PeekInputStream;->close()V
HSPLjava/io/ObjectInputStream$PeekInputStream;->peek()I+]Ljava/io/InputStream;Ljava/io/ByteArrayInputStream;
-HSPLjava/io/ObjectInputStream$PeekInputStream;->read()I+]Ljava/io/InputStream;Ljava/io/ByteArrayInputStream;
+HSPLjava/io/ObjectInputStream$PeekInputStream;->read()I
HSPLjava/io/ObjectInputStream$PeekInputStream;->read([BII)I+]Ljava/io/InputStream;Ljava/io/ByteArrayInputStream;
-HSPLjava/io/ObjectInputStream$PeekInputStream;->readFully([BII)V+]Ljava/io/ObjectInputStream$PeekInputStream;Ljava/io/ObjectInputStream$PeekInputStream;
+HSPLjava/io/ObjectInputStream$PeekInputStream;->readFully([BII)V
HSPLjava/io/ObjectInputStream$ValidationList;-><init>()V
HSPLjava/io/ObjectInputStream$ValidationList;->clear()V
HSPLjava/io/ObjectInputStream$ValidationList;->doCallbacks()V
-HSPLjava/io/ObjectInputStream;-><init>(Ljava/io/InputStream;)V+]Ljava/io/ObjectInputStream;Ljava/io/ObjectInputStream;,Landroid/os/Parcel$2;]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
+HSPLjava/io/ObjectInputStream;-><init>(Ljava/io/InputStream;)V+]Ljava/io/ObjectInputStream;Ljava/io/ObjectInputStream;]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
HSPLjava/io/ObjectInputStream;->checkResolve(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/io/ObjectInputStream;->clear()V
-HSPLjava/io/ObjectInputStream;->close()V+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
+HSPLjava/io/ObjectInputStream;->close()V
HSPLjava/io/ObjectInputStream;->defaultReadFields(Ljava/lang/Object;Ljava/io/ObjectStreamClass;)V
HSPLjava/io/ObjectInputStream;->defaultReadObject()V
HSPLjava/io/ObjectInputStream;->isCustomSubclass()Z
@@ -1505,45 +1510,45 @@
HSPLjava/io/ObjectInputStream;->readArray(Z)Ljava/lang/Object;
HSPLjava/io/ObjectInputStream;->readBoolean()Z
HSPLjava/io/ObjectInputStream;->readByte()B
-HSPLjava/io/ObjectInputStream;->readClassDesc(Z)Ljava/io/ObjectStreamClass;+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
+HSPLjava/io/ObjectInputStream;->readClassDesc(Z)Ljava/io/ObjectStreamClass;
HSPLjava/io/ObjectInputStream;->readClassDescriptor()Ljava/io/ObjectStreamClass;
HSPLjava/io/ObjectInputStream;->readEnum(Z)Ljava/lang/Enum;
-HSPLjava/io/ObjectInputStream;->readFields()Ljava/io/ObjectInputStream$GetField;+]Ljava/io/ObjectInputStream$GetFieldImpl;Ljava/io/ObjectInputStream$GetFieldImpl;]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/SerialCallbackContext;Ljava/io/SerialCallbackContext;
+HSPLjava/io/ObjectInputStream;->readFields()Ljava/io/ObjectInputStream$GetField;
HSPLjava/io/ObjectInputStream;->readFloat()F
-HSPLjava/io/ObjectInputStream;->readHandle(Z)Ljava/lang/Object;+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;]Ljava/io/ObjectInputStream$HandleTable;Ljava/io/ObjectInputStream$HandleTable;
+HSPLjava/io/ObjectInputStream;->readHandle(Z)Ljava/lang/Object;
HSPLjava/io/ObjectInputStream;->readInt()I
HSPLjava/io/ObjectInputStream;->readLong()J
-HSPLjava/io/ObjectInputStream;->readNonProxyDesc(Z)Ljava/io/ObjectStreamClass;+]Ljava/io/ObjectInputStream;Landroid/os/Parcel$2;]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/ObjectInputStream$HandleTable;Ljava/io/ObjectInputStream$HandleTable;
+HSPLjava/io/ObjectInputStream;->readNonProxyDesc(Z)Ljava/io/ObjectStreamClass;
HSPLjava/io/ObjectInputStream;->readNull()Ljava/lang/Object;
-HSPLjava/io/ObjectInputStream;->readObject()Ljava/lang/Object;+]Ljava/io/ObjectInputStream$ValidationList;Ljava/io/ObjectInputStream$ValidationList;]Ljava/io/ObjectInputStream$HandleTable;Ljava/io/ObjectInputStream$HandleTable;
+HSPLjava/io/ObjectInputStream;->readObject()Ljava/lang/Object;
HSPLjava/io/ObjectInputStream;->readObject0(Z)Ljava/lang/Object;+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
-HSPLjava/io/ObjectInputStream;->readOrdinaryObject(Z)Ljava/lang/Object;+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/ObjectInputStream$HandleTable;Ljava/io/ObjectInputStream$HandleTable;
-HSPLjava/io/ObjectInputStream;->readSerialData(Ljava/lang/Object;Ljava/io/ObjectStreamClass;)V+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/ObjectInputStream$HandleTable;Ljava/io/ObjectInputStream$HandleTable;]Ljava/io/SerialCallbackContext;Ljava/io/SerialCallbackContext;
+HSPLjava/io/ObjectInputStream;->readOrdinaryObject(Z)Ljava/lang/Object;
+HSPLjava/io/ObjectInputStream;->readSerialData(Ljava/lang/Object;Ljava/io/ObjectStreamClass;)V
HSPLjava/io/ObjectInputStream;->readShort()S
-HSPLjava/io/ObjectInputStream;->readStreamHeader()V+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
-HSPLjava/io/ObjectInputStream;->readString(Z)Ljava/lang/String;+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;]Ljava/io/ObjectInputStream$HandleTable;Ljava/io/ObjectInputStream$HandleTable;
-HSPLjava/io/ObjectInputStream;->readTypeString()Ljava/lang/String;+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
+HSPLjava/io/ObjectInputStream;->readStreamHeader()V
+HSPLjava/io/ObjectInputStream;->readString(Z)Ljava/lang/String;
+HSPLjava/io/ObjectInputStream;->readTypeString()Ljava/lang/String;
HSPLjava/io/ObjectInputStream;->readUTF()Ljava/lang/String;
HSPLjava/io/ObjectInputStream;->resolveClass(Ljava/io/ObjectStreamClass;)Ljava/lang/Class;
-HSPLjava/io/ObjectInputStream;->skipCustomData()V+]Ljava/io/ObjectInputStream$BlockDataInputStream;Ljava/io/ObjectInputStream$BlockDataInputStream;
+HSPLjava/io/ObjectInputStream;->skipCustomData()V
HSPLjava/io/ObjectInputStream;->verifySubclass()V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;-><init>(Ljava/io/OutputStream;)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->close()V
-HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->drain()V+]Ljava/io/OutputStream;Ljava/io/ByteArrayOutputStream;
+HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->drain()V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->flush()V
-HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->getUTFLength(Ljava/lang/String;)J+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->getUTFLength(Ljava/lang/String;)J
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->setBlockDataMode(Z)Z
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->warnIfClosed()V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->write([BIIZ)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeBlockHeader(I)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeByte(I)V
-HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeBytes(Ljava/lang/String;)V+]Ljava/lang/String;Ljava/lang/String;]Ljava/io/ObjectOutputStream$BlockDataOutputStream;Ljava/io/ObjectOutputStream$BlockDataOutputStream;
+HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeBytes(Ljava/lang/String;)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeFloat(F)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeInt(I)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeLong(J)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeShort(I)V
HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeUTF(Ljava/lang/String;)V
-HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeUTF(Ljava/lang/String;J)V+]Ljava/io/ObjectOutputStream$BlockDataOutputStream;Ljava/io/ObjectOutputStream$BlockDataOutputStream;
+HSPLjava/io/ObjectOutputStream$BlockDataOutputStream;->writeUTF(Ljava/lang/String;J)V
HSPLjava/io/ObjectOutputStream$HandleTable;-><init>(IF)V
HSPLjava/io/ObjectOutputStream$HandleTable;->assign(Ljava/lang/Object;)I
HSPLjava/io/ObjectOutputStream$HandleTable;->clear()V
@@ -1586,12 +1591,12 @@
HSPLjava/io/ObjectOutputStream;->writeNonProxyDesc(Ljava/io/ObjectStreamClass;Z)V
HSPLjava/io/ObjectOutputStream;->writeNull()V
HSPLjava/io/ObjectOutputStream;->writeObject(Ljava/lang/Object;)V
-HSPLjava/io/ObjectOutputStream;->writeObject0(Ljava/lang/Object;Z)V+]Ljava/io/ObjectOutputStream$ReplaceTable;Ljava/io/ObjectOutputStream$ReplaceTable;]Ljava/lang/Object;megamorphic_types]Ljava/io/ObjectOutputStream$HandleTable;Ljava/io/ObjectOutputStream$HandleTable;]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/ObjectOutputStream$BlockDataOutputStream;Ljava/io/ObjectOutputStream$BlockDataOutputStream;]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/io/ObjectOutputStream;->writeObject0(Ljava/lang/Object;Z)V
HSPLjava/io/ObjectOutputStream;->writeOrdinaryObject(Ljava/lang/Object;Ljava/io/ObjectStreamClass;Z)V+]Ljava/io/ObjectOutputStream$HandleTable;Ljava/io/ObjectOutputStream$HandleTable;]Ljava/io/ObjectStreamClass;Ljava/io/ObjectStreamClass;]Ljava/io/ObjectOutputStream$BlockDataOutputStream;Ljava/io/ObjectOutputStream$BlockDataOutputStream;
HSPLjava/io/ObjectOutputStream;->writeSerialData(Ljava/lang/Object;Ljava/io/ObjectStreamClass;)V
HSPLjava/io/ObjectOutputStream;->writeShort(I)V
HSPLjava/io/ObjectOutputStream;->writeStreamHeader()V
-HSPLjava/io/ObjectOutputStream;->writeString(Ljava/lang/String;Z)V+]Ljava/io/ObjectOutputStream$HandleTable;Ljava/io/ObjectOutputStream$HandleTable;]Ljava/io/ObjectOutputStream$BlockDataOutputStream;Ljava/io/ObjectOutputStream$BlockDataOutputStream;
+HSPLjava/io/ObjectOutputStream;->writeString(Ljava/lang/String;Z)V
HSPLjava/io/ObjectOutputStream;->writeTypeString(Ljava/lang/String;)V
HSPLjava/io/ObjectOutputStream;->writeUTF(Ljava/lang/String;)V
HSPLjava/io/ObjectStreamClass$1;-><init>(Ljava/io/ObjectStreamClass;)V
@@ -1624,8 +1629,8 @@
HSPLjava/io/ObjectStreamClass$FieldReflector;->getPrimFieldValues(Ljava/lang/Object;[B)V
HSPLjava/io/ObjectStreamClass$FieldReflector;->setObjFieldValues(Ljava/lang/Object;[Ljava/lang/Object;)V
HSPLjava/io/ObjectStreamClass$FieldReflector;->setPrimFieldValues(Ljava/lang/Object;[B)V
-HSPLjava/io/ObjectStreamClass$FieldReflectorKey;-><init>(Ljava/lang/Class;[Ljava/io/ObjectStreamField;Ljava/lang/ref/ReferenceQueue;)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/String;Ljava/lang/String;]Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamField;
-HSPLjava/io/ObjectStreamClass$FieldReflectorKey;->equals(Ljava/lang/Object;)Z+]Ljava/io/ObjectStreamClass$FieldReflectorKey;Ljava/io/ObjectStreamClass$FieldReflectorKey;
+HSPLjava/io/ObjectStreamClass$FieldReflectorKey;-><init>(Ljava/lang/Class;[Ljava/io/ObjectStreamField;Ljava/lang/ref/ReferenceQueue;)V
+HSPLjava/io/ObjectStreamClass$FieldReflectorKey;->equals(Ljava/lang/Object;)Z
HSPLjava/io/ObjectStreamClass$FieldReflectorKey;->hashCode()I
HSPLjava/io/ObjectStreamClass$MemberSignature;-><init>(Ljava/lang/reflect/Constructor;)V
HSPLjava/io/ObjectStreamClass$MemberSignature;-><init>(Ljava/lang/reflect/Field;)V
@@ -1658,15 +1663,15 @@
HSPLjava/io/ObjectStreamClass;->checkSerialize()V
HSPLjava/io/ObjectStreamClass;->classNamesEqual(Ljava/lang/String;Ljava/lang/String;)Z
HSPLjava/io/ObjectStreamClass;->computeDefaultSUID(Ljava/lang/Class;)J
-HSPLjava/io/ObjectStreamClass;->computeFieldOffsets()V+]Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamField;
+HSPLjava/io/ObjectStreamClass;->computeFieldOffsets()V
HSPLjava/io/ObjectStreamClass;->forClass()Ljava/lang/Class;
HSPLjava/io/ObjectStreamClass;->getClassDataLayout()[Ljava/io/ObjectStreamClass$ClassDataSlot;
-HSPLjava/io/ObjectStreamClass;->getClassDataLayout0()[Ljava/io/ObjectStreamClass$ClassDataSlot;+]Ljava/util/HashSet;Ljava/util/HashSet;]Ljava/lang/Class;Ljava/lang/Class;]Ljava/util/ArrayList;Ljava/util/ArrayList;
+HSPLjava/io/ObjectStreamClass;->getClassDataLayout0()[Ljava/io/ObjectStreamClass$ClassDataSlot;
HSPLjava/io/ObjectStreamClass;->getClassSignature(Ljava/lang/Class;)Ljava/lang/String;
HSPLjava/io/ObjectStreamClass;->getDeclaredSUID(Ljava/lang/Class;)Ljava/lang/Long;
HSPLjava/io/ObjectStreamClass;->getDeclaredSerialFields(Ljava/lang/Class;)[Ljava/io/ObjectStreamField;
HSPLjava/io/ObjectStreamClass;->getDefaultSerialFields(Ljava/lang/Class;)[Ljava/io/ObjectStreamField;
-HSPLjava/io/ObjectStreamClass;->getField(Ljava/lang/String;Ljava/lang/Class;)Ljava/io/ObjectStreamField;+]Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamField;]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/io/ObjectStreamClass;->getField(Ljava/lang/String;Ljava/lang/Class;)Ljava/io/ObjectStreamField;
HSPLjava/io/ObjectStreamClass;->getFields(Z)[Ljava/io/ObjectStreamField;
HSPLjava/io/ObjectStreamClass;->getInheritableMethod(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/reflect/Method;
HSPLjava/io/ObjectStreamClass;->getMethodSignature([Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/String;
@@ -1677,7 +1682,7 @@
HSPLjava/io/ObjectStreamClass;->getPrimDataSize()I
HSPLjava/io/ObjectStreamClass;->getPrimFieldValues(Ljava/lang/Object;[B)V
HSPLjava/io/ObjectStreamClass;->getPrivateMethod(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/reflect/Method;
-HSPLjava/io/ObjectStreamClass;->getReflector([Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamClass;)Ljava/io/ObjectStreamClass$FieldReflector;+]Ljava/lang/ref/Reference;Ljava/lang/ref/SoftReference;]Ljava/util/concurrent/ConcurrentMap;Ljava/util/concurrent/ConcurrentHashMap;]Ljava/io/ObjectStreamClass$EntryFuture;Ljava/io/ObjectStreamClass$EntryFuture;
+HSPLjava/io/ObjectStreamClass;->getReflector([Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamClass;)Ljava/io/ObjectStreamClass$FieldReflector;
HSPLjava/io/ObjectStreamClass;->getResolveException()Ljava/lang/ClassNotFoundException;
HSPLjava/io/ObjectStreamClass;->getSerialFields(Ljava/lang/Class;)[Ljava/io/ObjectStreamField;
HSPLjava/io/ObjectStreamClass;->getSerialVersionUID()J
@@ -1698,19 +1703,19 @@
HSPLjava/io/ObjectStreamClass;->isExternalizable()Z
HSPLjava/io/ObjectStreamClass;->isInstantiable()Z
HSPLjava/io/ObjectStreamClass;->isProxy()Z
-HSPLjava/io/ObjectStreamClass;->lookup(Ljava/lang/Class;Z)Ljava/io/ObjectStreamClass;+]Ljava/lang/ref/Reference;Ljava/lang/ref/SoftReference;]Ljava/util/concurrent/ConcurrentMap;Ljava/util/concurrent/ConcurrentHashMap;]Ljava/io/ObjectStreamClass$EntryFuture;Ljava/io/ObjectStreamClass$EntryFuture;]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/io/ObjectStreamClass;->lookup(Ljava/lang/Class;Z)Ljava/io/ObjectStreamClass;
HSPLjava/io/ObjectStreamClass;->matchFields([Ljava/io/ObjectStreamField;Ljava/io/ObjectStreamClass;)[Ljava/io/ObjectStreamField;
HSPLjava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object;
HSPLjava/io/ObjectStreamClass;->packageEquals(Ljava/lang/Class;Ljava/lang/Class;)Z
HSPLjava/io/ObjectStreamClass;->processQueue(Ljava/lang/ref/ReferenceQueue;Ljava/util/concurrent/ConcurrentMap;)V
-HSPLjava/io/ObjectStreamClass;->readNonProxy(Ljava/io/ObjectInputStream;)V+]Ljava/io/ObjectInputStream;Landroid/os/Parcel$2;
+HSPLjava/io/ObjectStreamClass;->readNonProxy(Ljava/io/ObjectInputStream;)V
HSPLjava/io/ObjectStreamClass;->requireInitialized()V
HSPLjava/io/ObjectStreamClass;->setObjFieldValues(Ljava/lang/Object;[Ljava/lang/Object;)V
HSPLjava/io/ObjectStreamClass;->setPrimFieldValues(Ljava/lang/Object;[B)V
HSPLjava/io/ObjectStreamClass;->writeNonProxy(Ljava/io/ObjectOutputStream;)V
HSPLjava/io/ObjectStreamField;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
HSPLjava/io/ObjectStreamField;-><init>(Ljava/lang/String;Ljava/lang/Class;Z)V
-HSPLjava/io/ObjectStreamField;-><init>(Ljava/lang/String;Ljava/lang/String;Z)V+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/io/ObjectStreamField;-><init>(Ljava/lang/String;Ljava/lang/String;Z)V
HSPLjava/io/ObjectStreamField;-><init>(Ljava/lang/reflect/Field;ZZ)V
HSPLjava/io/ObjectStreamField;->compareTo(Ljava/lang/Object;)I
HSPLjava/io/ObjectStreamField;->getClassSignature(Ljava/lang/Class;)Ljava/lang/String;
@@ -1753,20 +1758,20 @@
HSPLjava/io/PrintWriter;->close()V
HSPLjava/io/PrintWriter;->ensureOpen()V
HSPLjava/io/PrintWriter;->flush()V
-HSPLjava/io/PrintWriter;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintWriter;
-HSPLjava/io/PrintWriter;->newLine()V+]Ljava/io/Writer;Ljava/io/StringWriter;
+HSPLjava/io/PrintWriter;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintWriter;+]Ljava/util/Formatter;Ljava/util/Formatter;
+HSPLjava/io/PrintWriter;->newLine()V
HSPLjava/io/PrintWriter;->print(C)V
HSPLjava/io/PrintWriter;->print(I)V
HSPLjava/io/PrintWriter;->print(J)V
-HSPLjava/io/PrintWriter;->print(Ljava/lang/String;)V
+HSPLjava/io/PrintWriter;->print(Ljava/lang/String;)V+]Ljava/io/PrintWriter;Ljava/io/PrintWriter;,Lcom/android/internal/util/LineBreakBufferedWriter;
HSPLjava/io/PrintWriter;->print(Z)V
HSPLjava/io/PrintWriter;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintWriter;
HSPLjava/io/PrintWriter;->println()V
HSPLjava/io/PrintWriter;->println(I)V
-HSPLjava/io/PrintWriter;->println(Ljava/lang/Object;)V
+HSPLjava/io/PrintWriter;->println(Ljava/lang/Object;)V+]Ljava/io/PrintWriter;Lcom/android/internal/util/FastPrintWriter;,Ljava/io/PrintWriter;,Lcom/android/internal/util/LineBreakBufferedWriter;
HSPLjava/io/PrintWriter;->println(Ljava/lang/String;)V
HSPLjava/io/PrintWriter;->write(I)V
-HSPLjava/io/PrintWriter;->write(Ljava/lang/String;)V
+HSPLjava/io/PrintWriter;->write(Ljava/lang/String;)V+]Ljava/io/PrintWriter;Ljava/io/PrintWriter;,Lcom/android/internal/util/LineBreakBufferedWriter;
HSPLjava/io/PrintWriter;->write(Ljava/lang/String;II)V
HSPLjava/io/PrintWriter;->write([CII)V
HSPLjava/io/PushbackInputStream;-><init>(Ljava/io/InputStream;I)V
@@ -1824,7 +1829,7 @@
HSPLjava/io/StringReader;->close()V
HSPLjava/io/StringReader;->ensureOpen()V
HSPLjava/io/StringReader;->read()I
-HSPLjava/io/StringReader;->read([CII)I+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/io/StringReader;->read([CII)I
HSPLjava/io/StringWriter;-><init>()V
HSPLjava/io/StringWriter;-><init>(I)V
HSPLjava/io/StringWriter;->append(C)Ljava/io/StringWriter;
@@ -1835,29 +1840,29 @@
HSPLjava/io/StringWriter;->flush()V
HSPLjava/io/StringWriter;->getBuffer()Ljava/lang/StringBuffer;
HSPLjava/io/StringWriter;->toString()Ljava/lang/String;
-HSPLjava/io/StringWriter;->write(I)V
-HSPLjava/io/StringWriter;->write(Ljava/lang/String;)V
+HSPLjava/io/StringWriter;->write(I)V+]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;
+HSPLjava/io/StringWriter;->write(Ljava/lang/String;)V+]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;
HSPLjava/io/StringWriter;->write(Ljava/lang/String;II)V
HSPLjava/io/StringWriter;->write([CII)V
HSPLjava/io/UnixFileSystem;->canonicalize(Ljava/lang/String;)Ljava/lang/String;
-HSPLjava/io/UnixFileSystem;->checkAccess(Ljava/io/File;I)Z
+HSPLjava/io/UnixFileSystem;->checkAccess(Ljava/io/File;I)Z+]Ljava/io/File;Ljava/io/File;]Llibcore/io/Os;Landroid/app/ActivityThread$AndroidOs;
HSPLjava/io/UnixFileSystem;->compare(Ljava/io/File;Ljava/io/File;)I+]Ljava/io/File;Ljava/io/File;
HSPLjava/io/UnixFileSystem;->createDirectory(Ljava/io/File;)Z
HSPLjava/io/UnixFileSystem;->createFileExclusively(Ljava/lang/String;)Z
HSPLjava/io/UnixFileSystem;->delete(Ljava/io/File;)Z
-HSPLjava/io/UnixFileSystem;->getBooleanAttributes(Ljava/io/File;)I+]Ljava/io/File;Ljava/io/File;]Ldalvik/system/BlockGuard$VmPolicy;Landroid/os/StrictMode$5;]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;,Landroid/os/StrictMode$AndroidBlockGuardPolicy;
+HSPLjava/io/UnixFileSystem;->getBooleanAttributes(Ljava/io/File;)I+]Ljava/io/File;Ljava/io/File;]Ldalvik/system/BlockGuard$VmPolicy;Landroid/os/StrictMode$5;,Ldalvik/system/BlockGuard$2;]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;,Landroid/os/StrictMode$AndroidBlockGuardPolicy;
HSPLjava/io/UnixFileSystem;->getDefaultParent()Ljava/lang/String;
-HSPLjava/io/UnixFileSystem;->getLastModifiedTime(Ljava/io/File;)J+]Ljava/io/File;Ljava/io/File;]Ldalvik/system/BlockGuard$VmPolicy;Landroid/os/StrictMode$5;]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;
-HSPLjava/io/UnixFileSystem;->getLength(Ljava/io/File;)J+]Ljava/io/File;Ljava/io/File;]Llibcore/io/Os;Landroid/app/ActivityThread$AndroidOs;
+HSPLjava/io/UnixFileSystem;->getLastModifiedTime(Ljava/io/File;)J
+HSPLjava/io/UnixFileSystem;->getLength(Ljava/io/File;)J
HSPLjava/io/UnixFileSystem;->getSpace(Ljava/io/File;I)J
HSPLjava/io/UnixFileSystem;->hashCode(Ljava/io/File;)I+]Ljava/lang/String;Ljava/lang/String;]Ljava/io/File;Ljava/io/File;
HSPLjava/io/UnixFileSystem;->isAbsolute(Ljava/io/File;)Z
-HSPLjava/io/UnixFileSystem;->list(Ljava/io/File;)[Ljava/lang/String;+]Ljava/io/File;Ljava/io/File;]Ldalvik/system/BlockGuard$VmPolicy;Ldalvik/system/BlockGuard$2;]Ldalvik/system/BlockGuard$Policy;Landroid/os/StrictMode$AndroidBlockGuardPolicy;,Ldalvik/system/BlockGuard$1;
+HSPLjava/io/UnixFileSystem;->list(Ljava/io/File;)[Ljava/lang/String;+]Ljava/io/File;Ljava/io/File;]Ldalvik/system/BlockGuard$VmPolicy;Ldalvik/system/BlockGuard$2;,Landroid/os/StrictMode$5;]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;
HSPLjava/io/UnixFileSystem;->normalize(Ljava/lang/String;)Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/io/UnixFileSystem;->prefixLength(Ljava/lang/String;)I
HSPLjava/io/UnixFileSystem;->rename(Ljava/io/File;Ljava/io/File;)Z
HSPLjava/io/UnixFileSystem;->resolve(Ljava/io/File;)Ljava/lang/String;
-HSPLjava/io/UnixFileSystem;->resolve(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+HSPLjava/io/UnixFileSystem;->resolve(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/io/UnixFileSystem;->setLastModifiedTime(Ljava/io/File;J)Z
HSPLjava/io/UnixFileSystem;->setPermission(Ljava/io/File;IZZ)Z
HSPLjava/io/Writer;-><init>()V
@@ -1866,39 +1871,48 @@
HSPLjava/io/Writer;->append(Ljava/lang/CharSequence;)Ljava/io/Writer;
HSPLjava/io/Writer;->write(Ljava/lang/String;)V
HSPLjava/lang/AbstractStringBuilder;-><init>(I)V
-HSPLjava/lang/AbstractStringBuilder;->append(C)Ljava/lang/AbstractStringBuilder;
+HSPLjava/lang/AbstractStringBuilder;->append(C)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;,Ljava/lang/StringBuffer;
HSPLjava/lang/AbstractStringBuilder;->append(D)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->append(F)Ljava/lang/AbstractStringBuilder;
-HSPLjava/lang/AbstractStringBuilder;->append(I)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;
+HSPLjava/lang/AbstractStringBuilder;->append(I)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;,Ljava/lang/StringBuffer;
HSPLjava/lang/AbstractStringBuilder;->append(J)Ljava/lang/AbstractStringBuilder;
-HSPLjava/lang/AbstractStringBuilder;->append(Ljava/lang/AbstractStringBuilder;)Ljava/lang/AbstractStringBuilder;
+HSPLjava/lang/AbstractStringBuilder;->append(Ljava/lang/AbstractStringBuilder;)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;,Ljava/lang/StringBuffer;
HSPLjava/lang/AbstractStringBuilder;->append(Ljava/lang/CharSequence;)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/CharSequence;Ljava/nio/HeapCharBuffer;,Landroid/icu/impl/FormattedStringBuilder;]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;,Ljava/lang/StringBuffer;
HSPLjava/lang/AbstractStringBuilder;->append(Ljava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/CharSequence;Ljava/lang/String;,Ljava/nio/HeapCharBuffer;,Landroid/icu/impl/FormattedStringBuilder;
-HSPLjava/lang/AbstractStringBuilder;->append(Ljava/lang/String;)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/lang/AbstractStringBuilder;->append(Ljava/lang/String;)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->append(Ljava/lang/StringBuffer;)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->append(Z)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->append([CII)Ljava/lang/AbstractStringBuilder;
+HSPLjava/lang/AbstractStringBuilder;->appendChars(Ljava/lang/CharSequence;II)V+]Ljava/lang/CharSequence;Ljava/lang/String;,Ljava/nio/HeapCharBuffer;,Landroid/icu/impl/FormattedStringBuilder;,Ljava/lang/StringBuilder;]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;,Ljava/lang/StringBuffer;
+HSPLjava/lang/AbstractStringBuilder;->appendChars([CII)V+]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;,Ljava/lang/StringBuffer;
HSPLjava/lang/AbstractStringBuilder;->appendCodePoint(I)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->appendNull()Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->charAt(I)C
+HSPLjava/lang/AbstractStringBuilder;->checkRange(III)V
+HSPLjava/lang/AbstractStringBuilder;->checkRangeSIOOBE(III)V
HSPLjava/lang/AbstractStringBuilder;->codePointAt(I)I
HSPLjava/lang/AbstractStringBuilder;->delete(II)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->deleteCharAt(I)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->ensureCapacity(I)V
HSPLjava/lang/AbstractStringBuilder;->ensureCapacityInternal(I)V
+HSPLjava/lang/AbstractStringBuilder;->getBytes([BIB)V
HSPLjava/lang/AbstractStringBuilder;->getChars(II[CI)V
+HSPLjava/lang/AbstractStringBuilder;->getCoder()B
HSPLjava/lang/AbstractStringBuilder;->indexOf(Ljava/lang/String;)I
HSPLjava/lang/AbstractStringBuilder;->indexOf(Ljava/lang/String;I)I
HSPLjava/lang/AbstractStringBuilder;->insert(IC)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->insert(II)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->insert(ILjava/lang/String;)Ljava/lang/AbstractStringBuilder;
+HSPLjava/lang/AbstractStringBuilder;->isLatin1()Z
HSPLjava/lang/AbstractStringBuilder;->lastIndexOf(Ljava/lang/String;I)I
HSPLjava/lang/AbstractStringBuilder;->length()I
HSPLjava/lang/AbstractStringBuilder;->newCapacity(I)I
+HSPLjava/lang/AbstractStringBuilder;->putStringAt(ILjava/lang/String;)V+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/AbstractStringBuilder;Ljava/lang/StringBuilder;,Ljava/lang/StringBuffer;
HSPLjava/lang/AbstractStringBuilder;->replace(IILjava/lang/String;)Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->reverse()Ljava/lang/AbstractStringBuilder;
HSPLjava/lang/AbstractStringBuilder;->setCharAt(IC)V
HSPLjava/lang/AbstractStringBuilder;->setLength(I)V
+HSPLjava/lang/AbstractStringBuilder;->shift(II)V
HSPLjava/lang/AbstractStringBuilder;->subSequence(II)Ljava/lang/CharSequence;
HSPLjava/lang/AbstractStringBuilder;->substring(I)Ljava/lang/String;
HSPLjava/lang/AbstractStringBuilder;->substring(II)Ljava/lang/String;
@@ -1944,7 +1958,7 @@
HSPLjava/lang/Character;-><init>(C)V
HSPLjava/lang/Character;->charCount(I)I
HSPLjava/lang/Character;->charValue()C
-HSPLjava/lang/Character;->codePointAt(Ljava/lang/CharSequence;I)I+]Ljava/lang/CharSequence;Landroid/text/method/ReplacementTransformationMethod$SpannedReplacementCharSequence;,Ljava/lang/StringBuilder;,Ljava/lang/String;,Landroid/text/SpannedString;
+HSPLjava/lang/Character;->codePointAt(Ljava/lang/CharSequence;I)I+]Ljava/lang/CharSequence;megamorphic_types
HSPLjava/lang/Character;->codePointAtImpl([CII)I
HSPLjava/lang/Character;->codePointBefore(Ljava/lang/CharSequence;I)I
HSPLjava/lang/Character;->codePointCount(Ljava/lang/CharSequence;II)I
@@ -1998,7 +2012,7 @@
HSPLjava/lang/Character;->toUpperCase(I)I
HSPLjava/lang/Character;->valueOf(C)Ljava/lang/Character;
HSPLjava/lang/Class;->asSubclass(Ljava/lang/Class;)Ljava/lang/Class;
-HSPLjava/lang/Class;->cast(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLjava/lang/Class;->cast(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/Class;->classNameImpliesTopLevel()Z+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/Class;->desiredAssertionStatus()Z
HSPLjava/lang/Class;->findInterfaceMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
@@ -2007,7 +2021,7 @@
HSPLjava/lang/Class;->getAccessFlags()I
HSPLjava/lang/Class;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;
HSPLjava/lang/Class;->getCanonicalName()Ljava/lang/String;
-HSPLjava/lang/Class;->getClassLoader()Ljava/lang/ClassLoader;+]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/lang/Class;->getClassLoader()Ljava/lang/ClassLoader;
HSPLjava/lang/Class;->getComponentType()Ljava/lang/Class;
HSPLjava/lang/Class;->getConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
HSPLjava/lang/Class;->getConstructor0([Ljava/lang/Class;I)Ljava/lang/reflect/Constructor;
@@ -2026,20 +2040,20 @@
HSPLjava/lang/Class;->getGenericSuperclass()Ljava/lang/reflect/Type;
HSPLjava/lang/Class;->getInterfaces()[Ljava/lang/Class;
HSPLjava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
-HSPLjava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/reflect/Method;
+HSPLjava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/reflect/Method;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/Class;->getMethods()[Ljava/lang/reflect/Method;
-HSPLjava/lang/Class;->getModifiers()I
+HSPLjava/lang/Class;->getModifiers()I+]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/Class;->getName()Ljava/lang/String;
HSPLjava/lang/Class;->getPackage()Ljava/lang/Package;
HSPLjava/lang/Class;->getPackageName()Ljava/lang/String;
HSPLjava/lang/Class;->getProtectionDomain()Ljava/security/ProtectionDomain;
HSPLjava/lang/Class;->getPublicFieldsRecursive(Ljava/util/List;)V
-HSPLjava/lang/Class;->getPublicMethodRecursive(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
+HSPLjava/lang/Class;->getPublicMethodRecursive(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;+]Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/Class;->getPublicMethodsInternal(Ljava/util/List;)V
HSPLjava/lang/Class;->getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;
HSPLjava/lang/Class;->getSignatureAttribute()Ljava/lang/String;
-HSPLjava/lang/Class;->getSimpleName()Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/Class;Ljava/lang/Class;
-HSPLjava/lang/Class;->getSuperclass()Ljava/lang/Class;+]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/lang/Class;->getSimpleName()Ljava/lang/String;
+HSPLjava/lang/Class;->getSuperclass()Ljava/lang/Class;
HSPLjava/lang/Class;->getTypeName()Ljava/lang/String;
HSPLjava/lang/Class;->getTypeParameters()[Ljava/lang/reflect/TypeVariable;
HSPLjava/lang/Class;->isAnnotation()Z
@@ -2051,7 +2065,7 @@
HSPLjava/lang/Class;->isInterface()Z
HSPLjava/lang/Class;->isLocalClass()Z+]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/Class;->isLocalOrAnonymousClass()Z
-HSPLjava/lang/Class;->isMemberClass()Z+]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/lang/Class;->isMemberClass()Z
HSPLjava/lang/Class;->isPrimitive()Z
HSPLjava/lang/Class;->isProxy()Z
HSPLjava/lang/Class;->resolveName(Ljava/lang/String;)Ljava/lang/String;
@@ -2089,13 +2103,13 @@
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->-$$Nest$mmonitoringNotNeeded(Ljava/lang/Daemons$FinalizerWatchdogDaemon;I)V
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->-$$Nest$sfgetINSTANCE()Ljava/lang/Daemons$FinalizerWatchdogDaemon;
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->isActive(I)Z
-HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->monitoringNeeded(I)V+]Ljava/lang/Object;Ljava/lang/Daemons$FinalizerWatchdogDaemon;
+HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->monitoringNeeded(I)V
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->monitoringNotNeeded(I)V
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->resetTimeouts()V
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->runInternal()V
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->sleepForNanos(J)Z
HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->sleepUntilNeeded()Z
-HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->waitForProgress()Ljava/util/concurrent/TimeoutException;+]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
+HSPLjava/lang/Daemons$FinalizerWatchdogDaemon;->waitForProgress()Ljava/util/concurrent/TimeoutException;
HSPLjava/lang/Daemons$HeapTaskDaemon;->interrupt(Ljava/lang/Thread;)V
HSPLjava/lang/Daemons$HeapTaskDaemon;->runInternal()V
HSPLjava/lang/Daemons$ReferenceQueueDaemon;->-$$Nest$fgetprogressCounter(Ljava/lang/Daemons$ReferenceQueueDaemon;)Ljava/util/concurrent/atomic/AtomicInteger;
@@ -2116,6 +2130,7 @@
HSPLjava/lang/Double;->hashCode(D)I
HSPLjava/lang/Double;->intValue()I
HSPLjava/lang/Double;->isInfinite(D)Z
+HSPLjava/lang/Double;->isNaN()Z
HSPLjava/lang/Double;->isNaN(D)Z
HSPLjava/lang/Double;->longValue()J
HSPLjava/lang/Double;->parseDouble(Ljava/lang/String;)D
@@ -2131,13 +2146,13 @@
HSPLjava/lang/Enum;->compareTo(Ljava/lang/Object;)I
HSPLjava/lang/Enum;->enumValues(Ljava/lang/Class;)[Ljava/lang/Object;
HSPLjava/lang/Enum;->equals(Ljava/lang/Object;)Z
-HSPLjava/lang/Enum;->getDeclaringClass()Ljava/lang/Class;+]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/lang/Enum;->getDeclaringClass()Ljava/lang/Class;
HSPLjava/lang/Enum;->getSharedConstants(Ljava/lang/Class;)[Ljava/lang/Enum;
HSPLjava/lang/Enum;->hashCode()I
HSPLjava/lang/Enum;->name()Ljava/lang/String;
HSPLjava/lang/Enum;->ordinal()I
HSPLjava/lang/Enum;->toString()Ljava/lang/String;
-HSPLjava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;+]Ljava/lang/Enum;Landroid/net/NetworkInfo$State;,Landroid/net/NetworkInfo$DetailedState;
+HSPLjava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;+]Ljava/lang/Enum;Landroid/net/wifi/SupplicantState;,Landroid/net/NetworkInfo$State;,Landroid/net/NetworkRequest$Type;,Landroid/net/NetworkInfo$DetailedState;
HSPLjava/lang/Error;-><init>(Ljava/lang/String;)V
HSPLjava/lang/Exception;-><init>()V
HSPLjava/lang/Exception;-><init>(Ljava/lang/String;)V
@@ -2180,7 +2195,7 @@
HSPLjava/lang/Integer;->byteValue()B
HSPLjava/lang/Integer;->compare(II)I
HSPLjava/lang/Integer;->compareTo(Ljava/lang/Integer;)I
-HSPLjava/lang/Integer;->compareTo(Ljava/lang/Object;)I
+HSPLjava/lang/Integer;->compareTo(Ljava/lang/Object;)I+]Ljava/lang/Integer;Ljava/lang/Integer;
HSPLjava/lang/Integer;->decode(Ljava/lang/String;)Ljava/lang/Integer;
HSPLjava/lang/Integer;->divideUnsigned(II)I
HSPLjava/lang/Integer;->doubleValue()D
@@ -2223,7 +2238,7 @@
HSPLjava/lang/Integer;->valueOf(Ljava/lang/String;)Ljava/lang/Integer;
HSPLjava/lang/Integer;->valueOf(Ljava/lang/String;I)Ljava/lang/Integer;
HSPLjava/lang/InterruptedException;-><init>()V
-HSPLjava/lang/Iterable;->forEach(Ljava/util/function/Consumer;)V+]Ljava/lang/Iterable;Ljava/util/HashSet;,Ljava/util/WeakHashMap$KeySet;]Ljava/util/Iterator;Ljava/util/HashMap$KeyIterator;,Ljava/util/WeakHashMap$KeyIterator;]Ljava/util/function/Consumer;missing_types
+HSPLjava/lang/Iterable;->forEach(Ljava/util/function/Consumer;)V+]Ljava/util/function/Consumer;Ljava/util/ArrayDeque$$ExternalSyntheticLambda1;]Ljava/util/Iterator;missing_types
HSPLjava/lang/LinkageError;-><init>(Ljava/lang/String;)V
HSPLjava/lang/Long;-><init>(J)V
HSPLjava/lang/Long;->bitCount(J)I
@@ -2248,6 +2263,7 @@
HSPLjava/lang/Long;->lowestOneBit(J)J
HSPLjava/lang/Long;->numberOfLeadingZeros(J)I
HSPLjava/lang/Long;->numberOfTrailingZeros(J)I
+HSPLjava/lang/Long;->parseLong(Ljava/lang/CharSequence;III)J+]Ljava/lang/CharSequence;Ljava/lang/String;
HSPLjava/lang/Long;->parseLong(Ljava/lang/String;)J
HSPLjava/lang/Long;->parseLong(Ljava/lang/String;I)J
HSPLjava/lang/Long;->reverse(J)J
@@ -2316,7 +2332,8 @@
HSPLjava/lang/NullPointerException;-><init>(Ljava/lang/String;)V
HSPLjava/lang/Number;-><init>()V
HSPLjava/lang/NumberFormatException;-><init>(Ljava/lang/String;)V
-HSPLjava/lang/NumberFormatException;->forInputString(Ljava/lang/String;)Ljava/lang/NumberFormatException;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLjava/lang/NumberFormatException;->forInputString(Ljava/lang/String;)Ljava/lang/NumberFormatException;
+HSPLjava/lang/NumberFormatException;->forInputString(Ljava/lang/String;I)Ljava/lang/NumberFormatException;
HSPLjava/lang/Object;-><init>()V
HSPLjava/lang/Object;->clone()Ljava/lang/Object;
HSPLjava/lang/Object;->equals(Ljava/lang/Object;)Z
@@ -2324,7 +2341,7 @@
HSPLjava/lang/Object;->getClass()Ljava/lang/Class;
HSPLjava/lang/Object;->hashCode()I
HSPLjava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
-HSPLjava/lang/Object;->toString()Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/Object;missing_types]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/lang/Object;->toString()Ljava/lang/String;
HSPLjava/lang/Object;->wait()V
HSPLjava/lang/Object;->wait(J)V
HSPLjava/lang/Package;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/net/URL;Ljava/lang/ClassLoader;)V
@@ -2382,10 +2399,13 @@
HSPLjava/lang/StackTraceElement;->toString()Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/StackTraceElement;Ljava/lang/StackTraceElement;
HSPLjava/lang/String$CaseInsensitiveComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
HSPLjava/lang/String$CaseInsensitiveComparator;->compare(Ljava/lang/String;Ljava/lang/String;)I
-HSPLjava/lang/String;->checkBoundsBeginEnd(III)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLjava/lang/String;->checkBoundsBeginEnd(III)V
+HSPLjava/lang/String;->checkBoundsOffCount(III)V
HSPLjava/lang/String;->checkIndex(II)V
+HSPLjava/lang/String;->checkOffset(II)V
HSPLjava/lang/String;->codePointAt(I)I
HSPLjava/lang/String;->codePointCount(II)I
+HSPLjava/lang/String;->coder()B
HSPLjava/lang/String;->compareTo(Ljava/lang/Object;)I
HSPLjava/lang/String;->compareToIgnoreCase(Ljava/lang/String;)I
HSPLjava/lang/String;->contains(Ljava/lang/CharSequence;)Z+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/CharSequence;Ljava/lang/String;
@@ -2394,11 +2414,12 @@
HSPLjava/lang/String;->endsWith(Ljava/lang/String;)Z+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->equals(Ljava/lang/Object;)Z
HSPLjava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z+]Ljava/lang/String;Ljava/lang/String;
-HSPLjava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;+]Ljava/util/Formatter;Ljava/util/Formatter;
-HSPLjava/lang/String;->format(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+HSPLjava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+HSPLjava/lang/String;->format(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;+]Ljava/util/Formatter;Ljava/util/Formatter;
HSPLjava/lang/String;->getBytes()[B
HSPLjava/lang/String;->getBytes(Ljava/lang/String;)[B
HSPLjava/lang/String;->getBytes(Ljava/nio/charset/Charset;)[B
+HSPLjava/lang/String;->getBytes([BIB)V+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->getChars(II[CI)V
HSPLjava/lang/String;->getChars([CI)V
HSPLjava/lang/String;->hashCode()I
@@ -2407,24 +2428,23 @@
HSPLjava/lang/String;->indexOf(Ljava/lang/String;)I+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->indexOf(Ljava/lang/String;I)I
HSPLjava/lang/String;->indexOf(Ljava/lang/String;Ljava/lang/String;I)I
-HSPLjava/lang/String;->indexOf([CIILjava/lang/String;I)I
-HSPLjava/lang/String;->indexOf([CII[CIII)I
+HSPLjava/lang/String;->indexOf([BBILjava/lang/String;I)I+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->isEmpty()Z
HSPLjava/lang/String;->join(Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String;
HSPLjava/lang/String;->join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
-HSPLjava/lang/String;->lastIndexOf(I)I
+HSPLjava/lang/String;->lastIndexOf(I)I+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->lastIndexOf(II)I
-HSPLjava/lang/String;->lastIndexOf(Ljava/lang/String;)I+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/lang/String;->lastIndexOf(Ljava/lang/String;)I
HSPLjava/lang/String;->lastIndexOf(Ljava/lang/String;I)I
HSPLjava/lang/String;->lastIndexOf(Ljava/lang/String;Ljava/lang/String;I)I
-HSPLjava/lang/String;->lastIndexOf([CIILjava/lang/String;I)I
+HSPLjava/lang/String;->lastIndexOf([BBILjava/lang/String;I)I
HSPLjava/lang/String;->lastIndexOf([CII[CIII)I
HSPLjava/lang/String;->length()I
HSPLjava/lang/String;->matches(Ljava/lang/String;)Z
HSPLjava/lang/String;->regionMatches(ILjava/lang/String;II)Z
-HSPLjava/lang/String;->regionMatches(ZILjava/lang/String;II)Z
+HSPLjava/lang/String;->regionMatches(ZILjava/lang/String;II)Z+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->replace(CC)Ljava/lang/String;
-HSPLjava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/CharSequence;Ljava/lang/String;
+HSPLjava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
HSPLjava/lang/String;->replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
HSPLjava/lang/String;->replaceFirst(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
HSPLjava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;
@@ -2434,10 +2454,10 @@
HSPLjava/lang/String;->subSequence(II)Ljava/lang/CharSequence;+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->substring(I)Ljava/lang/String;
HSPLjava/lang/String;->substring(II)Ljava/lang/String;
-HSPLjava/lang/String;->toLowerCase()Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/lang/String;->toLowerCase()Ljava/lang/String;
HSPLjava/lang/String;->toLowerCase(Ljava/util/Locale;)Ljava/lang/String;
HSPLjava/lang/String;->toString()Ljava/lang/String;
-HSPLjava/lang/String;->toUpperCase()Ljava/lang/String;
+HSPLjava/lang/String;->toUpperCase()Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->toUpperCase(Ljava/util/Locale;)Ljava/lang/String;
HSPLjava/lang/String;->trim()Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/lang/String;->valueOf(C)Ljava/lang/String;
@@ -2454,6 +2474,7 @@
HSPLjava/lang/StringBuffer;->append(C)Ljava/lang/StringBuffer;
HSPLjava/lang/StringBuffer;->append(I)Ljava/lang/StringBuffer;
HSPLjava/lang/StringBuffer;->append(J)Ljava/lang/StringBuffer;
+HSPLjava/lang/StringBuffer;->append(Ljava/lang/AbstractStringBuilder;)Ljava/lang/StringBuffer;
HSPLjava/lang/StringBuffer;->append(Ljava/lang/CharSequence;)Ljava/lang/Appendable;
HSPLjava/lang/StringBuffer;->append(Ljava/lang/CharSequence;)Ljava/lang/StringBuffer;
HSPLjava/lang/StringBuffer;->append(Ljava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder;
@@ -2466,10 +2487,11 @@
HSPLjava/lang/StringBuffer;->append([CII)Ljava/lang/StringBuffer;
HSPLjava/lang/StringBuffer;->charAt(I)C
HSPLjava/lang/StringBuffer;->codePointAt(I)I
+HSPLjava/lang/StringBuffer;->getBytes([BIB)V
HSPLjava/lang/StringBuffer;->getChars(II[CI)V
HSPLjava/lang/StringBuffer;->length()I
HSPLjava/lang/StringBuffer;->setLength(I)V
-HSPLjava/lang/StringBuffer;->toString()Ljava/lang/String;
+HSPLjava/lang/StringBuffer;->toString()Ljava/lang/String;+]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;
HSPLjava/lang/StringBuilder;-><init>()V
HSPLjava/lang/StringBuilder;-><init>(I)V
HSPLjava/lang/StringBuilder;-><init>(Ljava/lang/CharSequence;)V
@@ -2483,7 +2505,7 @@
HSPLjava/lang/StringBuilder;->append(Ljava/lang/CharSequence;)Ljava/lang/Appendable;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/lang/StringBuilder;->append(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;
HSPLjava/lang/StringBuilder;->append(Ljava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder;
-HSPLjava/lang/StringBuilder;->append(Ljava/lang/CharSequence;II)Ljava/lang/Appendable;
+HSPLjava/lang/StringBuilder;->append(Ljava/lang/CharSequence;II)Ljava/lang/Appendable;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/lang/StringBuilder;->append(Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder;
HSPLjava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/AbstractStringBuilder;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
@@ -2512,7 +2534,7 @@
HSPLjava/lang/StringBuilder;->subSequence(II)Ljava/lang/CharSequence;
HSPLjava/lang/StringBuilder;->substring(I)Ljava/lang/String;
HSPLjava/lang/StringBuilder;->substring(II)Ljava/lang/String;
-HSPLjava/lang/StringBuilder;->toString()Ljava/lang/String;
+HSPLjava/lang/StringBuilder;->toString()Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/lang/StringFactory;->newEmptyString()Ljava/lang/String;
HSPLjava/lang/StringFactory;->newStringFromBytes([B)Ljava/lang/String;
HSPLjava/lang/StringFactory;->newStringFromBytes([BI)Ljava/lang/String;
@@ -2523,6 +2545,17 @@
HSPLjava/lang/StringFactory;->newStringFromBytes([BLjava/nio/charset/Charset;)Ljava/lang/String;
HSPLjava/lang/StringFactory;->newStringFromChars([C)Ljava/lang/String;
HSPLjava/lang/StringFactory;->newStringFromChars([CII)Ljava/lang/String;
+HSPLjava/lang/StringLatin1;->canEncode(I)Z
+HSPLjava/lang/StringLatin1;->indexOf([BILjava/lang/String;II)I
+HSPLjava/lang/StringLatin1;->inflate([BI[BII)V
+HSPLjava/lang/StringLatin1;->lastIndexOf([BILjava/lang/String;II)I
+HSPLjava/lang/StringLatin1;->newString([BII)Ljava/lang/String;
+HSPLjava/lang/StringUTF16;->checkBoundsOffCount(II[B)V
+HSPLjava/lang/StringUTF16;->getChar([BI)C
+HSPLjava/lang/StringUTF16;->inflate([BI[BII)V
+HSPLjava/lang/StringUTF16;->length([B)I
+HSPLjava/lang/StringUTF16;->newBytesFor(I)[B
+HSPLjava/lang/StringUTF16;->putChar([BII)V
HSPLjava/lang/System$PropertiesWithNonOverrideableDefaults;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/lang/System$PropertiesWithNonOverrideableDefaults;->remove(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/lang/System;->arraycopy([BI[BII)V
@@ -2557,7 +2590,7 @@
HSPLjava/lang/Thread;-><init>(Ljava/lang/String;)V
HSPLjava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;)V
HSPLjava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;J)V
-HSPLjava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;JLjava/security/AccessControlContext;Z)V+]Ljava/lang/Thread;missing_types]Ljava/lang/ThreadGroup;Ljava/lang/ThreadGroup;
+HSPLjava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;JLjava/security/AccessControlContext;Z)V
HSPLjava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/String;)V
HSPLjava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V
HSPLjava/lang/Thread;->activeCount()I
@@ -2572,7 +2605,7 @@
HSPLjava/lang/Thread;->getState()Ljava/lang/Thread$State;
HSPLjava/lang/Thread;->getThreadGroup()Ljava/lang/ThreadGroup;
HSPLjava/lang/Thread;->getUncaughtExceptionHandler()Ljava/lang/Thread$UncaughtExceptionHandler;
-HSPLjava/lang/Thread;->init2(Ljava/lang/Thread;Z)V+]Ljava/lang/Thread;Landroid/os/HandlerThread;,Ljava/lang/Thread;,Landroid/net/ConnectivityThread;
+HSPLjava/lang/Thread;->init2(Ljava/lang/Thread;Z)V
HSPLjava/lang/Thread;->interrupt()V
HSPLjava/lang/Thread;->isAlive()Z
HSPLjava/lang/Thread;->isDaemon()Z
@@ -2626,7 +2659,7 @@
HSPLjava/lang/ThreadLocal$ThreadLocalMap;->nextIndex(II)I
HSPLjava/lang/ThreadLocal$ThreadLocalMap;->prevIndex(II)I
HSPLjava/lang/ThreadLocal$ThreadLocalMap;->rehash()V
-HSPLjava/lang/ThreadLocal$ThreadLocalMap;->remove(Ljava/lang/ThreadLocal;)V+]Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;
+HSPLjava/lang/ThreadLocal$ThreadLocalMap;->remove(Ljava/lang/ThreadLocal;)V
HSPLjava/lang/ThreadLocal$ThreadLocalMap;->replaceStaleEntry(Ljava/lang/ThreadLocal;Ljava/lang/Object;I)V
HSPLjava/lang/ThreadLocal$ThreadLocalMap;->resize()V
HSPLjava/lang/ThreadLocal$ThreadLocalMap;->set(Ljava/lang/ThreadLocal;Ljava/lang/Object;)V
@@ -2640,7 +2673,7 @@
HSPLjava/lang/ThreadLocal;->initialValue()Ljava/lang/Object;
HSPLjava/lang/ThreadLocal;->nextHashCode()I
HSPLjava/lang/ThreadLocal;->remove()V
-HSPLjava/lang/ThreadLocal;->set(Ljava/lang/Object;)V+]Ljava/lang/ThreadLocal;megamorphic_types
+HSPLjava/lang/ThreadLocal;->set(Ljava/lang/Object;)V+]Ljava/lang/ThreadLocal;missing_types
HSPLjava/lang/ThreadLocal;->setInitialValue()Ljava/lang/Object;
HSPLjava/lang/ThreadLocal;->withInitial(Ljava/util/function/Supplier;)Ljava/lang/ThreadLocal;
HSPLjava/lang/Throwable$PrintStreamOrWriter;-><init>()V
@@ -2650,9 +2683,9 @@
HSPLjava/lang/Throwable$WrappedPrintStream;->println(Ljava/lang/Object;)V
HSPLjava/lang/Throwable$WrappedPrintWriter;-><init>(Ljava/io/PrintWriter;)V
HSPLjava/lang/Throwable$WrappedPrintWriter;->lock()Ljava/lang/Object;
-HSPLjava/lang/Throwable$WrappedPrintWriter;->println(Ljava/lang/Object;)V
+HSPLjava/lang/Throwable$WrappedPrintWriter;->println(Ljava/lang/Object;)V+]Ljava/io/PrintWriter;Lcom/android/internal/util/FastPrintWriter;,Ljava/io/PrintWriter;,Lcom/android/internal/util/LineBreakBufferedWriter;
HSPLjava/lang/Throwable;-><init>()V
-HSPLjava/lang/Throwable;-><init>(Ljava/lang/String;)V+]Ljava/lang/Throwable;megamorphic_types
+HSPLjava/lang/Throwable;-><init>(Ljava/lang/String;)V
HSPLjava/lang/Throwable;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
HSPLjava/lang/Throwable;-><init>(Ljava/lang/String;Ljava/lang/Throwable;ZZ)V
HSPLjava/lang/Throwable;-><init>(Ljava/lang/Throwable;)V
@@ -2669,7 +2702,7 @@
HSPLjava/lang/Throwable;->printStackTrace()V
HSPLjava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V
HSPLjava/lang/Throwable;->printStackTrace(Ljava/io/PrintWriter;)V
-HSPLjava/lang/Throwable;->printStackTrace(Ljava/lang/Throwable$PrintStreamOrWriter;)V
+HSPLjava/lang/Throwable;->printStackTrace(Ljava/lang/Throwable$PrintStreamOrWriter;)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/Throwable$PrintStreamOrWriter;Ljava/lang/Throwable$WrappedPrintWriter;]Ljava/lang/Throwable;missing_types]Ljava/util/Set;Ljava/util/Collections$SetFromMap;
HSPLjava/lang/Throwable;->readObject(Ljava/io/ObjectInputStream;)V
HSPLjava/lang/Throwable;->setStackTrace([Ljava/lang/StackTraceElement;)V
HSPLjava/lang/Throwable;->toString()Ljava/lang/String;
@@ -2792,7 +2825,7 @@
HSPLjava/lang/reflect/AccessibleObject;->setAccessible0(Ljava/lang/reflect/AccessibleObject;Z)V
HSPLjava/lang/reflect/Array;->get(Ljava/lang/Object;I)Ljava/lang/Object;
HSPLjava/lang/reflect/Array;->getLength(Ljava/lang/Object;)I
-HSPLjava/lang/reflect/Array;->newArray(Ljava/lang/Class;I)Ljava/lang/Object;
+HSPLjava/lang/reflect/Array;->newArray(Ljava/lang/Class;I)Ljava/lang/Object;+]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/reflect/Array;->newInstance(Ljava/lang/Class;I)Ljava/lang/Object;
HSPLjava/lang/reflect/Array;->newInstance(Ljava/lang/Class;[I)Ljava/lang/Object;
HSPLjava/lang/reflect/Array;->set(Ljava/lang/Object;ILjava/lang/Object;)V
@@ -2834,20 +2867,20 @@
HSPLjava/lang/reflect/Field;->getDeclaringClass()Ljava/lang/Class;
HSPLjava/lang/reflect/Field;->getGenericType()Ljava/lang/reflect/Type;+]Ljava/lang/reflect/Field;Ljava/lang/reflect/Field;]Ljava/lang/Class;Ljava/lang/Class;]Llibcore/reflect/GenericSignatureParser;Llibcore/reflect/GenericSignatureParser;
HSPLjava/lang/reflect/Field;->getModifiers()I
-HSPLjava/lang/reflect/Field;->getName()Ljava/lang/String;
+HSPLjava/lang/reflect/Field;->getName()Ljava/lang/String;+]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/lang/reflect/Field;->getOffset()I
HSPLjava/lang/reflect/Field;->getSignatureAttribute()Ljava/lang/String;
HSPLjava/lang/reflect/Field;->getType()Ljava/lang/Class;
HSPLjava/lang/reflect/Field;->hashCode()I
HSPLjava/lang/reflect/Field;->isAnnotationPresent(Ljava/lang/Class;)Z
HSPLjava/lang/reflect/Field;->isEnumConstant()Z
-HSPLjava/lang/reflect/Field;->isSynthetic()Z+]Ljava/lang/reflect/Field;Ljava/lang/reflect/Field;
+HSPLjava/lang/reflect/Field;->isSynthetic()Z
HSPLjava/lang/reflect/InvocationTargetException;-><init>(Ljava/lang/Throwable;)V
HSPLjava/lang/reflect/InvocationTargetException;->getCause()Ljava/lang/Throwable;
HSPLjava/lang/reflect/Method$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
HSPLjava/lang/reflect/Method$1;->compare(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)I
HSPLjava/lang/reflect/Method;->equalNameAndParameters(Ljava/lang/reflect/Method;)Z
-HSPLjava/lang/reflect/Method;->equals(Ljava/lang/Object;)Z+]Ljava/lang/Object;Ljava/lang/Class;]Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;
+HSPLjava/lang/reflect/Method;->equals(Ljava/lang/Object;)Z
HSPLjava/lang/reflect/Method;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;
HSPLjava/lang/reflect/Method;->getDeclaredAnnotations()[Ljava/lang/annotation/Annotation;
HSPLjava/lang/reflect/Method;->getDeclaringClass()Ljava/lang/Class;
@@ -2858,7 +2891,7 @@
HSPLjava/lang/reflect/Method;->getParameterAnnotations()[[Ljava/lang/annotation/Annotation;
HSPLjava/lang/reflect/Method;->getParameterTypes()[Ljava/lang/Class;
HSPLjava/lang/reflect/Method;->getReturnType()Ljava/lang/Class;+]Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;
-HSPLjava/lang/reflect/Method;->hashCode()I+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;]Ljava/lang/Class;Ljava/lang/Class;
+HSPLjava/lang/reflect/Method;->hashCode()I
HSPLjava/lang/reflect/Method;->isBridge()Z
HSPLjava/lang/reflect/Method;->isDefault()Z
HSPLjava/lang/reflect/Method;->isSynthetic()Z
@@ -2896,7 +2929,7 @@
HSPLjava/lang/reflect/Proxy;->getMethodsRecursive([Ljava/lang/Class;Ljava/util/List;)V
HSPLjava/lang/reflect/Proxy;->getProxyClass0(Ljava/lang/ClassLoader;[Ljava/lang/Class;)Ljava/lang/Class;
HSPLjava/lang/reflect/Proxy;->intersectExceptions([Ljava/lang/Class;[Ljava/lang/Class;)[Ljava/lang/Class;
-HSPLjava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/lang/reflect/InvocationHandler;Llibcore/reflect/AnnotationFactory;
+HSPLjava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/lang/reflect/Proxy;->isProxyClass(Ljava/lang/Class;)Z
HSPLjava/lang/reflect/Proxy;->newProxyInstance(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;
HSPLjava/lang/reflect/Proxy;->validateReturnTypes(Ljava/util/List;)V
@@ -2932,11 +2965,11 @@
HSPLjava/math/BigDecimal;->divide(JIJIII)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->divide(Ljava/math/BigDecimal;II)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->divide(Ljava/math/BigDecimal;ILjava/math/RoundingMode;)Ljava/math/BigDecimal;
-HSPLjava/math/BigDecimal;->divide(Ljava/math/BigDecimal;Ljava/math/RoundingMode;)Ljava/math/BigDecimal;+]Ljava/math/BigDecimal;Ljava/math/BigDecimal;
+HSPLjava/math/BigDecimal;->divide(Ljava/math/BigDecimal;Ljava/math/RoundingMode;)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->divideAndRound(JJIII)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->getValueString(ILjava/lang/String;I)Ljava/lang/String;
HSPLjava/math/BigDecimal;->inflated()Ljava/math/BigInteger;
-HSPLjava/math/BigDecimal;->layoutChars(Z)Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/math/BigDecimal$StringBuilderHelper;Ljava/math/BigDecimal$StringBuilderHelper;]Ljava/lang/ThreadLocal;Ljava/math/BigDecimal$1;]Ljava/math/BigDecimal;Ljava/math/BigDecimal;]Ljava/lang/String;Ljava/lang/String;]Ljava/math/BigInteger;Ljava/math/BigInteger;
+HSPLjava/math/BigDecimal;->layoutChars(Z)Ljava/lang/String;
HSPLjava/math/BigDecimal;->longCompareMagnitude(JJ)I
HSPLjava/math/BigDecimal;->longMultiplyPowerTen(JI)J
HSPLjava/math/BigDecimal;->longValueExact()J
@@ -2945,12 +2978,15 @@
HSPLjava/math/BigDecimal;->multiply(JJ)J
HSPLjava/math/BigDecimal;->multiply(JJI)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->multiply(Ljava/math/BigDecimal;)Ljava/math/BigDecimal;
+HSPLjava/math/BigDecimal;->needIncrement(JIIJJ)Z
HSPLjava/math/BigDecimal;->scale()I
+HSPLjava/math/BigDecimal;->scaleByPowerOfTen(I)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->setScale(II)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->setScale(ILjava/math/RoundingMode;)Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->signum()I
HSPLjava/math/BigDecimal;->stripTrailingZeros()Ljava/math/BigDecimal;
HSPLjava/math/BigDecimal;->subtract(Ljava/math/BigDecimal;)Ljava/math/BigDecimal;
+HSPLjava/math/BigDecimal;->toBigInteger()Ljava/math/BigInteger;
HSPLjava/math/BigDecimal;->toBigIntegerExact()Ljava/math/BigInteger;
HSPLjava/math/BigDecimal;->toPlainString()Ljava/lang/String;
HSPLjava/math/BigDecimal;->toString()Ljava/lang/String;
@@ -2995,10 +3031,11 @@
HSPLjava/math/BigInteger;->multiply(Ljava/math/BigInteger;Z)Ljava/math/BigInteger;
HSPLjava/math/BigInteger;->multiplyByInt([III)Ljava/math/BigInteger;
HSPLjava/math/BigInteger;->multiplyToLen([II[II[I)[I
-HSPLjava/math/BigInteger;->pow(I)Ljava/math/BigInteger;+]Ljava/math/BigInteger;Ljava/math/BigInteger;
+HSPLjava/math/BigInteger;->padWithZeros(Ljava/lang/StringBuilder;I)V
+HSPLjava/math/BigInteger;->pow(I)Ljava/math/BigInteger;
HSPLjava/math/BigInteger;->readObject(Ljava/io/ObjectInputStream;)V
HSPLjava/math/BigInteger;->remainder(Ljava/math/BigInteger;)Ljava/math/BigInteger;
-HSPLjava/math/BigInteger;->remainderKnuth(Ljava/math/BigInteger;)Ljava/math/BigInteger;+]Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;
+HSPLjava/math/BigInteger;->remainderKnuth(Ljava/math/BigInteger;)Ljava/math/BigInteger;
HSPLjava/math/BigInteger;->reverse([I)[I
HSPLjava/math/BigInteger;->shiftLeft(I)Ljava/math/BigInteger;
HSPLjava/math/BigInteger;->shiftLeft([II)[I
@@ -3006,15 +3043,16 @@
HSPLjava/math/BigInteger;->shiftRightImpl(I)Ljava/math/BigInteger;
HSPLjava/math/BigInteger;->signInt()I
HSPLjava/math/BigInteger;->signum()I
-HSPLjava/math/BigInteger;->smallToString(I)Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/math/BigInteger;Ljava/math/BigInteger;]Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;
+HSPLjava/math/BigInteger;->smallToString(ILjava/lang/StringBuilder;I)V
HSPLjava/math/BigInteger;->stripLeadingZeroBytes([BII)[I
HSPLjava/math/BigInteger;->stripLeadingZeroInts([I)[I
HSPLjava/math/BigInteger;->subtract(Ljava/math/BigInteger;)Ljava/math/BigInteger;
HSPLjava/math/BigInteger;->subtract([I[I)[I
HSPLjava/math/BigInteger;->testBit(I)Z
-HSPLjava/math/BigInteger;->toByteArray()[B+]Ljava/math/BigInteger;Ljava/math/BigInteger;
+HSPLjava/math/BigInteger;->toByteArray()[B
HSPLjava/math/BigInteger;->toString()Ljava/lang/String;
HSPLjava/math/BigInteger;->toString(I)Ljava/lang/String;
+HSPLjava/math/BigInteger;->toString(Ljava/math/BigInteger;Ljava/lang/StringBuilder;II)V
HSPLjava/math/BigInteger;->trustedStripLeadingZeroInts([I)[I
HSPLjava/math/BigInteger;->valueOf(J)Ljava/math/BigInteger;
HSPLjava/math/MathContext;->equals(Ljava/lang/Object;)Z
@@ -3033,7 +3071,7 @@
HSPLjava/math/MutableBigInteger;->divide(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;Z)Ljava/math/MutableBigInteger;
HSPLjava/math/MutableBigInteger;->divideKnuth(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;)Ljava/math/MutableBigInteger;
HSPLjava/math/MutableBigInteger;->divideKnuth(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;Z)Ljava/math/MutableBigInteger;
-HSPLjava/math/MutableBigInteger;->divideMagnitude(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;Z)Ljava/math/MutableBigInteger;+]Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;
+HSPLjava/math/MutableBigInteger;->divideMagnitude(Ljava/math/MutableBigInteger;Ljava/math/MutableBigInteger;Z)Ljava/math/MutableBigInteger;
HSPLjava/math/MutableBigInteger;->divideOneWord(ILjava/math/MutableBigInteger;)I
HSPLjava/math/MutableBigInteger;->getLowestSetBit()I
HSPLjava/math/MutableBigInteger;->getMagnitudeArray()[I
@@ -3043,6 +3081,7 @@
HSPLjava/math/MutableBigInteger;->rightShift(I)V
HSPLjava/math/MutableBigInteger;->toBigInteger(I)Ljava/math/BigInteger;
HSPLjava/math/MutableBigInteger;->unsignedLongCompare(JJ)Z
+HSPLjava/math/RoundingMode;->valueOf(I)Ljava/math/RoundingMode;
HSPLjava/math/RoundingMode;->values()[Ljava/math/RoundingMode;
HSPLjava/net/AbstractPlainDatagramSocketImpl;-><init>()V
HSPLjava/net/AbstractPlainDatagramSocketImpl;->bind(ILjava/net/InetAddress;)V
@@ -3138,9 +3177,9 @@
HSPLjava/net/HttpCookie;-><init>(Ljava/lang/String;Ljava/lang/String;)V
HSPLjava/net/HttpCookie;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
HSPLjava/net/HttpCookie;->assignAttribute(Ljava/net/HttpCookie;Ljava/lang/String;Ljava/lang/String;)V
-HSPLjava/net/HttpCookie;->domainMatches(Ljava/lang/String;Ljava/lang/String;)Z+]Ljava/lang/String;Ljava/lang/String;
-HSPLjava/net/HttpCookie;->equals(Ljava/lang/Object;)Z+]Ljava/net/HttpCookie;Ljava/net/HttpCookie;
-HSPLjava/net/HttpCookie;->equalsIgnoreCase(Ljava/lang/String;Ljava/lang/String;)Z+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/net/HttpCookie;->domainMatches(Ljava/lang/String;Ljava/lang/String;)Z
+HSPLjava/net/HttpCookie;->equals(Ljava/lang/Object;)Z
+HSPLjava/net/HttpCookie;->equalsIgnoreCase(Ljava/lang/String;Ljava/lang/String;)Z
HSPLjava/net/HttpCookie;->getDomain()Ljava/lang/String;
HSPLjava/net/HttpCookie;->getMaxAge()J
HSPLjava/net/HttpCookie;->getName()Ljava/lang/String;
@@ -3175,13 +3214,13 @@
HSPLjava/net/IDN;->toASCII(Ljava/lang/String;I)Ljava/lang/String;
HSPLjava/net/InMemoryCookieStore;-><init>()V
HSPLjava/net/InMemoryCookieStore;-><init>(I)V
-HSPLjava/net/InMemoryCookieStore;->add(Ljava/net/URI;Ljava/net/HttpCookie;)V+]Ljava/util/concurrent/locks/ReentrantLock;Ljava/util/concurrent/locks/ReentrantLock;
+HSPLjava/net/InMemoryCookieStore;->add(Ljava/net/URI;Ljava/net/HttpCookie;)V
HSPLjava/net/InMemoryCookieStore;->addIndex(Ljava/util/Map;Ljava/lang/Object;Ljava/net/HttpCookie;)V
HSPLjava/net/InMemoryCookieStore;->get(Ljava/net/URI;)Ljava/util/List;
HSPLjava/net/InMemoryCookieStore;->getEffectiveURI(Ljava/net/URI;)Ljava/net/URI;
HSPLjava/net/InMemoryCookieStore;->getInternal1(Ljava/util/List;Ljava/util/Map;Ljava/lang/String;)V
HSPLjava/net/InMemoryCookieStore;->getInternal2(Ljava/util/List;Ljava/util/Map;Ljava/lang/Comparable;)V
-HSPLjava/net/InMemoryCookieStore;->netscapeDomainMatches(Ljava/lang/String;Ljava/lang/String;)Z+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/net/InMemoryCookieStore;->netscapeDomainMatches(Ljava/lang/String;Ljava/lang/String;)Z
HSPLjava/net/Inet4Address;-><init>()V
HSPLjava/net/Inet4Address;-><init>(Ljava/lang/String;[B)V
HSPLjava/net/Inet4Address;->equals(Ljava/lang/Object;)Z
@@ -3269,7 +3308,7 @@
HSPLjava/net/NetworkInterface$1checkedAddresses;->nextElement()Ljava/net/InetAddress;
HSPLjava/net/NetworkInterface;-><init>(Ljava/lang/String;I[Ljava/net/InetAddress;)V
HSPLjava/net/NetworkInterface;->getAll()[Ljava/net/NetworkInterface;
-HSPLjava/net/NetworkInterface;->getByName(Ljava/lang/String;)Ljava/net/NetworkInterface;+]Ljava/net/NetworkInterface;Ljava/net/NetworkInterface;
+HSPLjava/net/NetworkInterface;->getByName(Ljava/lang/String;)Ljava/net/NetworkInterface;
HSPLjava/net/NetworkInterface;->getFlags()I
HSPLjava/net/NetworkInterface;->getHardwareAddress()[B
HSPLjava/net/NetworkInterface;->getIndex()I
@@ -3432,8 +3471,8 @@
HSPLjava/net/URI;->checkPath(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
HSPLjava/net/URI;->compare(Ljava/lang/String;Ljava/lang/String;)I
HSPLjava/net/URI;->compareIgnoringCase(Ljava/lang/String;Ljava/lang/String;)I
-HSPLjava/net/URI;->compareTo(Ljava/lang/Object;)I+]Ljava/net/URI;Ljava/net/URI;
-HSPLjava/net/URI;->compareTo(Ljava/net/URI;)I+]Ljava/net/URI;Ljava/net/URI;
+HSPLjava/net/URI;->compareTo(Ljava/lang/Object;)I
+HSPLjava/net/URI;->compareTo(Ljava/net/URI;)I
HSPLjava/net/URI;->create(Ljava/lang/String;)Ljava/net/URI;
HSPLjava/net/URI;->decode(Ljava/lang/String;)Ljava/lang/String;
HSPLjava/net/URI;->defineString()V
@@ -3502,10 +3541,10 @@
HSPLjava/net/URLConnection;->setReadTimeout(I)V
HSPLjava/net/URLConnection;->setUseCaches(Z)V
HSPLjava/net/URLDecoder;->decode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-HSPLjava/net/URLDecoder;->decode(Ljava/lang/String;Ljava/nio/charset/Charset;)Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLjava/net/URLDecoder;->decode(Ljava/lang/String;Ljava/nio/charset/Charset;)Ljava/lang/String;
HSPLjava/net/URLDecoder;->isValidHexChar(C)Z
HSPLjava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-HSPLjava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/nio/charset/Charset;)Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/String;Ljava/lang/String;]Ljava/util/BitSet;Ljava/util/BitSet;]Ljava/io/CharArrayWriter;Ljava/io/CharArrayWriter;
+HSPLjava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/nio/charset/Charset;)Ljava/lang/String;
HSPLjava/net/URLStreamHandler;-><init>()V
HSPLjava/net/URLStreamHandler;->parseURL(Ljava/net/URL;Ljava/lang/String;II)V
HSPLjava/net/URLStreamHandler;->setURL(Ljava/net/URL;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
@@ -3518,13 +3557,13 @@
HSPLjava/nio/Bits;->getFloatL(Ljava/nio/ByteBuffer;I)F
HSPLjava/nio/Bits;->getInt(Ljava/nio/ByteBuffer;IZ)I
HSPLjava/nio/Bits;->getIntB(Ljava/nio/ByteBuffer;I)I
-HSPLjava/nio/Bits;->getIntL(Ljava/nio/ByteBuffer;I)I+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/Bits;->getIntL(Ljava/nio/ByteBuffer;I)I
HSPLjava/nio/Bits;->getLong(Ljava/nio/ByteBuffer;IZ)J
HSPLjava/nio/Bits;->getLongB(Ljava/nio/ByteBuffer;I)J
-HSPLjava/nio/Bits;->getLongL(Ljava/nio/ByteBuffer;I)J+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/Bits;->getLongL(Ljava/nio/ByteBuffer;I)J
HSPLjava/nio/Bits;->getShort(Ljava/nio/ByteBuffer;IZ)S
-HSPLjava/nio/Bits;->getShortB(Ljava/nio/ByteBuffer;I)S+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/Bits;->getShortL(Ljava/nio/ByteBuffer;I)S+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/Bits;->getShortB(Ljava/nio/ByteBuffer;I)S
+HSPLjava/nio/Bits;->getShortL(Ljava/nio/ByteBuffer;I)S
HSPLjava/nio/Bits;->int0(I)B
HSPLjava/nio/Bits;->int1(I)B
HSPLjava/nio/Bits;->int2(I)B
@@ -3548,17 +3587,17 @@
HSPLjava/nio/Bits;->putFloat(Ljava/nio/ByteBuffer;IFZ)V
HSPLjava/nio/Bits;->putInt(Ljava/nio/ByteBuffer;IIZ)V
HSPLjava/nio/Bits;->putIntB(Ljava/nio/ByteBuffer;II)V
-HSPLjava/nio/Bits;->putIntL(Ljava/nio/ByteBuffer;II)V+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/Bits;->putIntL(Ljava/nio/ByteBuffer;II)V
HSPLjava/nio/Bits;->putLong(Ljava/nio/ByteBuffer;IJZ)V
HSPLjava/nio/Bits;->putLongB(Ljava/nio/ByteBuffer;IJ)V
-HSPLjava/nio/Bits;->putLongL(Ljava/nio/ByteBuffer;IJ)V+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/Bits;->putLongL(Ljava/nio/ByteBuffer;IJ)V
HSPLjava/nio/Bits;->putShort(Ljava/nio/ByteBuffer;ISZ)V
-HSPLjava/nio/Bits;->putShortB(Ljava/nio/ByteBuffer;IS)V+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/Bits;->putShortL(Ljava/nio/ByteBuffer;IS)V+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/Bits;->putShortB(Ljava/nio/ByteBuffer;IS)V
+HSPLjava/nio/Bits;->putShortL(Ljava/nio/ByteBuffer;IS)V
HSPLjava/nio/Bits;->short0(S)B
HSPLjava/nio/Bits;->short1(S)B
HSPLjava/nio/Bits;->unsafe()Lsun/misc/Unsafe;
-HSPLjava/nio/Buffer;-><init>(IIIII)V+]Ljava/nio/Buffer;megamorphic_types
+HSPLjava/nio/Buffer;-><init>(IIIII)V
HSPLjava/nio/Buffer;->capacity()I
HSPLjava/nio/Buffer;->checkBounds(III)V
HSPLjava/nio/Buffer;->checkIndex(I)I
@@ -3589,19 +3628,20 @@
HSPLjava/nio/ByteBuffer;->arrayOffset()I
HSPLjava/nio/ByteBuffer;->clear()Ljava/nio/Buffer;
HSPLjava/nio/ByteBuffer;->compare(BB)I
-HSPLjava/nio/ByteBuffer;->compareTo(Ljava/nio/ByteBuffer;)I+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;,Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/ByteBuffer;->compareTo(Ljava/lang/Object;)I+]Ljava/nio/ByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/ByteBuffer;->compareTo(Ljava/nio/ByteBuffer;)I
HSPLjava/nio/ByteBuffer;->equals(BB)Z
HSPLjava/nio/ByteBuffer;->equals(Ljava/lang/Object;)Z
HSPLjava/nio/ByteBuffer;->flip()Ljava/nio/Buffer;
HSPLjava/nio/ByteBuffer;->get([B)Ljava/nio/ByteBuffer;
HSPLjava/nio/ByteBuffer;->hasArray()Z
-HSPLjava/nio/ByteBuffer;->hashCode()I+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;,Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/ByteBuffer;->hashCode()I
HSPLjava/nio/ByteBuffer;->limit(I)Ljava/nio/Buffer;
HSPLjava/nio/ByteBuffer;->mark()Ljava/nio/Buffer;
HSPLjava/nio/ByteBuffer;->order()Ljava/nio/ByteOrder;
HSPLjava/nio/ByteBuffer;->order(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;
HSPLjava/nio/ByteBuffer;->position(I)Ljava/nio/Buffer;
-HSPLjava/nio/ByteBuffer;->put(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;,Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/ByteBuffer;->put(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;
HSPLjava/nio/ByteBuffer;->put([B)Ljava/nio/ByteBuffer;
HSPLjava/nio/ByteBuffer;->reset()Ljava/nio/Buffer;
HSPLjava/nio/ByteBuffer;->rewind()Ljava/nio/Buffer;
@@ -3618,12 +3658,14 @@
HSPLjava/nio/ByteBufferAsCharBuffer;->toString(II)Ljava/lang/String;
HSPLjava/nio/ByteBufferAsFloatBuffer;-><init>(Ljava/nio/ByteBuffer;IIIIILjava/nio/ByteOrder;)V
HSPLjava/nio/ByteBufferAsFloatBuffer;->ix(I)I
-HSPLjava/nio/ByteBufferAsFloatBuffer;->put(IF)Ljava/nio/FloatBuffer;+]Ljava/nio/ByteBufferAsFloatBuffer;Ljava/nio/ByteBufferAsFloatBuffer;]Ljava/nio/ByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/ByteBufferAsFloatBuffer;->put(IF)Ljava/nio/FloatBuffer;
HSPLjava/nio/ByteBufferAsFloatBuffer;->put([FII)Ljava/nio/FloatBuffer;
HSPLjava/nio/ByteBufferAsIntBuffer;-><init>(Ljava/nio/ByteBuffer;IIIIILjava/nio/ByteOrder;)V
HSPLjava/nio/ByteBufferAsIntBuffer;->get([III)Ljava/nio/IntBuffer;
HSPLjava/nio/ByteBufferAsIntBuffer;->ix(I)I
-HSPLjava/nio/ByteBufferAsIntBuffer;->put([III)Ljava/nio/IntBuffer;+]Ljava/nio/ByteBufferAsIntBuffer;Ljava/nio/ByteBufferAsIntBuffer;]Ljava/nio/ByteBuffer;Ljava/nio/DirectByteBuffer;,Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/ByteBufferAsIntBuffer;->put(I)Ljava/nio/IntBuffer;
+HSPLjava/nio/ByteBufferAsIntBuffer;->put(II)Ljava/nio/IntBuffer;
+HSPLjava/nio/ByteBufferAsIntBuffer;->put([III)Ljava/nio/IntBuffer;
HSPLjava/nio/ByteBufferAsLongBuffer;-><init>(Ljava/nio/ByteBuffer;IIIIILjava/nio/ByteOrder;)V
HSPLjava/nio/ByteBufferAsLongBuffer;->get([JII)Ljava/nio/LongBuffer;
HSPLjava/nio/ByteBufferAsLongBuffer;->ix(I)I
@@ -3646,7 +3688,7 @@
HSPLjava/nio/CharBuffer;->length()I
HSPLjava/nio/CharBuffer;->limit(I)Ljava/nio/Buffer;
HSPLjava/nio/CharBuffer;->position(I)Ljava/nio/Buffer;
-HSPLjava/nio/CharBuffer;->toString()Ljava/lang/String;+]Ljava/nio/CharBuffer;Ljava/nio/HeapCharBuffer;
+HSPLjava/nio/CharBuffer;->toString()Ljava/lang/String;
HSPLjava/nio/CharBuffer;->wrap(Ljava/lang/CharSequence;)Ljava/nio/CharBuffer;
HSPLjava/nio/CharBuffer;->wrap(Ljava/lang/CharSequence;II)Ljava/nio/CharBuffer;
HSPLjava/nio/CharBuffer;->wrap([C)Ljava/nio/CharBuffer;
@@ -3666,17 +3708,18 @@
HSPLjava/nio/DirectByteBuffer;->asShortBuffer()Ljava/nio/ShortBuffer;
HSPLjava/nio/DirectByteBuffer;->cleaner()Lsun/misc/Cleaner;
HSPLjava/nio/DirectByteBuffer;->duplicate()Ljava/nio/ByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->duplicate()Ljava/nio/MappedByteBuffer;
HSPLjava/nio/DirectByteBuffer;->get()B
-HSPLjava/nio/DirectByteBuffer;->get(I)B+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->get(I)B
HSPLjava/nio/DirectByteBuffer;->get(J)B
-HSPLjava/nio/DirectByteBuffer;->get([BII)Ljava/nio/ByteBuffer;+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->get([BII)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->getChar()C
HSPLjava/nio/DirectByteBuffer;->getChar(I)C
HSPLjava/nio/DirectByteBuffer;->getCharUnchecked(I)C
HSPLjava/nio/DirectByteBuffer;->getInt()I
HSPLjava/nio/DirectByteBuffer;->getInt(I)I+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
HSPLjava/nio/DirectByteBuffer;->getInt(J)I
-HSPLjava/nio/DirectByteBuffer;->getLong(I)J+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->getLong(I)J
HSPLjava/nio/DirectByteBuffer;->getLong(J)J
HSPLjava/nio/DirectByteBuffer;->getShort()S
HSPLjava/nio/DirectByteBuffer;->getShort(I)S+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
@@ -3691,19 +3734,20 @@
HSPLjava/nio/DirectByteBuffer;->put(IB)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->put(JB)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->put(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;
-HSPLjava/nio/DirectByteBuffer;->put([BII)Ljava/nio/ByteBuffer;+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->put([BII)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->putDouble(JD)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->putFloat(JF)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->putFloatUnchecked(IF)V
HSPLjava/nio/DirectByteBuffer;->putInt(I)Ljava/nio/ByteBuffer;
-HSPLjava/nio/DirectByteBuffer;->putInt(II)Ljava/nio/ByteBuffer;+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->putInt(II)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->putInt(JI)Ljava/nio/ByteBuffer;
-HSPLjava/nio/DirectByteBuffer;->putLong(IJ)Ljava/nio/ByteBuffer;+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
-HSPLjava/nio/DirectByteBuffer;->putLong(J)Ljava/nio/ByteBuffer;+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->putLong(IJ)Ljava/nio/ByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->putLong(J)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->putLong(JJ)Ljava/nio/ByteBuffer;
HSPLjava/nio/DirectByteBuffer;->putUnchecked(I[FII)V
HSPLjava/nio/DirectByteBuffer;->setAccessible(Z)V
HSPLjava/nio/DirectByteBuffer;->slice()Ljava/nio/ByteBuffer;
+HSPLjava/nio/DirectByteBuffer;->slice()Ljava/nio/MappedByteBuffer;+]Ljava/nio/DirectByteBuffer;Ljava/nio/DirectByteBuffer;
HSPLjava/nio/FloatBuffer;-><init>(IIII)V
HSPLjava/nio/FloatBuffer;-><init>(IIII[FI)V
HSPLjava/nio/FloatBuffer;->limit(I)Ljava/nio/Buffer;
@@ -3718,38 +3762,38 @@
HSPLjava/nio/HeapByteBuffer;->_put(IB)V
HSPLjava/nio/HeapByteBuffer;->asIntBuffer()Ljava/nio/IntBuffer;
HSPLjava/nio/HeapByteBuffer;->asLongBuffer()Ljava/nio/LongBuffer;
-HSPLjava/nio/HeapByteBuffer;->asReadOnlyBuffer()Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->asReadOnlyBuffer()Ljava/nio/ByteBuffer;
HSPLjava/nio/HeapByteBuffer;->asShortBuffer()Ljava/nio/ShortBuffer;
-HSPLjava/nio/HeapByteBuffer;->compact()Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->compact()Ljava/nio/ByteBuffer;
HSPLjava/nio/HeapByteBuffer;->duplicate()Ljava/nio/ByteBuffer;
HSPLjava/nio/HeapByteBuffer;->get()B+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->get(I)B+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->get(I)B
HSPLjava/nio/HeapByteBuffer;->get([BII)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
HSPLjava/nio/HeapByteBuffer;->getFloat()F
HSPLjava/nio/HeapByteBuffer;->getFloat(I)F
-HSPLjava/nio/HeapByteBuffer;->getInt()I+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->getInt(I)I+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->getLong()J+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->getInt()I
+HSPLjava/nio/HeapByteBuffer;->getInt(I)I
+HSPLjava/nio/HeapByteBuffer;->getLong()J
HSPLjava/nio/HeapByteBuffer;->getLong(I)J
-HSPLjava/nio/HeapByteBuffer;->getShort()S+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->getShort(I)S+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->getShort()S
+HSPLjava/nio/HeapByteBuffer;->getShort(I)S
HSPLjava/nio/HeapByteBuffer;->getUnchecked(I[III)V
HSPLjava/nio/HeapByteBuffer;->getUnchecked(I[SII)V
HSPLjava/nio/HeapByteBuffer;->isDirect()Z
HSPLjava/nio/HeapByteBuffer;->isReadOnly()Z
HSPLjava/nio/HeapByteBuffer;->ix(I)I
HSPLjava/nio/HeapByteBuffer;->put(B)Ljava/nio/ByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->put(IB)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->put([BII)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->put(IB)Ljava/nio/ByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->put([BII)Ljava/nio/ByteBuffer;
HSPLjava/nio/HeapByteBuffer;->putChar(C)Ljava/nio/ByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->putFloat(F)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->putInt(I)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->putInt(II)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->putFloat(F)Ljava/nio/ByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->putInt(I)Ljava/nio/ByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->putInt(II)Ljava/nio/ByteBuffer;
HSPLjava/nio/HeapByteBuffer;->putLong(IJ)Ljava/nio/ByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->putLong(J)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->putShort(IS)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->putShort(S)Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
-HSPLjava/nio/HeapByteBuffer;->slice()Ljava/nio/ByteBuffer;+]Ljava/nio/HeapByteBuffer;Ljava/nio/HeapByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->putLong(J)Ljava/nio/ByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->putShort(IS)Ljava/nio/ByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->putShort(S)Ljava/nio/ByteBuffer;
+HSPLjava/nio/HeapByteBuffer;->slice()Ljava/nio/ByteBuffer;
HSPLjava/nio/HeapCharBuffer;-><init>(II)V
HSPLjava/nio/HeapCharBuffer;-><init>(IIZ)V
HSPLjava/nio/HeapCharBuffer;-><init>([CII)V
@@ -3757,7 +3801,7 @@
HSPLjava/nio/HeapCharBuffer;-><init>([CIIZ)V
HSPLjava/nio/HeapCharBuffer;->get(I)C
HSPLjava/nio/HeapCharBuffer;->ix(I)I
-HSPLjava/nio/HeapCharBuffer;->put(Ljava/nio/CharBuffer;)Ljava/nio/CharBuffer;+]Ljava/nio/HeapCharBuffer;Ljava/nio/HeapCharBuffer;
+HSPLjava/nio/HeapCharBuffer;->put(Ljava/nio/CharBuffer;)Ljava/nio/CharBuffer;
HSPLjava/nio/HeapCharBuffer;->put([CII)Ljava/nio/CharBuffer;
HSPLjava/nio/HeapCharBuffer;->slice()Ljava/nio/CharBuffer;
HSPLjava/nio/HeapCharBuffer;->toString(II)Ljava/lang/String;
@@ -3780,7 +3824,7 @@
HSPLjava/nio/MappedByteBuffer;-><init>(IIIILjava/io/FileDescriptor;)V
HSPLjava/nio/MappedByteBuffer;-><init>(IIII[BI)V
HSPLjava/nio/MappedByteBuffer;->checkMapped()V
-HSPLjava/nio/MappedByteBuffer;->load()Ljava/nio/MappedByteBuffer;+]Ljava/nio/MappedByteBuffer;Ljava/nio/DirectByteBuffer;]Lsun/misc/Unsafe;Lsun/misc/Unsafe;
+HSPLjava/nio/MappedByteBuffer;->load()Ljava/nio/MappedByteBuffer;
HSPLjava/nio/MappedByteBuffer;->mappingAddress(J)J
HSPLjava/nio/MappedByteBuffer;->mappingLength(J)J
HSPLjava/nio/MappedByteBuffer;->mappingOffset()J
@@ -3823,9 +3867,9 @@
HSPLjava/nio/channels/SocketChannel;->validOps()I
HSPLjava/nio/channels/spi/AbstractInterruptibleChannel$1;-><init>(Ljava/nio/channels/spi/AbstractInterruptibleChannel;)V
HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;-><init>()V
-HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;->begin()V+]Ljava/lang/Thread;Landroid/os/HandlerThread;
+HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;->begin()V+]Ljava/lang/Thread;missing_types
HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;->blockedOn(Lsun/nio/ch/Interruptible;)V+]Ljava/lang/Thread;Landroid/os/HandlerThread;
-HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;->close()V
+HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;->close()V+]Ljava/nio/channels/spi/AbstractInterruptibleChannel;Lsun/nio/ch/FileChannelImpl;
HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;->end(Z)V
HSPLjava/nio/channels/spi/AbstractInterruptibleChannel;->isOpen()Z
HSPLjava/nio/channels/spi/AbstractSelectableChannel;-><init>(Ljava/nio/channels/spi/SelectorProvider;)V
@@ -3858,7 +3902,6 @@
HSPLjava/nio/channels/spi/SelectorProvider;->provider()Ljava/nio/channels/spi/SelectorProvider;
HSPLjava/nio/charset/Charset;-><init>(Ljava/lang/String;[Ljava/lang/String;)V
HSPLjava/nio/charset/Charset;->aliases()Ljava/util/Set;
-HSPLjava/nio/charset/Charset;->atBugLevel(Ljava/lang/String;)Z
HSPLjava/nio/charset/Charset;->cache(Ljava/lang/String;Ljava/nio/charset/Charset;)V
HSPLjava/nio/charset/Charset;->checkName(Ljava/lang/String;)V
HSPLjava/nio/charset/Charset;->decode(Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer;
@@ -3870,7 +3913,7 @@
HSPLjava/nio/charset/Charset;->forName(Ljava/lang/String;)Ljava/nio/charset/Charset;
HSPLjava/nio/charset/Charset;->forNameUEE(Ljava/lang/String;)Ljava/nio/charset/Charset;
HSPLjava/nio/charset/Charset;->isSupported(Ljava/lang/String;)Z
-HSPLjava/nio/charset/Charset;->lookup(Ljava/lang/String;)Ljava/nio/charset/Charset;+]Ljava/util/Map$Entry;Ljava/util/AbstractMap$SimpleImmutableEntry;
+HSPLjava/nio/charset/Charset;->lookup(Ljava/lang/String;)Ljava/nio/charset/Charset;
HSPLjava/nio/charset/Charset;->lookup2(Ljava/lang/String;)Ljava/nio/charset/Charset;
HSPLjava/nio/charset/Charset;->name()Ljava/lang/String;
HSPLjava/nio/charset/CharsetDecoder;-><init>(Ljava/nio/charset/Charset;FF)V
@@ -3878,8 +3921,8 @@
HSPLjava/nio/charset/CharsetDecoder;->averageCharsPerByte()F
HSPLjava/nio/charset/CharsetDecoder;->charset()Ljava/nio/charset/Charset;
HSPLjava/nio/charset/CharsetDecoder;->decode(Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer;
-HSPLjava/nio/charset/CharsetDecoder;->decode(Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;Z)Ljava/nio/charset/CoderResult;
-HSPLjava/nio/charset/CharsetDecoder;->flush(Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult;
+HSPLjava/nio/charset/CharsetDecoder;->decode(Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;Z)Ljava/nio/charset/CoderResult;+]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;]Ljava/nio/charset/CoderResult;Ljava/nio/charset/CoderResult;]Ljava/nio/charset/CharsetDecoder;Lcom/android/icu/charset/CharsetDecoderICU;
+HSPLjava/nio/charset/CharsetDecoder;->flush(Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult;+]Ljava/nio/charset/CoderResult;Ljava/nio/charset/CoderResult;]Ljava/nio/charset/CharsetDecoder;Lcom/android/icu/charset/CharsetDecoderICU;
HSPLjava/nio/charset/CharsetDecoder;->implFlush(Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult;
HSPLjava/nio/charset/CharsetDecoder;->implOnMalformedInput(Ljava/nio/charset/CodingErrorAction;)V
HSPLjava/nio/charset/CharsetDecoder;->implOnUnmappableCharacter(Ljava/nio/charset/CodingErrorAction;)V
@@ -3890,7 +3933,7 @@
HSPLjava/nio/charset/CharsetDecoder;->onUnmappableCharacter(Ljava/nio/charset/CodingErrorAction;)Ljava/nio/charset/CharsetDecoder;
HSPLjava/nio/charset/CharsetDecoder;->replaceWith(Ljava/lang/String;)Ljava/nio/charset/CharsetDecoder;
HSPLjava/nio/charset/CharsetDecoder;->replacement()Ljava/lang/String;
-HSPLjava/nio/charset/CharsetDecoder;->reset()Ljava/nio/charset/CharsetDecoder;
+HSPLjava/nio/charset/CharsetDecoder;->reset()Ljava/nio/charset/CharsetDecoder;+]Ljava/nio/charset/CharsetDecoder;Lcom/android/icu/charset/CharsetDecoderICU;
HSPLjava/nio/charset/CharsetDecoder;->unmappableCharacterAction()Ljava/nio/charset/CodingErrorAction;
HSPLjava/nio/charset/CharsetEncoder;-><init>(Ljava/nio/charset/Charset;FF)V
HSPLjava/nio/charset/CharsetEncoder;-><init>(Ljava/nio/charset/Charset;FF[B)V
@@ -3943,6 +3986,7 @@
HSPLjava/nio/file/attribute/FileTime;-><init>(JLjava/util/concurrent/TimeUnit;Ljava/time/Instant;)V
HSPLjava/nio/file/attribute/FileTime;->append(Ljava/lang/StringBuilder;II)Ljava/lang/StringBuilder;
HSPLjava/nio/file/attribute/FileTime;->from(JLjava/util/concurrent/TimeUnit;)Ljava/nio/file/attribute/FileTime;
+HSPLjava/nio/file/attribute/FileTime;->toMillis()J
HSPLjava/nio/file/attribute/FileTime;->toString()Ljava/lang/String;
HSPLjava/nio/file/spi/FileSystemProvider;->newInputStream(Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream;
HSPLjava/security/AccessControlContext;-><init>([Ljava/security/ProtectionDomain;)V
@@ -3954,7 +3998,7 @@
HSPLjava/security/CodeSigner;-><init>(Ljava/security/cert/CertPath;Ljava/security/Timestamp;)V
HSPLjava/security/CodeSigner;->getSignerCertPath()Ljava/security/cert/CertPath;
HSPLjava/security/DigestInputStream;-><init>(Ljava/io/InputStream;Ljava/security/MessageDigest;)V
-HSPLjava/security/DigestInputStream;->read([BII)I+]Ljava/io/InputStream;Ljava/io/FileInputStream;]Ljava/security/MessageDigest;Ljava/security/MessageDigest$Delegate;
+HSPLjava/security/DigestInputStream;->read([BII)I
HSPLjava/security/DigestInputStream;->setMessageDigest(Ljava/security/MessageDigest;)V
HSPLjava/security/GeneralSecurityException;-><init>(Ljava/lang/String;)V
HSPLjava/security/KeyFactory;-><init>(Ljava/lang/String;)V
@@ -3995,16 +4039,16 @@
HSPLjava/security/MessageDigest$Delegate;-><init>(Ljava/security/MessageDigestSpi;Ljava/lang/String;)V
HSPLjava/security/MessageDigest$Delegate;->clone()Ljava/lang/Object;
HSPLjava/security/MessageDigest$Delegate;->engineDigest()[B
-HSPLjava/security/MessageDigest$Delegate;->engineDigest([BII)I+]Ljava/security/MessageDigestSpi;Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$SHA1;
+HSPLjava/security/MessageDigest$Delegate;->engineDigest([BII)I
HSPLjava/security/MessageDigest$Delegate;->engineGetDigestLength()I
HSPLjava/security/MessageDigest$Delegate;->engineReset()V
HSPLjava/security/MessageDigest$Delegate;->engineUpdate(B)V
HSPLjava/security/MessageDigest$Delegate;->engineUpdate(Ljava/nio/ByteBuffer;)V
-HSPLjava/security/MessageDigest$Delegate;->engineUpdate([BII)V+]Ljava/security/MessageDigestSpi;Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$SHA1;
+HSPLjava/security/MessageDigest$Delegate;->engineUpdate([BII)V
HSPLjava/security/MessageDigest;-><init>(Ljava/lang/String;)V
HSPLjava/security/MessageDigest;->digest()[B
HSPLjava/security/MessageDigest;->digest([B)[B
-HSPLjava/security/MessageDigest;->digest([BII)I+]Ljava/security/MessageDigest;Ljava/security/MessageDigest$Delegate;
+HSPLjava/security/MessageDigest;->digest([BII)I
HSPLjava/security/MessageDigest;->getDigestLength()I
HSPLjava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;
HSPLjava/security/MessageDigest;->getInstance(Ljava/lang/String;Ljava/lang/String;)Ljava/security/MessageDigest;
@@ -4014,9 +4058,9 @@
HSPLjava/security/MessageDigest;->update(B)V
HSPLjava/security/MessageDigest;->update(Ljava/nio/ByteBuffer;)V
HSPLjava/security/MessageDigest;->update([B)V
-HSPLjava/security/MessageDigest;->update([BII)V+]Ljava/security/MessageDigest;Ljava/security/MessageDigest$Delegate;
+HSPLjava/security/MessageDigest;->update([BII)V
HSPLjava/security/MessageDigestSpi;-><init>()V
-HSPLjava/security/MessageDigestSpi;->engineDigest([BII)I+]Ljava/security/MessageDigestSpi;Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$SHA1;
+HSPLjava/security/MessageDigestSpi;->engineDigest([BII)I
HSPLjava/security/MessageDigestSpi;->engineUpdate(Ljava/nio/ByteBuffer;)V
HSPLjava/security/NoSuchAlgorithmException;-><init>(Ljava/lang/String;)V
HSPLjava/security/Provider$EngineDescription;->getConstructorParameterClass()Ljava/lang/Class;
@@ -4271,7 +4315,7 @@
HSPLjava/text/DecimalFormat;->initPattern(Ljava/lang/String;)V
HSPLjava/text/DecimalFormat;->isParseBigDecimal()Z
HSPLjava/text/DecimalFormat;->isParseIntegerOnly()Z
-HSPLjava/text/DecimalFormat;->parse(Ljava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;+]Ljava/lang/Object;Ljava/lang/Long;]Ljava/text/DecimalFormat;Ljava/text/DecimalFormat;]Landroid/icu/text/DecimalFormat;Landroid/icu/text/DecimalFormat;
+HSPLjava/text/DecimalFormat;->parse(Ljava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;
HSPLjava/text/DecimalFormat;->setDecimalSeparatorAlwaysShown(Z)V
HSPLjava/text/DecimalFormat;->setGroupingUsed(Z)V
HSPLjava/text/DecimalFormat;->setMaximumFractionDigits(I)V
@@ -4287,13 +4331,13 @@
HSPLjava/text/DecimalFormatSymbols;->getCurrency()Ljava/util/Currency;
HSPLjava/text/DecimalFormatSymbols;->getDecimalSeparator()C
HSPLjava/text/DecimalFormatSymbols;->getGroupingSeparator()C
-HSPLjava/text/DecimalFormatSymbols;->getIcuDecimalFormatSymbols()Landroid/icu/text/DecimalFormatSymbols;
+HSPLjava/text/DecimalFormatSymbols;->getIcuDecimalFormatSymbols()Landroid/icu/text/DecimalFormatSymbols;+]Ljava/text/DecimalFormatSymbols;Ljava/text/DecimalFormatSymbols;]Ljava/util/Currency;Ljava/util/Currency;]Landroid/icu/text/DecimalFormatSymbols;Landroid/icu/text/DecimalFormatSymbols;
HSPLjava/text/DecimalFormatSymbols;->getInfinity()Ljava/lang/String;
HSPLjava/text/DecimalFormatSymbols;->getInstance(Ljava/util/Locale;)Ljava/text/DecimalFormatSymbols;
HSPLjava/text/DecimalFormatSymbols;->getNaN()Ljava/lang/String;
HSPLjava/text/DecimalFormatSymbols;->getZeroDigit()C
HSPLjava/text/DecimalFormatSymbols;->initialize(Ljava/util/Locale;)V
-HSPLjava/text/DecimalFormatSymbols;->initializeCurrency(Ljava/util/Locale;)V+]Ljava/util/Currency;Ljava/util/Currency;]Ljava/util/Locale;Ljava/util/Locale;
+HSPLjava/text/DecimalFormatSymbols;->initializeCurrency(Ljava/util/Locale;)V
HSPLjava/text/DecimalFormatSymbols;->maybeStripMarkers(Ljava/lang/String;C)C
HSPLjava/text/DecimalFormatSymbols;->setCurrency(Ljava/util/Currency;)V
HSPLjava/text/DecimalFormatSymbols;->setCurrencySymbol(Ljava/lang/String;)V
@@ -4328,9 +4372,9 @@
HSPLjava/text/Format;->format(Ljava/lang/Object;)Ljava/lang/String;
HSPLjava/text/IcuIteratorWrapper;-><init>(Landroid/icu/text/BreakIterator;)V
HSPLjava/text/IcuIteratorWrapper;->checkOffset(ILjava/text/CharacterIterator;)V
-HSPLjava/text/IcuIteratorWrapper;->following(I)I+]Landroid/icu/text/BreakIterator;Landroid/icu/text/RuleBasedBreakIterator;]Ljava/text/IcuIteratorWrapper;Ljava/text/IcuIteratorWrapper;
+HSPLjava/text/IcuIteratorWrapper;->following(I)I
HSPLjava/text/IcuIteratorWrapper;->getText()Ljava/text/CharacterIterator;
-HSPLjava/text/IcuIteratorWrapper;->isBoundary(I)Z+]Landroid/icu/text/BreakIterator;Landroid/icu/text/RuleBasedBreakIterator;]Ljava/text/IcuIteratorWrapper;Ljava/text/IcuIteratorWrapper;
+HSPLjava/text/IcuIteratorWrapper;->isBoundary(I)Z
HSPLjava/text/IcuIteratorWrapper;->next()I
HSPLjava/text/IcuIteratorWrapper;->preceding(I)I
HSPLjava/text/IcuIteratorWrapper;->setText(Ljava/lang/String;)V
@@ -4350,7 +4394,6 @@
HSPLjava/text/NumberFormat;->format(J)Ljava/lang/String;
HSPLjava/text/NumberFormat;->getInstance()Ljava/text/NumberFormat;
HSPLjava/text/NumberFormat;->getInstance(Ljava/util/Locale;)Ljava/text/NumberFormat;
-HSPLjava/text/NumberFormat;->getInstance(Ljava/util/Locale;I)Ljava/text/NumberFormat;
HSPLjava/text/NumberFormat;->getIntegerInstance()Ljava/text/NumberFormat;
HSPLjava/text/NumberFormat;->getIntegerInstance(Ljava/util/Locale;)Ljava/text/NumberFormat;
HSPLjava/text/NumberFormat;->getNumberInstance(Ljava/util/Locale;)Ljava/text/NumberFormat;
@@ -4376,10 +4419,10 @@
HSPLjava/text/SimpleDateFormat;-><init>(Ljava/lang/String;)V
HSPLjava/text/SimpleDateFormat;-><init>(Ljava/lang/String;Ljava/util/Locale;)V
HSPLjava/text/SimpleDateFormat;->checkNegativeNumberExpression()V
-HSPLjava/text/SimpleDateFormat;->compile(Ljava/lang/String;)[C+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLjava/text/SimpleDateFormat;->compile(Ljava/lang/String;)[C
HSPLjava/text/SimpleDateFormat;->encode(IILjava/lang/StringBuilder;)V
HSPLjava/text/SimpleDateFormat;->format(Ljava/util/Date;Ljava/lang/StringBuffer;Ljava/text/FieldPosition;)Ljava/lang/StringBuffer;
-HSPLjava/text/SimpleDateFormat;->format(Ljava/util/Date;Ljava/lang/StringBuffer;Ljava/text/Format$FieldDelegate;)Ljava/lang/StringBuffer;
+HSPLjava/text/SimpleDateFormat;->format(Ljava/util/Date;Ljava/lang/StringBuffer;Ljava/text/Format$FieldDelegate;)Ljava/lang/StringBuffer;+]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;]Ljava/util/Calendar;Ljava/util/GregorianCalendar;
HSPLjava/text/SimpleDateFormat;->formatMonth(IIILjava/lang/StringBuffer;ZZII)Ljava/lang/String;
HSPLjava/text/SimpleDateFormat;->formatWeekday(IIZZ)Ljava/lang/String;
HSPLjava/text/SimpleDateFormat;->getDateTimeFormat(IILjava/util/Locale;)Ljava/lang/String;
@@ -4392,16 +4435,16 @@
HSPLjava/text/SimpleDateFormat;->matchString(Ljava/lang/String;II[Ljava/lang/String;Ljava/text/CalendarBuilder;)I
HSPLjava/text/SimpleDateFormat;->parse(Ljava/lang/String;Ljava/text/ParsePosition;)Ljava/util/Date;
HSPLjava/text/SimpleDateFormat;->parseAmbiguousDatesAsAfter(Ljava/util/Date;)V
-HSPLjava/text/SimpleDateFormat;->parseInternal(Ljava/lang/String;Ljava/text/ParsePosition;)Ljava/util/Date;+]Ljava/text/CalendarBuilder;Ljava/text/CalendarBuilder;]Ljava/util/Calendar;Ljava/util/GregorianCalendar;
+HSPLjava/text/SimpleDateFormat;->parseInternal(Ljava/lang/String;Ljava/text/ParsePosition;)Ljava/util/Date;
HSPLjava/text/SimpleDateFormat;->parseMonth(Ljava/lang/String;IIIILjava/text/ParsePosition;ZZLjava/text/CalendarBuilder;)I
HSPLjava/text/SimpleDateFormat;->parseWeekday(Ljava/lang/String;IIZZLjava/text/CalendarBuilder;)I
HSPLjava/text/SimpleDateFormat;->shouldObeyCount(II)Z
-HSPLjava/text/SimpleDateFormat;->subFormat(IILjava/text/Format$FieldDelegate;Ljava/lang/StringBuffer;Z)V
-HSPLjava/text/SimpleDateFormat;->subParse(Ljava/lang/String;IIIZ[ZLjava/text/ParsePosition;ZLjava/text/CalendarBuilder;)I+]Ljava/lang/String;Ljava/lang/String;]Ljava/text/ParsePosition;Ljava/text/ParsePosition;]Ljava/text/CalendarBuilder;Ljava/text/CalendarBuilder;]Ljava/lang/Number;Ljava/lang/Long;]Ljava/text/NumberFormat;Ljava/text/DecimalFormat;
+HSPLjava/text/SimpleDateFormat;->subFormat(IILjava/text/Format$FieldDelegate;Ljava/lang/StringBuffer;Z)V+]Ljava/text/Format$FieldDelegate;Ljava/text/FieldPosition$Delegate;,Ljava/text/DontCareFieldPosition$1;]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;]Ljava/util/Calendar;Ljava/util/GregorianCalendar;]Ljava/text/DateFormatSymbols;Ljava/text/DateFormatSymbols;
+HSPLjava/text/SimpleDateFormat;->subParse(Ljava/lang/String;IIIZ[ZLjava/text/ParsePosition;ZLjava/text/CalendarBuilder;)I
HSPLjava/text/SimpleDateFormat;->subParseNumericZone(Ljava/lang/String;IIIZLjava/text/CalendarBuilder;)I
HSPLjava/text/SimpleDateFormat;->toPattern()Ljava/lang/String;
-HSPLjava/text/SimpleDateFormat;->useDateFormatSymbols()Z+]Ljava/lang/Object;Ljava/util/GregorianCalendar;]Ljava/lang/Class;Ljava/lang/Class;
-HSPLjava/text/SimpleDateFormat;->zeroPaddingNumber(IIILjava/lang/StringBuffer;)V
+HSPLjava/text/SimpleDateFormat;->useDateFormatSymbols()Z
+HSPLjava/text/SimpleDateFormat;->zeroPaddingNumber(IIILjava/lang/StringBuffer;)V+]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;]Ljava/text/NumberFormat;Ljava/text/DecimalFormat;]Ljava/text/DecimalFormatSymbols;Ljava/text/DecimalFormatSymbols;]Ljava/text/DecimalFormat;Ljava/text/DecimalFormat;
HSPLjava/text/StringCharacterIterator;-><init>(Ljava/lang/String;)V
HSPLjava/text/StringCharacterIterator;-><init>(Ljava/lang/String;I)V
HSPLjava/text/StringCharacterIterator;-><init>(Ljava/lang/String;III)V
@@ -4451,6 +4494,7 @@
HSPLjava/time/Instant;->isAfter(Ljava/time/Instant;)Z
HSPLjava/time/Instant;->isSupported(Ljava/time/temporal/TemporalField;)Z
HSPLjava/time/Instant;->minus(JLjava/time/temporal/TemporalUnit;)Ljava/time/Instant;
+HSPLjava/time/Instant;->minusMillis(J)Ljava/time/Instant;
HSPLjava/time/Instant;->nanosUntil(Ljava/time/Instant;)J
HSPLjava/time/Instant;->now()Ljava/time/Instant;
HSPLjava/time/Instant;->ofEpochMilli(J)Ljava/time/Instant;
@@ -4508,7 +4552,7 @@
HSPLjava/time/LocalDateTime;->isBefore(Ljava/time/chrono/ChronoLocalDateTime;)Z
HSPLjava/time/LocalDateTime;->isSupported(Ljava/time/temporal/TemporalField;)Z
HSPLjava/time/LocalDateTime;->now()Ljava/time/LocalDateTime;
-HSPLjava/time/LocalDateTime;->now(Ljava/time/Clock;)Ljava/time/LocalDateTime;+]Ljava/time/Clock;Ljava/time/Clock$SystemClock;]Ljava/time/Instant;Ljava/time/Instant;]Ljava/time/ZoneId;Ljava/time/ZoneRegion;]Ljava/time/zone/ZoneRules;Ljava/time/zone/ZoneRules;
+HSPLjava/time/LocalDateTime;->now(Ljava/time/Clock;)Ljava/time/LocalDateTime;
HSPLjava/time/LocalDateTime;->of(Ljava/time/LocalDate;Ljava/time/LocalTime;)Ljava/time/LocalDateTime;
HSPLjava/time/LocalDateTime;->ofEpochSecond(JILjava/time/ZoneOffset;)Ljava/time/LocalDateTime;
HSPLjava/time/LocalDateTime;->ofInstant(Ljava/time/Instant;Ljava/time/ZoneId;)Ljava/time/LocalDateTime;
@@ -4594,7 +4638,7 @@
HSPLjava/time/chrono/ChronoZonedDateTime;->getChronology()Ljava/time/chrono/Chronology;
HSPLjava/time/chrono/ChronoZonedDateTime;->query(Ljava/time/temporal/TemporalQuery;)Ljava/lang/Object;
HSPLjava/time/chrono/ChronoZonedDateTime;->toEpochSecond()J+]Ljava/time/ZoneOffset;Ljava/time/ZoneOffset;]Ljava/time/LocalTime;Ljava/time/LocalTime;]Ljava/time/chrono/ChronoZonedDateTime;Ljava/time/ZonedDateTime;]Ljava/time/chrono/ChronoLocalDate;Ljava/time/LocalDate;
-HSPLjava/time/chrono/ChronoZonedDateTime;->toInstant()Ljava/time/Instant;+]Ljava/time/LocalTime;Ljava/time/LocalTime;]Ljava/time/chrono/ChronoZonedDateTime;Ljava/time/ZonedDateTime;
+HSPLjava/time/chrono/ChronoZonedDateTime;->toInstant()Ljava/time/Instant;
HSPLjava/time/chrono/IsoChronology;->isLeapYear(J)Z
HSPLjava/time/chrono/IsoChronology;->resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/LocalDate;
HSPLjava/time/chrono/IsoChronology;->resolveDate(Ljava/util/Map;Ljava/time/format/ResolverStyle;)Ljava/time/chrono/ChronoLocalDate;
@@ -4612,7 +4656,6 @@
HSPLjava/time/format/DateTimeFormatter;->parse(Ljava/lang/CharSequence;Ljava/time/temporal/TemporalQuery;)Ljava/lang/Object;
HSPLjava/time/format/DateTimeFormatter;->parseResolved0(Ljava/lang/CharSequence;Ljava/text/ParsePosition;)Ljava/time/temporal/TemporalAccessor;
HSPLjava/time/format/DateTimeFormatter;->parseUnresolved0(Ljava/lang/CharSequence;Ljava/text/ParsePosition;)Ljava/time/format/DateTimeParseContext;
-HSPLjava/time/format/DateTimeFormatterBuilder$3;-><clinit>()V
HSPLjava/time/format/DateTimeFormatterBuilder$CharLiteralPrinterParser;-><init>(C)V
HSPLjava/time/format/DateTimeFormatterBuilder$CharLiteralPrinterParser;->format(Ljava/time/format/DateTimePrintContext;Ljava/lang/StringBuilder;)Z
HSPLjava/time/format/DateTimeFormatterBuilder$CharLiteralPrinterParser;->parse(Ljava/time/format/DateTimeParseContext;Ljava/lang/CharSequence;I)I
@@ -4663,7 +4706,7 @@
HSPLjava/time/format/DateTimeParseContext;->toResolved(Ljava/time/format/ResolverStyle;Ljava/util/Set;)Ljava/time/temporal/TemporalAccessor;
HSPLjava/time/format/DateTimePrintContext;-><init>(Ljava/time/temporal/TemporalAccessor;Ljava/time/format/DateTimeFormatter;)V
HSPLjava/time/format/DateTimePrintContext;->adjust(Ljava/time/temporal/TemporalAccessor;Ljava/time/format/DateTimeFormatter;)Ljava/time/temporal/TemporalAccessor;
-HSPLjava/time/format/DateTimePrintContext;->getDecimalStyle()Ljava/time/format/DecimalStyle;+]Ljava/time/format/DateTimeFormatter;Ljava/time/format/DateTimeFormatter;
+HSPLjava/time/format/DateTimePrintContext;->getDecimalStyle()Ljava/time/format/DecimalStyle;
HSPLjava/time/format/DateTimePrintContext;->getValue(Ljava/time/temporal/TemporalField;)Ljava/lang/Long;
HSPLjava/time/format/DecimalStyle;->convertNumberToI18N(Ljava/lang/String;)Ljava/lang/String;
HSPLjava/time/format/DecimalStyle;->convertToDigit(C)I
@@ -4758,7 +4801,7 @@
HSPLjava/time/zone/ZoneRulesProvider;->getProvider(Ljava/lang/String;)Ljava/time/zone/ZoneRulesProvider;
HSPLjava/time/zone/ZoneRulesProvider;->getRules(Ljava/lang/String;Z)Ljava/time/zone/ZoneRules;
HSPLjava/util/AbstractCollection;-><init>()V
-HSPLjava/util/AbstractCollection;->addAll(Ljava/util/Collection;)Z+]Ljava/util/AbstractCollection;missing_types]Ljava/util/Collection;missing_types]Ljava/util/Iterator;missing_types
+HSPLjava/util/AbstractCollection;->addAll(Ljava/util/Collection;)Z
HSPLjava/util/AbstractCollection;->clear()V
HSPLjava/util/AbstractCollection;->contains(Ljava/lang/Object;)Z+]Ljava/lang/Object;missing_types]Ljava/util/Iterator;Ljava/util/AbstractList$Itr;
HSPLjava/util/AbstractCollection;->containsAll(Ljava/util/Collection;)Z
@@ -4766,8 +4809,8 @@
HSPLjava/util/AbstractCollection;->remove(Ljava/lang/Object;)Z
HSPLjava/util/AbstractCollection;->removeAll(Ljava/util/Collection;)Z
HSPLjava/util/AbstractCollection;->retainAll(Ljava/util/Collection;)Z
-HSPLjava/util/AbstractCollection;->toArray()[Ljava/lang/Object;+]Ljava/util/AbstractCollection;missing_types]Ljava/util/Iterator;megamorphic_types
-HSPLjava/util/AbstractCollection;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;+]Ljava/util/AbstractCollection;Ljava/util/ArrayList$SubList;,Lsun/security/jca/ProviderList$3;,Ljava/util/HashMap$Values;,Ljava/util/HashSet;]Ljava/lang/Object;missing_types]Ljava/lang/Class;Ljava/lang/Class;]Ljava/util/Iterator;Ljava/util/HashMap$KeyIterator;,Ljava/util/AbstractList$Itr;,Ljava/util/ArrayList$SubList$1;,Ljava/util/HashMap$ValueIterator;
+HSPLjava/util/AbstractCollection;->toArray()[Ljava/lang/Object;+]Ljava/util/AbstractCollection;Ljava/util/TreeMap$KeySet;]Ljava/util/Iterator;Ljava/util/TreeMap$KeyIterator;
+HSPLjava/util/AbstractCollection;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/AbstractCollection;->toString()Ljava/lang/String;
HSPLjava/util/AbstractList$Itr;-><init>(Ljava/util/AbstractList;)V
HSPLjava/util/AbstractList$Itr;-><init>(Ljava/util/AbstractList;Ljava/util/AbstractList$Itr-IA;)V
@@ -4802,8 +4845,8 @@
HSPLjava/util/AbstractList;-><init>()V
HSPLjava/util/AbstractList;->add(Ljava/lang/Object;)Z
HSPLjava/util/AbstractList;->clear()V
-HSPLjava/util/AbstractList;->equals(Ljava/lang/Object;)Z+]Ljava/lang/Object;missing_types]Ljava/util/ListIterator;Ljava/util/ArrayList$ListItr;,Ljava/util/Collections$UnmodifiableList$1;]Ljava/util/AbstractList;Ljava/util/ArrayList;]Ljava/util/List;Ljava/util/ArrayList;,Ljava/util/Collections$UnmodifiableRandomAccessList;
-HSPLjava/util/AbstractList;->hashCode()I+]Ljava/lang/Object;missing_types]Ljava/util/AbstractList;Ljava/util/Collections$SingletonList;,Ljava/util/ArrayList;]Ljava/util/Iterator;Ljava/util/ArrayList$Itr;,Ljava/util/Collections$1;
+HSPLjava/util/AbstractList;->equals(Ljava/lang/Object;)Z
+HSPLjava/util/AbstractList;->hashCode()I
HSPLjava/util/AbstractList;->indexOf(Ljava/lang/Object;)I
HSPLjava/util/AbstractList;->iterator()Ljava/util/Iterator;
HSPLjava/util/AbstractList;->listIterator()Ljava/util/ListIterator;
@@ -4829,9 +4872,9 @@
HSPLjava/util/AbstractMap;->clear()V
HSPLjava/util/AbstractMap;->clone()Ljava/lang/Object;
HSPLjava/util/AbstractMap;->eq(Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLjava/util/AbstractMap;->equals(Ljava/lang/Object;)Z+]Ljava/util/Map$Entry;Ljava/util/HashMap$Node;]Ljava/util/AbstractMap;Ljava/util/HashMap;]Ljava/util/Map;Ljava/util/HashMap;]Ljava/util/Iterator;Ljava/util/HashMap$EntryIterator;]Ljava/util/Set;Ljava/util/HashMap$EntrySet;
-HSPLjava/util/AbstractMap;->get(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/Map$Entry;Ljava/util/AbstractMap$SimpleImmutableEntry;]Ljava/lang/Object;Ljava/lang/Boolean;]Ljava/util/Iterator;Ljava/util/Collections$UnmodifiableCollection$1;
-HSPLjava/util/AbstractMap;->hashCode()I+]Ljava/util/Map$Entry;Ljava/util/LinkedHashMap$LinkedHashMapEntry;,Ljava/util/HashMap$Node;]Ljava/util/AbstractMap;Ljava/util/HashMap;,Ljava/util/LinkedHashMap;]Ljava/util/Iterator;Ljava/util/HashMap$EntryIterator;,Ljava/util/LinkedHashMap$LinkedEntryIterator;]Ljava/util/Set;Ljava/util/LinkedHashMap$LinkedEntrySet;,Ljava/util/HashMap$EntrySet;
+HSPLjava/util/AbstractMap;->equals(Ljava/lang/Object;)Z+]Ljava/util/Map$Entry;Ljava/util/HashMap$Node;]Ljava/lang/Object;Ljava/lang/Integer;]Ljava/util/AbstractMap;Ljava/util/HashMap;]Ljava/util/Map;Ljava/util/HashMap;]Ljava/util/Iterator;Ljava/util/HashMap$EntryIterator;]Ljava/util/Set;Ljava/util/HashMap$EntrySet;
+HSPLjava/util/AbstractMap;->get(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLjava/util/AbstractMap;->hashCode()I
HSPLjava/util/AbstractMap;->isEmpty()Z
HSPLjava/util/AbstractMap;->putAll(Ljava/util/Map;)V
HSPLjava/util/AbstractMap;->size()I
@@ -4839,39 +4882,44 @@
HSPLjava/util/AbstractMap;->values()Ljava/util/Collection;
HSPLjava/util/AbstractQueue;-><init>()V
HSPLjava/util/AbstractQueue;->add(Ljava/lang/Object;)Z
-HSPLjava/util/AbstractQueue;->addAll(Ljava/util/Collection;)Z
+HSPLjava/util/AbstractQueue;->addAll(Ljava/util/Collection;)Z+]Ljava/util/Collection;Ljava/util/TreeMap$KeySet;]Ljava/util/Iterator;Ljava/util/TreeMap$KeyIterator;]Ljava/util/AbstractQueue;Ljava/util/PriorityQueue;
HSPLjava/util/AbstractQueue;->clear()V
HSPLjava/util/AbstractQueue;->remove()Ljava/lang/Object;
HSPLjava/util/AbstractSequentialList;-><init>()V
HSPLjava/util/AbstractSequentialList;->iterator()Ljava/util/Iterator;
HSPLjava/util/AbstractSet;-><init>()V
HSPLjava/util/AbstractSet;->equals(Ljava/lang/Object;)Z
-HSPLjava/util/AbstractSet;->hashCode()I+]Ljava/lang/Object;missing_types]Ljava/util/AbstractSet;Ljava/util/HashSet;,Ljava/util/LinkedHashSet;]Ljava/util/Iterator;Ljava/util/HashMap$KeyIterator;,Ljava/util/LinkedHashMap$LinkedKeyIterator;
-HSPLjava/util/AbstractSet;->removeAll(Ljava/util/Collection;)Z+]Ljava/util/Collection;missing_types]Ljava/util/AbstractSet;Ljava/util/TreeSet;,Ljava/util/HashSet;]Ljava/util/Iterator;missing_types
+HSPLjava/util/AbstractSet;->hashCode()I
+HSPLjava/util/AbstractSet;->removeAll(Ljava/util/Collection;)Z
+HSPLjava/util/ArrayDeque$$ExternalSyntheticLambda1;-><init>(Ljava/util/ArrayDeque;)V
+HSPLjava/util/ArrayDeque$$ExternalSyntheticLambda1;->accept(Ljava/lang/Object;)V+]Ljava/util/ArrayDeque;Ljava/util/ArrayDeque;
HSPLjava/util/ArrayDeque$DeqIterator;-><init>(Ljava/util/ArrayDeque;)V
-HSPLjava/util/ArrayDeque$DeqIterator;-><init>(Ljava/util/ArrayDeque;Ljava/util/ArrayDeque$DeqIterator-IA;)V
HSPLjava/util/ArrayDeque$DeqIterator;->hasNext()Z
HSPLjava/util/ArrayDeque$DeqIterator;->next()Ljava/lang/Object;
+HSPLjava/util/ArrayDeque$DeqIterator;->postDelete(Z)V
HSPLjava/util/ArrayDeque$DeqIterator;->remove()V
HSPLjava/util/ArrayDeque$DescendingIterator;-><init>(Ljava/util/ArrayDeque;)V
-HSPLjava/util/ArrayDeque$DescendingIterator;-><init>(Ljava/util/ArrayDeque;Ljava/util/ArrayDeque$DescendingIterator-IA;)V
-HSPLjava/util/ArrayDeque$DescendingIterator;->hasNext()Z
HSPLjava/util/ArrayDeque$DescendingIterator;->next()Ljava/lang/Object;
HSPLjava/util/ArrayDeque;-><init>()V
HSPLjava/util/ArrayDeque;-><init>(I)V
HSPLjava/util/ArrayDeque;-><init>(Ljava/util/Collection;)V
HSPLjava/util/ArrayDeque;->add(Ljava/lang/Object;)Z+]Ljava/util/ArrayDeque;Ljava/util/ArrayDeque;
+HSPLjava/util/ArrayDeque;->addAll(Ljava/util/Collection;)Z+]Ljava/util/ArrayDeque;Ljava/util/ArrayDeque;]Ljava/util/Collection;missing_types
HSPLjava/util/ArrayDeque;->addFirst(Ljava/lang/Object;)V
HSPLjava/util/ArrayDeque;->addLast(Ljava/lang/Object;)V
-HSPLjava/util/ArrayDeque;->allocateElements(I)V
HSPLjava/util/ArrayDeque;->checkInvariants()V
+HSPLjava/util/ArrayDeque;->circularClear([Ljava/lang/Object;II)V
HSPLjava/util/ArrayDeque;->clear()V
HSPLjava/util/ArrayDeque;->contains(Ljava/lang/Object;)Z
+HSPLjava/util/ArrayDeque;->copyElements(Ljava/util/Collection;)V+]Ljava/util/Collection;missing_types
+HSPLjava/util/ArrayDeque;->dec(II)I
HSPLjava/util/ArrayDeque;->delete(I)Z
HSPLjava/util/ArrayDeque;->descendingIterator()Ljava/util/Iterator;
-HSPLjava/util/ArrayDeque;->doubleCapacity()V
+HSPLjava/util/ArrayDeque;->elementAt([Ljava/lang/Object;I)Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->getFirst()Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->getLast()Ljava/lang/Object;
+HSPLjava/util/ArrayDeque;->grow(I)V
+HSPLjava/util/ArrayDeque;->inc(II)I
HSPLjava/util/ArrayDeque;->isEmpty()Z
HSPLjava/util/ArrayDeque;->iterator()Ljava/util/Iterator;
HSPLjava/util/ArrayDeque;->offer(Ljava/lang/Object;)Z
@@ -4884,78 +4932,100 @@
HSPLjava/util/ArrayDeque;->pollLast()Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->pop()Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->push(Ljava/lang/Object;)V
-HSPLjava/util/ArrayDeque;->remove()Ljava/lang/Object;+]Ljava/util/ArrayDeque;Ljava/util/ArrayDeque;
+HSPLjava/util/ArrayDeque;->remove()Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->remove(Ljava/lang/Object;)Z
-HSPLjava/util/ArrayDeque;->removeFirst()Ljava/lang/Object;+]Ljava/util/ArrayDeque;Ljava/util/ArrayDeque;
+HSPLjava/util/ArrayDeque;->removeFirst()Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->removeFirstOccurrence(Ljava/lang/Object;)Z
HSPLjava/util/ArrayDeque;->removeLast()Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->size()I
+HSPLjava/util/ArrayDeque;->sub(III)I
HSPLjava/util/ArrayDeque;->toArray()[Ljava/lang/Object;
+HSPLjava/util/ArrayDeque;->toArray(Ljava/lang/Class;)[Ljava/lang/Object;
HSPLjava/util/ArrayDeque;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/ArrayList$ArrayListSpliterator;-><init>(Ljava/util/ArrayList;III)V
HSPLjava/util/ArrayList$ArrayListSpliterator;->characteristics()I
HSPLjava/util/ArrayList$ArrayListSpliterator;->estimateSize()J
HSPLjava/util/ArrayList$ArrayListSpliterator;->forEachRemaining(Ljava/util/function/Consumer;)V
HSPLjava/util/ArrayList$ArrayListSpliterator;->getFence()I
-HSPLjava/util/ArrayList$ArrayListSpliterator;->tryAdvance(Ljava/util/function/Consumer;)Z+]Ljava/util/function/Consumer;Ljava/util/stream/ReferencePipeline$2$1;,Ljava/util/stream/MatchOps$1MatchSink;
+HSPLjava/util/ArrayList$ArrayListSpliterator;->tryAdvance(Ljava/util/function/Consumer;)Z
HSPLjava/util/ArrayList$Itr;-><init>(Ljava/util/ArrayList;)V
-HSPLjava/util/ArrayList$Itr;-><init>(Ljava/util/ArrayList;Ljava/util/ArrayList$Itr-IA;)V
+HSPLjava/util/ArrayList$Itr;->checkForComodification()V
HSPLjava/util/ArrayList$Itr;->hasNext()Z
-HSPLjava/util/ArrayList$Itr;->next()Ljava/lang/Object;
+HSPLjava/util/ArrayList$Itr;->next()Ljava/lang/Object;+]Ljava/util/ArrayList$Itr;Ljava/util/ArrayList$ListItr;,Ljava/util/ArrayList$Itr;
HSPLjava/util/ArrayList$Itr;->remove()V
HSPLjava/util/ArrayList$ListItr;-><init>(Ljava/util/ArrayList;I)V
HSPLjava/util/ArrayList$ListItr;->hasPrevious()Z
HSPLjava/util/ArrayList$ListItr;->nextIndex()I
HSPLjava/util/ArrayList$ListItr;->previous()Ljava/lang/Object;
HSPLjava/util/ArrayList$ListItr;->set(Ljava/lang/Object;)V
-HSPLjava/util/ArrayList$SubList$1;-><init>(Ljava/util/ArrayList$SubList;II)V
+HSPLjava/util/ArrayList$SubList$1;-><init>(Ljava/util/ArrayList$SubList;I)V
+HSPLjava/util/ArrayList$SubList$1;->checkForComodification()V
HSPLjava/util/ArrayList$SubList$1;->hasNext()Z
HSPLjava/util/ArrayList$SubList$1;->next()Ljava/lang/Object;
-HSPLjava/util/ArrayList$SubList;-><init>(Ljava/util/ArrayList;Ljava/util/AbstractList;III)V
+HSPLjava/util/ArrayList$SubList;->-$$Nest$fgetoffset(Ljava/util/ArrayList$SubList;)I
+HSPLjava/util/ArrayList$SubList;->-$$Nest$fgetroot(Ljava/util/ArrayList$SubList;)Ljava/util/ArrayList;
+HSPLjava/util/ArrayList$SubList;->-$$Nest$fgetsize(Ljava/util/ArrayList$SubList;)I
+HSPLjava/util/ArrayList$SubList;-><init>(Ljava/util/ArrayList;II)V
+HSPLjava/util/ArrayList$SubList;->checkForComodification()V
HSPLjava/util/ArrayList$SubList;->get(I)Ljava/lang/Object;
HSPLjava/util/ArrayList$SubList;->iterator()Ljava/util/Iterator;
HSPLjava/util/ArrayList$SubList;->listIterator(I)Ljava/util/ListIterator;
+HSPLjava/util/ArrayList$SubList;->rangeCheckForAdd(I)V
HSPLjava/util/ArrayList$SubList;->removeRange(II)V
HSPLjava/util/ArrayList$SubList;->size()I
HSPLjava/util/ArrayList$SubList;->subList(II)Ljava/util/List;
+HSPLjava/util/ArrayList$SubList;->toArray()[Ljava/lang/Object;
+HSPLjava/util/ArrayList$SubList;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/ArrayList;->-$$Nest$fgetsize(Ljava/util/ArrayList;)I
HSPLjava/util/ArrayList;-><init>()V
HSPLjava/util/ArrayList;-><init>(I)V
-HSPLjava/util/ArrayList;-><init>(Ljava/util/Collection;)V+]Ljava/lang/Object;missing_types]Ljava/util/Collection;megamorphic_types
+HSPLjava/util/ArrayList;-><init>(Ljava/util/Collection;)V+]Ljava/lang/Object;Ljava/util/ArrayList;]Ljava/util/Collection;Ljava/util/HashMap$Values;,Ljava/util/ArrayList;
HSPLjava/util/ArrayList;->add(ILjava/lang/Object;)V
HSPLjava/util/ArrayList;->add(Ljava/lang/Object;)Z
+HSPLjava/util/ArrayList;->add(Ljava/lang/Object;[Ljava/lang/Object;I)V
HSPLjava/util/ArrayList;->addAll(ILjava/util/Collection;)Z
HSPLjava/util/ArrayList;->addAll(Ljava/util/Collection;)Z+]Ljava/util/Collection;missing_types
-HSPLjava/util/ArrayList;->batchRemove(Ljava/util/Collection;Z)Z
+HSPLjava/util/ArrayList;->batchRemove(Ljava/util/Collection;ZII)Z+]Ljava/util/Collection;Ljava/util/ArrayList;
+HSPLjava/util/ArrayList;->checkForComodification(I)V
HSPLjava/util/ArrayList;->clear()V
HSPLjava/util/ArrayList;->clone()Ljava/lang/Object;
HSPLjava/util/ArrayList;->contains(Ljava/lang/Object;)Z+]Ljava/util/ArrayList;Ljava/util/ArrayList;
+HSPLjava/util/ArrayList;->elementAt([Ljava/lang/Object;I)Ljava/lang/Object;
+HSPLjava/util/ArrayList;->elementData(I)Ljava/lang/Object;
HSPLjava/util/ArrayList;->ensureCapacity(I)V
-HSPLjava/util/ArrayList;->ensureCapacityInternal(I)V
-HSPLjava/util/ArrayList;->ensureExplicitCapacity(I)V
-HSPLjava/util/ArrayList;->fastRemove(I)V
+HSPLjava/util/ArrayList;->equals(Ljava/lang/Object;)Z
+HSPLjava/util/ArrayList;->equalsArrayList(Ljava/util/ArrayList;)Z
+HSPLjava/util/ArrayList;->equalsRange(Ljava/util/List;II)Z+]Ljava/util/List;missing_types]Ljava/util/Iterator;missing_types
+HSPLjava/util/ArrayList;->fastRemove([Ljava/lang/Object;I)V
HSPLjava/util/ArrayList;->forEach(Ljava/util/function/Consumer;)V
-HSPLjava/util/ArrayList;->get(I)Ljava/lang/Object;
-HSPLjava/util/ArrayList;->grow(I)V
-HSPLjava/util/ArrayList;->indexOf(Ljava/lang/Object;)I+]Ljava/lang/Object;missing_types
+HSPLjava/util/ArrayList;->get(I)Ljava/lang/Object;+]Ljava/util/ArrayList;missing_types
+HSPLjava/util/ArrayList;->grow()[Ljava/lang/Object;
+HSPLjava/util/ArrayList;->grow(I)[Ljava/lang/Object;
+HSPLjava/util/ArrayList;->hashCode()I
+HSPLjava/util/ArrayList;->hashCodeRange(II)I
+HSPLjava/util/ArrayList;->indexOf(Ljava/lang/Object;)I+]Ljava/util/ArrayList;Ljava/util/ArrayList;
+HSPLjava/util/ArrayList;->indexOfRange(Ljava/lang/Object;II)I+]Ljava/lang/Object;Ljava/lang/String;,Ljava/lang/Integer;
HSPLjava/util/ArrayList;->isEmpty()Z
HSPLjava/util/ArrayList;->iterator()Ljava/util/Iterator;
HSPLjava/util/ArrayList;->lastIndexOf(Ljava/lang/Object;)I
HSPLjava/util/ArrayList;->listIterator()Ljava/util/ListIterator;
HSPLjava/util/ArrayList;->listIterator(I)Ljava/util/ListIterator;
+HSPLjava/util/ArrayList;->newCapacity(I)I
+HSPLjava/util/ArrayList;->rangeCheckForAdd(I)V
HSPLjava/util/ArrayList;->readObject(Ljava/io/ObjectInputStream;)V
HSPLjava/util/ArrayList;->remove(I)Ljava/lang/Object;
HSPLjava/util/ArrayList;->remove(Ljava/lang/Object;)Z
HSPLjava/util/ArrayList;->removeAll(Ljava/util/Collection;)Z
HSPLjava/util/ArrayList;->removeIf(Ljava/util/function/Predicate;)Z
+HSPLjava/util/ArrayList;->removeIf(Ljava/util/function/Predicate;II)Z+]Ljava/util/function/Predicate;missing_types
HSPLjava/util/ArrayList;->removeRange(II)V
HSPLjava/util/ArrayList;->retainAll(Ljava/util/Collection;)Z
HSPLjava/util/ArrayList;->set(ILjava/lang/Object;)Ljava/lang/Object;
+HSPLjava/util/ArrayList;->shiftTailOverGap([Ljava/lang/Object;II)V
HSPLjava/util/ArrayList;->size()I
HSPLjava/util/ArrayList;->sort(Ljava/util/Comparator;)V
HSPLjava/util/ArrayList;->spliterator()Ljava/util/Spliterator;
HSPLjava/util/ArrayList;->subList(II)Ljava/util/List;
-HSPLjava/util/ArrayList;->subListRangeCheck(III)V
HSPLjava/util/ArrayList;->toArray()[Ljava/lang/Object;
HSPLjava/util/ArrayList;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;+]Ljava/lang/Object;missing_types
HSPLjava/util/ArrayList;->trimToSize()V
@@ -4967,7 +5037,7 @@
HSPLjava/util/Arrays$ArrayList;->contains(Ljava/lang/Object;)Z
HSPLjava/util/Arrays$ArrayList;->forEach(Ljava/util/function/Consumer;)V
HSPLjava/util/Arrays$ArrayList;->get(I)Ljava/lang/Object;
-HSPLjava/util/Arrays$ArrayList;->indexOf(Ljava/lang/Object;)I
+HSPLjava/util/Arrays$ArrayList;->indexOf(Ljava/lang/Object;)I+]Ljava/lang/Object;missing_types
HSPLjava/util/Arrays$ArrayList;->iterator()Ljava/util/Iterator;
HSPLjava/util/Arrays$ArrayList;->set(ILjava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/Arrays$ArrayList;->size()I
@@ -5052,7 +5122,7 @@
HSPLjava/util/Arrays;->toString([F)Ljava/lang/String;
HSPLjava/util/Arrays;->toString([I)Ljava/lang/String;
HSPLjava/util/Arrays;->toString([J)Ljava/lang/String;
-HSPLjava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;
+HSPLjava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/util/Base64$Decoder;->decode(Ljava/lang/String;)[B
HSPLjava/util/Base64$Decoder;->decode([B)[B
HSPLjava/util/Base64$Decoder;->decode0([BII[B)I
@@ -5099,9 +5169,10 @@
HSPLjava/util/Calendar;->clone()Ljava/lang/Object;
HSPLjava/util/Calendar;->compareTo(J)I
HSPLjava/util/Calendar;->compareTo(Ljava/util/Calendar;)I
-HSPLjava/util/Calendar;->complete()V
+HSPLjava/util/Calendar;->complete()V+]Ljava/util/Calendar;Ljava/util/GregorianCalendar;
HSPLjava/util/Calendar;->createCalendar(Ljava/util/TimeZone;Ljava/util/Locale;)Ljava/util/Calendar;
-HSPLjava/util/Calendar;->get(I)I
+HSPLjava/util/Calendar;->defaultTimeZone(Ljava/util/Locale;)Ljava/util/TimeZone;
+HSPLjava/util/Calendar;->get(I)I+]Ljava/util/Calendar;Ljava/util/GregorianCalendar;
HSPLjava/util/Calendar;->getFirstDayOfWeek()I
HSPLjava/util/Calendar;->getInstance()Ljava/util/Calendar;
HSPLjava/util/Calendar;->getInstance(Ljava/util/Locale;)Ljava/util/Calendar;
@@ -5123,7 +5194,7 @@
HSPLjava/util/Calendar;->isPartiallyNormalized()Z
HSPLjava/util/Calendar;->isSet(I)Z
HSPLjava/util/Calendar;->selectFields()I
-HSPLjava/util/Calendar;->set(II)V+]Ljava/util/Calendar;Ljava/util/GregorianCalendar;
+HSPLjava/util/Calendar;->set(II)V
HSPLjava/util/Calendar;->set(III)V
HSPLjava/util/Calendar;->set(IIIIII)V
HSPLjava/util/Calendar;->setFieldsComputed(I)V
@@ -5135,7 +5206,7 @@
HSPLjava/util/Calendar;->setWeekCountData(Ljava/util/Locale;)V
HSPLjava/util/Calendar;->setZoneShared(Z)V
HSPLjava/util/Calendar;->updateTime()V
-HSPLjava/util/Collection;->removeIf(Ljava/util/function/Predicate;)Z+]Ljava/util/Collection;Landroid/util/MapCollections$ValuesCollection;,Ljava/util/HashMap$EntrySet;,Landroid/util/MapCollections$KeySet;,Ljava/util/LinkedList;,Lcom/android/internal/telephony/data/DataNetworkController$NetworkRequestList;,Landroid/util/MapCollections$EntrySet;,Ljava/util/HashSet;]Ljava/util/Iterator;Landroid/util/MapCollections$ArrayIterator;,Ljava/util/HashMap$EntryIterator;,Ljava/util/LinkedList$ListItr;,Landroid/util/MapCollections$MapIterator;,Ljava/util/HashMap$KeyIterator;]Ljava/util/function/Predicate;Lcom/android/internal/telephony/data/DataNetworkController$$ExternalSyntheticLambda24;,Lcom/android/internal/telephony/data/DataNetworkController$$ExternalSyntheticLambda15;
+HSPLjava/util/Collection;->removeIf(Ljava/util/function/Predicate;)Z+]Ljava/util/Collection;Landroid/util/MapCollections$ValuesCollection;,Landroid/util/MapCollections$KeySet;]Ljava/util/Iterator;Landroid/util/MapCollections$ArrayIterator;
HSPLjava/util/Collection;->spliterator()Ljava/util/Spliterator;
HSPLjava/util/Collection;->stream()Ljava/util/stream/Stream;+]Ljava/util/Collection;megamorphic_types
HSPLjava/util/Collections$1;-><init>(Ljava/lang/Object;)V
@@ -5152,7 +5223,7 @@
HSPLjava/util/Collections$EmptyIterator;->hasNext()Z
HSPLjava/util/Collections$EmptyList;->contains(Ljava/lang/Object;)Z
HSPLjava/util/Collections$EmptyList;->containsAll(Ljava/util/Collection;)Z
-HSPLjava/util/Collections$EmptyList;->equals(Ljava/lang/Object;)Z+]Ljava/util/List;Ljava/util/Collections$EmptyList;,Ljava/util/ArrayList;
+HSPLjava/util/Collections$EmptyList;->equals(Ljava/lang/Object;)Z
HSPLjava/util/Collections$EmptyList;->isEmpty()Z
HSPLjava/util/Collections$EmptyList;->iterator()Ljava/util/Iterator;
HSPLjava/util/Collections$EmptyList;->listIterator()Ljava/util/ListIterator;
@@ -5179,10 +5250,10 @@
HSPLjava/util/Collections$EmptySet;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/Collections$ReverseComparator2;-><init>(Ljava/util/Comparator;)V
HSPLjava/util/Collections$ReverseComparator2;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
-HSPLjava/util/Collections$ReverseComparator;->compare(Ljava/lang/Comparable;Ljava/lang/Comparable;)I
-HSPLjava/util/Collections$ReverseComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
+HSPLjava/util/Collections$ReverseComparator;->compare(Ljava/lang/Comparable;Ljava/lang/Comparable;)I+]Ljava/lang/Comparable;Ljava/lang/String;
+HSPLjava/util/Collections$ReverseComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I+]Ljava/util/Collections$ReverseComparator;Ljava/util/Collections$ReverseComparator;
HSPLjava/util/Collections$SetFromMap;-><init>(Ljava/util/Map;)V
-HSPLjava/util/Collections$SetFromMap;->add(Ljava/lang/Object;)Z+]Ljava/util/Map;Ljava/util/WeakHashMap;,Ljava/util/concurrent/ConcurrentHashMap;,Ljava/util/IdentityHashMap;
+HSPLjava/util/Collections$SetFromMap;->add(Ljava/lang/Object;)Z+]Ljava/util/Map;Ljava/util/WeakHashMap;
HSPLjava/util/Collections$SetFromMap;->clear()V
HSPLjava/util/Collections$SetFromMap;->contains(Ljava/lang/Object;)Z
HSPLjava/util/Collections$SetFromMap;->forEach(Ljava/util/function/Consumer;)V
@@ -5195,6 +5266,7 @@
HSPLjava/util/Collections$SingletonList;-><init>(Ljava/lang/Object;)V
HSPLjava/util/Collections$SingletonList;->contains(Ljava/lang/Object;)Z
HSPLjava/util/Collections$SingletonList;->get(I)Ljava/lang/Object;
+HSPLjava/util/Collections$SingletonList;->hashCode()I
HSPLjava/util/Collections$SingletonList;->iterator()Ljava/util/Iterator;
HSPLjava/util/Collections$SingletonList;->size()I
HSPLjava/util/Collections$SingletonMap;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -5211,7 +5283,7 @@
HSPLjava/util/Collections$SingletonSet;->size()I
HSPLjava/util/Collections$SynchronizedCollection;-><init>(Ljava/util/Collection;)V
HSPLjava/util/Collections$SynchronizedCollection;-><init>(Ljava/util/Collection;Ljava/lang/Object;)V
-HSPLjava/util/Collections$SynchronizedCollection;->add(Ljava/lang/Object;)Z
+HSPLjava/util/Collections$SynchronizedCollection;->add(Ljava/lang/Object;)Z+]Ljava/util/Collection;Ljava/util/Collections$SetFromMap;
HSPLjava/util/Collections$SynchronizedCollection;->addAll(Ljava/util/Collection;)Z
HSPLjava/util/Collections$SynchronizedCollection;->clear()V
HSPLjava/util/Collections$SynchronizedCollection;->contains(Ljava/lang/Object;)Z
@@ -5222,6 +5294,7 @@
HSPLjava/util/Collections$SynchronizedCollection;->size()I
HSPLjava/util/Collections$SynchronizedCollection;->toArray()[Ljava/lang/Object;
HSPLjava/util/Collections$SynchronizedCollection;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
+HSPLjava/util/Collections$SynchronizedCollection;->toString()Ljava/lang/String;
HSPLjava/util/Collections$SynchronizedList;-><init>(Ljava/util/List;)V
HSPLjava/util/Collections$SynchronizedList;->get(I)Ljava/lang/Object;
HSPLjava/util/Collections$SynchronizedMap;-><init>(Ljava/util/Map;)V
@@ -5233,7 +5306,7 @@
HSPLjava/util/Collections$SynchronizedMap;->isEmpty()Z
HSPLjava/util/Collections$SynchronizedMap;->keySet()Ljava/util/Set;
HSPLjava/util/Collections$SynchronizedMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLjava/util/Collections$SynchronizedMap;->putAll(Ljava/util/Map;)V+]Ljava/util/Map;Ljava/util/HashMap;
+HSPLjava/util/Collections$SynchronizedMap;->putAll(Ljava/util/Map;)V
HSPLjava/util/Collections$SynchronizedMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/Collections$SynchronizedMap;->size()I
HSPLjava/util/Collections$SynchronizedMap;->values()Ljava/util/Collection;
@@ -5243,9 +5316,9 @@
HSPLjava/util/Collections$SynchronizedSet;->equals(Ljava/lang/Object;)Z
HSPLjava/util/Collections$UnmodifiableCollection$1;-><init>(Ljava/util/Collections$UnmodifiableCollection;)V
HSPLjava/util/Collections$UnmodifiableCollection$1;->hasNext()Z+]Ljava/util/Iterator;missing_types
-HSPLjava/util/Collections$UnmodifiableCollection$1;->next()Ljava/lang/Object;+]Ljava/util/Iterator;missing_types
+HSPLjava/util/Collections$UnmodifiableCollection$1;->next()Ljava/lang/Object;+]Ljava/util/Iterator;megamorphic_types
HSPLjava/util/Collections$UnmodifiableCollection;-><init>(Ljava/util/Collection;)V
-HSPLjava/util/Collections$UnmodifiableCollection;->contains(Ljava/lang/Object;)Z+]Ljava/util/Collection;Ljava/util/HashSet;,Ljava/util/RegularEnumSet;,Ljava/util/ArrayList;,Ljava/util/LinkedHashSet;
+HSPLjava/util/Collections$UnmodifiableCollection;->contains(Ljava/lang/Object;)Z+]Ljava/util/Collection;megamorphic_types
HSPLjava/util/Collections$UnmodifiableCollection;->containsAll(Ljava/util/Collection;)Z
HSPLjava/util/Collections$UnmodifiableCollection;->forEach(Ljava/util/function/Consumer;)V
HSPLjava/util/Collections$UnmodifiableCollection;->isEmpty()Z
@@ -5266,13 +5339,13 @@
HSPLjava/util/Collections$UnmodifiableList;->indexOf(Ljava/lang/Object;)I
HSPLjava/util/Collections$UnmodifiableList;->listIterator()Ljava/util/ListIterator;
HSPLjava/util/Collections$UnmodifiableList;->listIterator(I)Ljava/util/ListIterator;
-HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;-><init>(Ljava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet;)V
-HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;->hasNext()Z
-HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;->next()Ljava/lang/Object;
-HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;->next()Ljava/util/Map$Entry;
+HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;-><init>(Ljava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet;)V+]Ljava/util/Collection;Ljava/util/LinkedHashMap$LinkedEntrySet;,Ljava/util/Collections$EmptySet;
+HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;->hasNext()Z+]Ljava/util/Iterator;Ljava/util/Collections$EmptyIterator;,Ljava/util/LinkedHashMap$LinkedEntryIterator;
+HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;->next()Ljava/lang/Object;+]Ljava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;Ljava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;
+HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;->next()Ljava/util/Map$Entry;+]Ljava/util/Iterator;Ljava/util/LinkedHashMap$LinkedEntryIterator;
HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry;-><init>(Ljava/util/Map$Entry;)V
-HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry;->getKey()Ljava/lang/Object;
-HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry;->getValue()Ljava/lang/Object;
+HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry;->getKey()Ljava/lang/Object;+]Ljava/util/Map$Entry;Ljava/util/LinkedHashMap$LinkedHashMapEntry;
+HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry;->getValue()Ljava/lang/Object;+]Ljava/util/Map$Entry;Ljava/util/LinkedHashMap$LinkedHashMapEntry;
HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet;-><init>(Ljava/util/Set;)V
HSPLjava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet;->iterator()Ljava/util/Iterator;
HSPLjava/util/Collections$UnmodifiableMap;-><init>(Ljava/util/Map;)V
@@ -5284,7 +5357,7 @@
HSPLjava/util/Collections$UnmodifiableMap;->hashCode()I
HSPLjava/util/Collections$UnmodifiableMap;->isEmpty()Z
HSPLjava/util/Collections$UnmodifiableMap;->keySet()Ljava/util/Set;
-HSPLjava/util/Collections$UnmodifiableMap;->size()I
+HSPLjava/util/Collections$UnmodifiableMap;->size()I+]Ljava/util/Map;missing_types
HSPLjava/util/Collections$UnmodifiableMap;->toString()Ljava/lang/String;
HSPLjava/util/Collections$UnmodifiableMap;->values()Ljava/util/Collection;
HSPLjava/util/Collections$UnmodifiableRandomAccessList;-><init>(Ljava/util/List;)V
@@ -5293,10 +5366,10 @@
HSPLjava/util/Collections$UnmodifiableSet;->equals(Ljava/lang/Object;)Z
HSPLjava/util/Collections$UnmodifiableSortedMap;-><init>(Ljava/util/SortedMap;)V
HSPLjava/util/Collections$UnmodifiableSortedSet;-><init>(Ljava/util/SortedSet;)V
-HSPLjava/util/Collections;->addAll(Ljava/util/Collection;[Ljava/lang/Object;)Z
+HSPLjava/util/Collections;->addAll(Ljava/util/Collection;[Ljava/lang/Object;)Z+]Ljava/util/Collection;Ljava/util/ArrayList;
HSPLjava/util/Collections;->binarySearch(Ljava/util/List;Ljava/lang/Object;)I
HSPLjava/util/Collections;->binarySearch(Ljava/util/List;Ljava/lang/Object;Ljava/util/Comparator;)I
-HSPLjava/util/Collections;->disjoint(Ljava/util/Collection;Ljava/util/Collection;)Z+]Ljava/util/Iterator;Ljava/util/AbstractList$Itr;
+HSPLjava/util/Collections;->disjoint(Ljava/util/Collection;Ljava/util/Collection;)Z
HSPLjava/util/Collections;->emptyEnumeration()Ljava/util/Enumeration;
HSPLjava/util/Collections;->emptyIterator()Ljava/util/Iterator;
HSPLjava/util/Collections;->emptyList()Ljava/util/List;
@@ -5335,7 +5408,7 @@
HSPLjava/util/Collections;->synchronizedSet(Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set;
HSPLjava/util/Collections;->unmodifiableCollection(Ljava/util/Collection;)Ljava/util/Collection;
HSPLjava/util/Collections;->unmodifiableList(Ljava/util/List;)Ljava/util/List;
-HSPLjava/util/Collections;->unmodifiableMap(Ljava/util/Map;)Ljava/util/Map;
+HSPLjava/util/Collections;->unmodifiableMap(Ljava/util/Map;)Ljava/util/Map;+]Ljava/lang/Object;missing_types
HSPLjava/util/Collections;->unmodifiableSet(Ljava/util/Set;)Ljava/util/Set;
HSPLjava/util/Collections;->unmodifiableSortedMap(Ljava/util/SortedMap;)Ljava/util/SortedMap;
HSPLjava/util/Collections;->unmodifiableSortedSet(Ljava/util/SortedSet;)Ljava/util/SortedSet;
@@ -5368,11 +5441,14 @@
HSPLjava/util/Comparator;->lambda$comparingInt$7b0bb60$1(Ljava/util/function/ToIntFunction;Ljava/lang/Object;Ljava/lang/Object;)I
HSPLjava/util/Comparator;->lambda$thenComparing$36697e65$1(Ljava/util/Comparator;Ljava/util/Comparator;Ljava/lang/Object;Ljava/lang/Object;)I
HSPLjava/util/Comparator;->naturalOrder()Ljava/util/Comparator;
+HSPLjava/util/Comparator;->nullsFirst(Ljava/util/Comparator;)Ljava/util/Comparator;
HSPLjava/util/Comparator;->reversed()Ljava/util/Comparator;
HSPLjava/util/Comparator;->thenComparing(Ljava/util/Comparator;)Ljava/util/Comparator;
HSPLjava/util/Comparator;->thenComparing(Ljava/util/function/Function;)Ljava/util/Comparator;
HSPLjava/util/Comparators$NaturalOrderComparator;->compare(Ljava/lang/Comparable;Ljava/lang/Comparable;)I
HSPLjava/util/Comparators$NaturalOrderComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
+HSPLjava/util/Comparators$NullComparator;-><init>(ZLjava/util/Comparator;)V
+HSPLjava/util/Comparators$NullComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I
HSPLjava/util/Currency;-><init>(Landroid/icu/util/Currency;)V
HSPLjava/util/Currency;->getCurrencyCode()Ljava/lang/String;
HSPLjava/util/Currency;->getInstance(Ljava/lang/String;)Ljava/util/Currency;
@@ -5401,16 +5477,19 @@
HSPLjava/util/Date;->toInstant()Ljava/time/Instant;
HSPLjava/util/Date;->toString()Ljava/lang/String;
HSPLjava/util/Dictionary;-><init>()V
-HSPLjava/util/DualPivotQuicksort;->doSort([CII[CII)V
-HSPLjava/util/DualPivotQuicksort;->doSort([FII[FII)V
-HSPLjava/util/DualPivotQuicksort;->sort([CIIZ)V
-HSPLjava/util/DualPivotQuicksort;->sort([CII[CII)V
-HSPLjava/util/DualPivotQuicksort;->sort([FIIZ)V
-HSPLjava/util/DualPivotQuicksort;->sort([FII[FII)V
-HSPLjava/util/DualPivotQuicksort;->sort([IIIZ)V
-HSPLjava/util/DualPivotQuicksort;->sort([III[III)V
-HSPLjava/util/DualPivotQuicksort;->sort([JIIZ)V
-HSPLjava/util/DualPivotQuicksort;->sort([JII[JII)V
+HSPLjava/util/DualPivotQuicksort;->insertionSort([CII)V
+HSPLjava/util/DualPivotQuicksort;->insertionSort([FII)V
+HSPLjava/util/DualPivotQuicksort;->insertionSort([III)V
+HSPLjava/util/DualPivotQuicksort;->insertionSort([JII)V
+HSPLjava/util/DualPivotQuicksort;->sort(Ljava/util/DualPivotQuicksort$Sorter;[FIII)V
+HSPLjava/util/DualPivotQuicksort;->sort(Ljava/util/DualPivotQuicksort$Sorter;[IIII)V
+HSPLjava/util/DualPivotQuicksort;->sort(Ljava/util/DualPivotQuicksort$Sorter;[JIII)V
+HSPLjava/util/DualPivotQuicksort;->sort([CIII)V
+HSPLjava/util/DualPivotQuicksort;->sort([FIII)V
+HSPLjava/util/DualPivotQuicksort;->sort([IIII)V
+HSPLjava/util/DualPivotQuicksort;->sort([JIII)V
+HSPLjava/util/DualPivotQuicksort;->tryMergeRuns(Ljava/util/DualPivotQuicksort$Sorter;[III)Z
+HSPLjava/util/DualPivotQuicksort;->tryMergeRuns(Ljava/util/DualPivotQuicksort$Sorter;[JII)Z
HSPLjava/util/EnumMap$EntryIterator$Entry;-><init>(Ljava/util/EnumMap$EntryIterator;I)V
HSPLjava/util/EnumMap$EntryIterator$Entry;->checkIndexForEntryUse()V
HSPLjava/util/EnumMap$EntryIterator$Entry;->getKey()Ljava/lang/Enum;
@@ -5475,62 +5554,62 @@
HSPLjava/util/Formatter$Conversion;->isText(C)Z
HSPLjava/util/Formatter$Conversion;->isValid(C)Z
HSPLjava/util/Formatter$DateTime;->isValid(C)Z
-HSPLjava/util/Formatter$FixedString;-><init>(Ljava/util/Formatter;Ljava/lang/String;)V
+HSPLjava/util/Formatter$FixedString;-><init>(Ljava/util/Formatter;Ljava/lang/String;II)V
HSPLjava/util/Formatter$FixedString;->index()I
-HSPLjava/util/Formatter$FixedString;->print(Ljava/lang/Object;Ljava/util/Locale;)V+]Ljava/lang/Appendable;Ljava/lang/StringBuilder;
+HSPLjava/util/Formatter$FixedString;->print(Ljava/lang/Object;Ljava/util/Locale;)V+]Ljava/lang/Appendable;Ljava/lang/StringBuilder;,Ljava/io/PrintWriter;
+HSPLjava/util/Formatter$Flags;->-$$Nest$madd(Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;)Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$Flags;-><init>(I)V
HSPLjava/util/Formatter$Flags;->add(Ljava/util/Formatter$Flags;)Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$Flags;->contains(Ljava/util/Formatter$Flags;)Z+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$Flags;->parse(C)Ljava/util/Formatter$Flags;
-HSPLjava/util/Formatter$Flags;->parse(Ljava/lang/String;)Ljava/util/Formatter$Flags;+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/util/Formatter$Flags;->parse(Ljava/lang/String;II)Ljava/util/Formatter$Flags;+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$Flags;->valueOf()I
HSPLjava/util/Formatter$FormatSpecifier;-><init>(Ljava/util/Formatter;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-HSPLjava/util/Formatter$FormatSpecifier;->addZeros([CI)[C
+HSPLjava/util/Formatter$FormatSpecifier;->addZeros(Ljava/lang/StringBuilder;I)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/util/Formatter$FormatSpecifier;->adjustWidth(ILjava/util/Formatter$Flags;Z)I
+HSPLjava/util/Formatter$FormatSpecifier;->appendJustified(Ljava/lang/Appendable;Ljava/lang/CharSequence;)Ljava/lang/Appendable;+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;]Ljava/lang/CharSequence;Ljava/lang/StringBuilder;,Ljava/lang/String;]Ljava/lang/Appendable;megamorphic_types
HSPLjava/util/Formatter$FormatSpecifier;->checkBadFlags([Ljava/util/Formatter$Flags;)V+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$FormatSpecifier;->checkCharacter()V
HSPLjava/util/Formatter$FormatSpecifier;->checkDateTime()V
HSPLjava/util/Formatter$FormatSpecifier;->checkFloat()V
HSPLjava/util/Formatter$FormatSpecifier;->checkGeneral()V+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$FormatSpecifier;->checkInteger()V
-HSPLjava/util/Formatter$FormatSpecifier;->checkNumeric()V
+HSPLjava/util/Formatter$FormatSpecifier;->checkNumeric()V+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$FormatSpecifier;->checkText()V
-HSPLjava/util/Formatter$FormatSpecifier;->conversion(Ljava/lang/String;)C
+HSPLjava/util/Formatter$FormatSpecifier;->conversion(C)C
HSPLjava/util/Formatter$FormatSpecifier;->flags(Ljava/lang/String;)Ljava/util/Formatter$Flags;+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
-HSPLjava/util/Formatter$FormatSpecifier;->getZero(Ljava/util/Locale;)C
+HSPLjava/util/Formatter$FormatSpecifier;->getZero(Ljava/util/Locale;)C+]Ljava/util/Formatter;Ljava/util/Formatter;]Ljava/util/Locale;Ljava/util/Locale;
HSPLjava/util/Formatter$FormatSpecifier;->index()I
HSPLjava/util/Formatter$FormatSpecifier;->index(Ljava/lang/String;)I
-HSPLjava/util/Formatter$FormatSpecifier;->justify(Ljava/lang/String;)Ljava/lang/String;
-HSPLjava/util/Formatter$FormatSpecifier;->leadingSign(Ljava/lang/StringBuilder;Z)Ljava/lang/StringBuilder;
+HSPLjava/util/Formatter$FormatSpecifier;->leadingSign(Ljava/lang/StringBuilder;Z)Ljava/lang/StringBuilder;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$FormatSpecifier;->localizedMagnitude(Ljava/lang/StringBuilder;JLjava/util/Formatter$Flags;ILjava/util/Locale;)Ljava/lang/StringBuilder;
-HSPLjava/util/Formatter$FormatSpecifier;->localizedMagnitude(Ljava/lang/StringBuilder;[CLjava/util/Formatter$Flags;ILjava/util/Locale;)Ljava/lang/StringBuilder;+]Ljava/text/DecimalFormatSymbols;Ljava/text/DecimalFormatSymbols;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;]Ljava/util/Locale;Ljava/util/Locale;
+HSPLjava/util/Formatter$FormatSpecifier;->localizedMagnitude(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;ILjava/util/Formatter$Flags;ILjava/util/Locale;)Ljava/lang/StringBuilder;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;]Ljava/util/Locale;Ljava/util/Locale;]Ljava/lang/CharSequence;Ljava/lang/StringBuilder;,Ljava/lang/String;]Ljava/text/DecimalFormatSymbols;Ljava/text/DecimalFormatSymbols;]Ljava/text/DecimalFormat;Ljava/text/DecimalFormat;
HSPLjava/util/Formatter$FormatSpecifier;->precision(Ljava/lang/String;)I
HSPLjava/util/Formatter$FormatSpecifier;->print(BLjava/util/Locale;)V
HSPLjava/util/Formatter$FormatSpecifier;->print(DLjava/util/Locale;)V
HSPLjava/util/Formatter$FormatSpecifier;->print(FLjava/util/Locale;)V
HSPLjava/util/Formatter$FormatSpecifier;->print(ILjava/util/Locale;)V
-HSPLjava/util/Formatter$FormatSpecifier;->print(JLjava/util/Locale;)V
+HSPLjava/util/Formatter$FormatSpecifier;->print(JLjava/util/Locale;)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
HSPLjava/util/Formatter$FormatSpecifier;->print(Ljava/lang/Object;Ljava/util/Locale;)V
-HSPLjava/util/Formatter$FormatSpecifier;->print(Ljava/lang/String;)V+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;]Ljava/lang/Appendable;Ljava/lang/StringBuilder;
+HSPLjava/util/Formatter$FormatSpecifier;->print(Ljava/lang/String;Ljava/util/Locale;)V+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;]Ljava/lang/String;Ljava/lang/String;
HSPLjava/util/Formatter$FormatSpecifier;->print(Ljava/lang/StringBuilder;DLjava/util/Locale;Ljava/util/Formatter$Flags;CIZ)V
HSPLjava/util/Formatter$FormatSpecifier;->print(Ljava/lang/StringBuilder;Ljava/util/Calendar;CLjava/util/Locale;)Ljava/lang/Appendable;
HSPLjava/util/Formatter$FormatSpecifier;->print(Ljava/math/BigInteger;Ljava/util/Locale;)V
HSPLjava/util/Formatter$FormatSpecifier;->print(Ljava/util/Calendar;CLjava/util/Locale;)V
-HSPLjava/util/Formatter$FormatSpecifier;->printBoolean(Ljava/lang/Object;)V
-HSPLjava/util/Formatter$FormatSpecifier;->printCharacter(Ljava/lang/Object;)V
HSPLjava/util/Formatter$FormatSpecifier;->printDateTime(Ljava/lang/Object;Ljava/util/Locale;)V
HSPLjava/util/Formatter$FormatSpecifier;->printFloat(Ljava/lang/Object;Ljava/util/Locale;)V
-HSPLjava/util/Formatter$FormatSpecifier;->printInteger(Ljava/lang/Object;Ljava/util/Locale;)V
+HSPLjava/util/Formatter$FormatSpecifier;->printInteger(Ljava/lang/Object;Ljava/util/Locale;)V+]Ljava/lang/Integer;Ljava/lang/Integer;]Ljava/lang/Long;Ljava/lang/Long;]Ljava/lang/Byte;Ljava/lang/Byte;
HSPLjava/util/Formatter$FormatSpecifier;->printString(Ljava/lang/Object;Ljava/util/Locale;)V+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;]Ljava/lang/Object;missing_types
-HSPLjava/util/Formatter$FormatSpecifier;->trailingSign(Ljava/lang/StringBuilder;Z)Ljava/lang/StringBuilder;
+HSPLjava/util/Formatter$FormatSpecifier;->trailingSign(Ljava/lang/StringBuilder;Z)Ljava/lang/StringBuilder;+]Ljava/util/Formatter$Flags;Ljava/util/Formatter$Flags;
+HSPLjava/util/Formatter$FormatSpecifier;->trailingZeros(Ljava/lang/StringBuilder;I)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/util/Formatter$FormatSpecifier;->width(Ljava/lang/String;)I
-HSPLjava/util/Formatter$FormatSpecifierParser;-><init>(Ljava/util/Formatter;Ljava/lang/String;I)V
+HSPLjava/util/Formatter$FormatSpecifierParser;-><init>(Ljava/util/Formatter;Ljava/lang/String;I)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
HSPLjava/util/Formatter$FormatSpecifierParser;->advance()C
HSPLjava/util/Formatter$FormatSpecifierParser;->back(I)V
HSPLjava/util/Formatter$FormatSpecifierParser;->getEndIdx()I
HSPLjava/util/Formatter$FormatSpecifierParser;->getFormatSpecifier()Ljava/util/Formatter$FormatSpecifier;
HSPLjava/util/Formatter$FormatSpecifierParser;->isEnd()Z
-HSPLjava/util/Formatter$FormatSpecifierParser;->nextInt()Ljava/lang/String;
+HSPLjava/util/Formatter$FormatSpecifierParser;->nextInt()Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/util/Formatter$FormatSpecifierParser;->nextIsInt()Z
HSPLjava/util/Formatter$FormatSpecifierParser;->peek()C
HSPLjava/util/Formatter;->-$$Nest$fgeta(Ljava/util/Formatter;)Ljava/lang/Appendable;
@@ -5543,12 +5622,12 @@
HSPLjava/util/Formatter;->close()V
HSPLjava/util/Formatter;->ensureOpen()V
HSPLjava/util/Formatter;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;+]Ljava/util/Formatter;Ljava/util/Formatter;
-HSPLjava/util/Formatter;->format(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;+]Ljava/util/Formatter$FormatString;Ljava/util/Formatter$FixedString;,Ljava/util/Formatter$FormatSpecifier;
-HSPLjava/util/Formatter;->getZero(Ljava/util/Locale;)C+]Ljava/util/Locale;Ljava/util/Locale;
+HSPLjava/util/Formatter;->format(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;+]Ljava/util/List;Ljava/util/ArrayList;]Ljava/util/Formatter$FormatString;Ljava/util/Formatter$FixedString;,Ljava/util/Formatter$FormatSpecifier;]Ljava/util/Iterator;Ljava/util/ArrayList$Itr;
+HSPLjava/util/Formatter;->getZero(Ljava/util/Locale;)C+]Ljava/util/Locale;Ljava/util/Locale;]Llibcore/icu/DecimalFormatData;Llibcore/icu/DecimalFormatData;
HSPLjava/util/Formatter;->locale()Ljava/util/Locale;
HSPLjava/util/Formatter;->nonNullAppendable(Ljava/lang/Appendable;)Ljava/lang/Appendable;
HSPLjava/util/Formatter;->out()Ljava/lang/Appendable;
-HSPLjava/util/Formatter;->parse(Ljava/lang/String;)[Ljava/util/Formatter$FormatString;+]Ljava/lang/String;Ljava/lang/String;]Ljava/util/Formatter$FormatSpecifierParser;Ljava/util/Formatter$FormatSpecifierParser;]Ljava/util/ArrayList;Ljava/util/ArrayList;
+HSPLjava/util/Formatter;->parse(Ljava/lang/String;)Ljava/util/List;+]Ljava/util/Formatter$FormatSpecifierParser;Ljava/util/Formatter$FormatSpecifierParser;]Ljava/util/ArrayList;Ljava/util/ArrayList;
HSPLjava/util/Formatter;->toString()Ljava/lang/String;+]Ljava/lang/Object;Ljava/lang/StringBuilder;
HSPLjava/util/GregorianCalendar;-><init>()V
HSPLjava/util/GregorianCalendar;-><init>(IIIIII)V
@@ -5560,12 +5639,12 @@
HSPLjava/util/GregorianCalendar;->adjustForZoneAndDaylightSavingsTime(IJLjava/util/TimeZone;)J
HSPLjava/util/GregorianCalendar;->clone()Ljava/lang/Object;
HSPLjava/util/GregorianCalendar;->computeFields()V
-HSPLjava/util/GregorianCalendar;->computeFields(II)I+]Lsun/util/calendar/Gregorian;Lsun/util/calendar/Gregorian;]Ljava/util/GregorianCalendar;Ljava/util/GregorianCalendar;]Ljava/util/TimeZone;Ljava/util/SimpleTimeZone;]Lsun/util/calendar/BaseCalendar$Date;Lsun/util/calendar/Gregorian$Date;]Lsun/util/calendar/BaseCalendar;Lsun/util/calendar/Gregorian;]Llibcore/util/ZoneInfo;Llibcore/util/ZoneInfo;
-HSPLjava/util/GregorianCalendar;->computeTime()V+]Ljava/util/GregorianCalendar;Ljava/util/GregorianCalendar;
+HSPLjava/util/GregorianCalendar;->computeFields(II)I
+HSPLjava/util/GregorianCalendar;->computeTime()V
HSPLjava/util/GregorianCalendar;->getActualMaximum(I)I
HSPLjava/util/GregorianCalendar;->getCalendarDate(J)Lsun/util/calendar/BaseCalendar$Date;
HSPLjava/util/GregorianCalendar;->getCurrentFixedDate()J
-HSPLjava/util/GregorianCalendar;->getFixedDate(Lsun/util/calendar/BaseCalendar;II)J+]Ljava/util/GregorianCalendar;Ljava/util/GregorianCalendar;]Lsun/util/calendar/BaseCalendar;Lsun/util/calendar/Gregorian;
+HSPLjava/util/GregorianCalendar;->getFixedDate(Lsun/util/calendar/BaseCalendar;II)J
HSPLjava/util/GregorianCalendar;->getGregorianCutoverDate()Lsun/util/calendar/BaseCalendar$Date;
HSPLjava/util/GregorianCalendar;->getJulianCalendarSystem()Lsun/util/calendar/BaseCalendar;
HSPLjava/util/GregorianCalendar;->getLeastMaximum(I)I
@@ -5573,7 +5652,7 @@
HSPLjava/util/GregorianCalendar;->getMinimum(I)I
HSPLjava/util/GregorianCalendar;->getNormalizedCalendar()Ljava/util/GregorianCalendar;
HSPLjava/util/GregorianCalendar;->getTimeZone()Ljava/util/TimeZone;
-HSPLjava/util/GregorianCalendar;->getWeekNumber(JJ)I
+HSPLjava/util/GregorianCalendar;->getWeekNumber(JJ)I+]Ljava/util/GregorianCalendar;Ljava/util/GregorianCalendar;
HSPLjava/util/GregorianCalendar;->internalGetEra()I
HSPLjava/util/GregorianCalendar;->isCutoverYear(I)Z
HSPLjava/util/GregorianCalendar;->isLeapYear(I)Z
@@ -5607,9 +5686,12 @@
HSPLjava/util/HashMap$KeySet;->iterator()Ljava/util/Iterator;
HSPLjava/util/HashMap$KeySet;->remove(Ljava/lang/Object;)Z
HSPLjava/util/HashMap$KeySet;->size()I
+HSPLjava/util/HashMap$KeySet;->toArray()[Ljava/lang/Object;
+HSPLjava/util/HashMap$KeySet;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/HashMap$KeySpliterator;-><init>(Ljava/util/HashMap;IIII)V
HSPLjava/util/HashMap$KeySpliterator;->characteristics()I
-HSPLjava/util/HashMap$KeySpliterator;->forEachRemaining(Ljava/util/function/Consumer;)V+]Ljava/util/function/Consumer;Ljava/util/stream/ReferencePipeline$4$1;
+HSPLjava/util/HashMap$KeySpliterator;->forEachRemaining(Ljava/util/function/Consumer;)V
+HSPLjava/util/HashMap$KeySpliterator;->tryAdvance(Ljava/util/function/Consumer;)Z
HSPLjava/util/HashMap$Node;-><init>(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)V
HSPLjava/util/HashMap$Node;->getKey()Ljava/lang/Object;
HSPLjava/util/HashMap$Node;->getValue()Ljava/lang/Object;
@@ -5617,7 +5699,7 @@
HSPLjava/util/HashMap$Node;->setValue(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/HashMap$TreeNode;-><init>(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)V
HSPLjava/util/HashMap$TreeNode;->balanceInsertion(Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;)Ljava/util/HashMap$TreeNode;
-HSPLjava/util/HashMap$TreeNode;->find(ILjava/lang/Object;Ljava/lang/Class;)Ljava/util/HashMap$TreeNode;
+HSPLjava/util/HashMap$TreeNode;->find(ILjava/lang/Object;Ljava/lang/Class;)Ljava/util/HashMap$TreeNode;+]Ljava/lang/Object;Ljava/lang/Long;
HSPLjava/util/HashMap$TreeNode;->getTreeNode(ILjava/lang/Object;)Ljava/util/HashMap$TreeNode;
HSPLjava/util/HashMap$TreeNode;->moveRootToFront([Ljava/util/HashMap$Node;Ljava/util/HashMap$TreeNode;)V
HSPLjava/util/HashMap$TreeNode;->putTreeVal(Ljava/util/HashMap;[Ljava/util/HashMap$Node;ILjava/lang/Object;Ljava/lang/Object;)Ljava/util/HashMap$TreeNode;
@@ -5627,7 +5709,7 @@
HSPLjava/util/HashMap$TreeNode;->treeify([Ljava/util/HashMap$Node;)V
HSPLjava/util/HashMap$TreeNode;->untreeify(Ljava/util/HashMap;)Ljava/util/HashMap$Node;
HSPLjava/util/HashMap$ValueIterator;-><init>(Ljava/util/HashMap;)V
-HSPLjava/util/HashMap$ValueIterator;->next()Ljava/lang/Object;+]Ljava/util/HashMap$ValueIterator;Ljava/util/HashMap$ValueIterator;
+HSPLjava/util/HashMap$ValueIterator;->next()Ljava/lang/Object;
HSPLjava/util/HashMap$ValueSpliterator;-><init>(Ljava/util/HashMap;IIII)V
HSPLjava/util/HashMap$ValueSpliterator;->characteristics()I
HSPLjava/util/HashMap$ValueSpliterator;->forEachRemaining(Ljava/util/function/Consumer;)V
@@ -5637,6 +5719,8 @@
HSPLjava/util/HashMap$Values;->iterator()Ljava/util/Iterator;
HSPLjava/util/HashMap$Values;->size()I
HSPLjava/util/HashMap$Values;->spliterator()Ljava/util/Spliterator;
+HSPLjava/util/HashMap$Values;->toArray()[Ljava/lang/Object;
+HSPLjava/util/HashMap$Values;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/HashMap;-><init>()V
HSPLjava/util/HashMap;-><init>(I)V
HSPLjava/util/HashMap;-><init>(IF)V
@@ -5652,33 +5736,36 @@
HSPLjava/util/HashMap;->containsValue(Ljava/lang/Object;)Z
HSPLjava/util/HashMap;->entrySet()Ljava/util/Set;
HSPLjava/util/HashMap;->forEach(Ljava/util/function/BiConsumer;)V
-HSPLjava/util/HashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/HashMap;megamorphic_types
-HSPLjava/util/HashMap;->getNode(ILjava/lang/Object;)Ljava/util/HashMap$Node;+]Ljava/lang/Object;megamorphic_types]Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;
+HSPLjava/util/HashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/HashMap;missing_types
+HSPLjava/util/HashMap;->getNode(Ljava/lang/Object;)Ljava/util/HashMap$Node;+]Ljava/lang/Object;megamorphic_types
HSPLjava/util/HashMap;->getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/HashMap;->hash(Ljava/lang/Object;)I+]Ljava/lang/Object;megamorphic_types
HSPLjava/util/HashMap;->internalWriteEntries(Ljava/io/ObjectOutputStream;)V
HSPLjava/util/HashMap;->isEmpty()Z
HSPLjava/util/HashMap;->keySet()Ljava/util/Set;
+HSPLjava/util/HashMap;->keysToArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/HashMap;->loadFactor()F
-HSPLjava/util/HashMap;->merge(Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;+]Ljava/util/HashMap;Ljava/util/HashMap;]Ljava/util/function/BiFunction;Lcom/android/internal/graphics/palette/QuantizerMap$$ExternalSyntheticLambda0;]Ljava/lang/Object;Ljava/lang/Integer;]Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;
+HSPLjava/util/HashMap;->merge(Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
HSPLjava/util/HashMap;->newNode(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node;
HSPLjava/util/HashMap;->newTreeNode(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode;
+HSPLjava/util/HashMap;->prepareArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/HashMap;missing_types
HSPLjava/util/HashMap;->putAll(Ljava/util/Map;)V
HSPLjava/util/HashMap;->putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLjava/util/HashMap;->putMapEntries(Ljava/util/Map;Z)V+]Ljava/util/HashMap;missing_types]Ljava/util/Map$Entry;megamorphic_types]Ljava/util/Map;missing_types]Ljava/util/Iterator;missing_types]Ljava/util/Set;missing_types
+HSPLjava/util/HashMap;->putMapEntries(Ljava/util/Map;Z)V+]Ljava/util/HashMap;missing_types]Ljava/util/Map$Entry;Ljava/util/LinkedHashMap$LinkedHashMapEntry;,Ljava/util/HashMap$Node;,Ljava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry;]Ljava/util/Map;missing_types]Ljava/util/Iterator;Ljava/util/HashMap$EntryIterator;,Ljava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet$1;,Ljava/util/LinkedHashMap$LinkedEntryIterator;]Ljava/util/Set;Ljava/util/LinkedHashMap$LinkedEntrySet;,Ljava/util/Collections$UnmodifiableMap$UnmodifiableEntrySet;,Ljava/util/HashMap$EntrySet;
HSPLjava/util/HashMap;->putVal(ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/lang/Object;+]Ljava/util/HashMap;missing_types]Ljava/lang/Object;megamorphic_types]Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;
HSPLjava/util/HashMap;->readObject(Ljava/io/ObjectInputStream;)V
HSPLjava/util/HashMap;->reinitialize()V
-HSPLjava/util/HashMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/HashMap;missing_types
+HSPLjava/util/HashMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/HashMap;Ljava/util/HashMap;,Ljava/util/LinkedHashMap;
HSPLjava/util/HashMap;->removeNode(ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/util/HashMap$Node;+]Ljava/util/HashMap;missing_types]Ljava/lang/Object;missing_types]Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;
HSPLjava/util/HashMap;->replacementNode(Ljava/util/HashMap$Node;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node;
HSPLjava/util/HashMap;->replacementTreeNode(Ljava/util/HashMap$Node;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode;
-HSPLjava/util/HashMap;->resize()[Ljava/util/HashMap$Node;
+HSPLjava/util/HashMap;->resize()[Ljava/util/HashMap$Node;+]Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;
HSPLjava/util/HashMap;->size()I
HSPLjava/util/HashMap;->tableSizeFor(I)I
HSPLjava/util/HashMap;->treeifyBin([Ljava/util/HashMap$Node;I)V
HSPLjava/util/HashMap;->values()Ljava/util/Collection;
+HSPLjava/util/HashMap;->valuesToArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/HashMap;->writeObject(Ljava/io/ObjectOutputStream;)V
HSPLjava/util/HashSet;-><init>()V
HSPLjava/util/HashSet;-><init>(I)V
@@ -5718,7 +5805,7 @@
HSPLjava/util/Hashtable;-><init>()V
HSPLjava/util/Hashtable;-><init>(I)V
HSPLjava/util/Hashtable;-><init>(IF)V
-HSPLjava/util/Hashtable;->addEntry(ILjava/lang/Object;Ljava/lang/Object;I)V+]Ljava/lang/Object;Ljava/lang/String;]Ljava/util/Hashtable;Ljava/util/Hashtable;
+HSPLjava/util/Hashtable;->addEntry(ILjava/lang/Object;Ljava/lang/Object;I)V
HSPLjava/util/Hashtable;->clear()V
HSPLjava/util/Hashtable;->clone()Ljava/lang/Object;
HSPLjava/util/Hashtable;->containsKey(Ljava/lang/Object;)Z
@@ -5740,10 +5827,11 @@
HSPLjava/util/IdentityHashMap$EntryIterator$Entry;->getKey()Ljava/lang/Object;
HSPLjava/util/IdentityHashMap$EntryIterator$Entry;->getValue()Ljava/lang/Object;
HSPLjava/util/IdentityHashMap$EntryIterator;-><init>(Ljava/util/IdentityHashMap;)V
-HSPLjava/util/IdentityHashMap$EntryIterator;->next()Ljava/lang/Object;
-HSPLjava/util/IdentityHashMap$EntryIterator;->next()Ljava/util/Map$Entry;
+HSPLjava/util/IdentityHashMap$EntryIterator;->next()Ljava/lang/Object;+]Ljava/util/IdentityHashMap$EntryIterator;Ljava/util/IdentityHashMap$EntryIterator;
+HSPLjava/util/IdentityHashMap$EntryIterator;->next()Ljava/util/Map$Entry;+]Ljava/util/IdentityHashMap$EntryIterator;Ljava/util/IdentityHashMap$EntryIterator;
HSPLjava/util/IdentityHashMap$EntrySet;-><init>(Ljava/util/IdentityHashMap;)V
HSPLjava/util/IdentityHashMap$EntrySet;->iterator()Ljava/util/Iterator;
+HSPLjava/util/IdentityHashMap$EntrySet;->size()I
HSPLjava/util/IdentityHashMap$IdentityHashMapIterator;-><init>(Ljava/util/IdentityHashMap;)V
HSPLjava/util/IdentityHashMap$IdentityHashMapIterator;->hasNext()Z
HSPLjava/util/IdentityHashMap$IdentityHashMapIterator;->nextIndex()I
@@ -5779,24 +5867,32 @@
HSPLjava/util/IdentityHashMap;->values()Ljava/util/Collection;
HSPLjava/util/ImmutableCollections$AbstractImmutableCollection;-><init>()V
HSPLjava/util/ImmutableCollections$AbstractImmutableList;-><init>()V
-HSPLjava/util/ImmutableCollections$AbstractImmutableList;->iterator()Ljava/util/Iterator;+]Ljava/util/ImmutableCollections$AbstractImmutableList;Ljava/util/ImmutableCollections$ListN;,Ljava/util/ImmutableCollections$List12;
+HSPLjava/util/ImmutableCollections$AbstractImmutableList;->iterator()Ljava/util/Iterator;
+HSPLjava/util/ImmutableCollections$AbstractImmutableMap;-><init>()V
HSPLjava/util/ImmutableCollections$AbstractImmutableSet;-><init>()V
HSPLjava/util/ImmutableCollections$List12;-><init>(Ljava/lang/Object;)V
HSPLjava/util/ImmutableCollections$List12;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
HSPLjava/util/ImmutableCollections$List12;->get(I)Ljava/lang/Object;
HSPLjava/util/ImmutableCollections$List12;->size()I
+HSPLjava/util/ImmutableCollections$ListItr;-><init>(Ljava/util/List;I)V
+HSPLjava/util/ImmutableCollections$ListItr;->hasNext()Z
+HSPLjava/util/ImmutableCollections$ListItr;->next()Ljava/lang/Object;
HSPLjava/util/ImmutableCollections$ListN;-><init>([Ljava/lang/Object;)V
HSPLjava/util/ImmutableCollections$ListN;->get(I)Ljava/lang/Object;
HSPLjava/util/ImmutableCollections$ListN;->size()I
+HSPLjava/util/ImmutableCollections$Map1;-><init>(Ljava/lang/Object;Ljava/lang/Object;)V
HSPLjava/util/ImmutableCollections$Map1;->entrySet()Ljava/util/Set;
+HSPLjava/util/ImmutableCollections$MapN;-><init>([Ljava/lang/Object;)V
+HSPLjava/util/ImmutableCollections$MapN;->containsKey(Ljava/lang/Object;)Z
HSPLjava/util/ImmutableCollections$MapN;->get(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLjava/util/ImmutableCollections$MapN;->probe(Ljava/lang/Object;)I+]Ljava/lang/Object;Ljava/lang/String;,Ljava/lang/Integer;,Landroid/hardware/biometrics/BiometricSourceType;
-HSPLjava/util/ImmutableCollections$Set0;->instance()Ljava/util/ImmutableCollections$Set0;
-HSPLjava/util/ImmutableCollections$Set1;-><init>(Ljava/lang/Object;)V
-HSPLjava/util/ImmutableCollections$Set1;->iterator()Ljava/util/Iterator;
+HSPLjava/util/ImmutableCollections$MapN;->probe(Ljava/lang/Object;)I
+HSPLjava/util/ImmutableCollections$SetN;-><init>([Ljava/lang/Object;)V
+HSPLjava/util/ImmutableCollections$SetN;->contains(Ljava/lang/Object;)Z
+HSPLjava/util/ImmutableCollections$SetN;->probe(Ljava/lang/Object;)I
HSPLjava/util/ImmutableCollections;-><clinit>()V
HSPLjava/util/ImmutableCollections;->emptyList()Ljava/util/List;
-HSPLjava/util/Iterator;->forEachRemaining(Ljava/util/function/Consumer;)V+]Ljava/util/function/Consumer;missing_types]Ljava/util/Iterator;Landroid/util/MapCollections$ArrayIterator;,Ljava/util/AbstractList$Itr;
+HSPLjava/util/ImmutableCollections;->listCopy(Ljava/util/Collection;)Ljava/util/List;
+HSPLjava/util/Iterator;->forEachRemaining(Ljava/util/function/Consumer;)V+]Ljava/util/function/Consumer;Ljava/util/stream/ReferencePipeline$3$1;]Ljava/util/Iterator;Ljava/util/AbstractList$Itr;
HSPLjava/util/JumboEnumSet$EnumSetIterator;-><init>(Ljava/util/JumboEnumSet;)V
HSPLjava/util/JumboEnumSet$EnumSetIterator;->hasNext()Z
HSPLjava/util/JumboEnumSet$EnumSetIterator;->next()Ljava/lang/Enum;
@@ -5823,7 +5919,7 @@
HSPLjava/util/LinkedHashMap$LinkedHashIterator;->remove()V
HSPLjava/util/LinkedHashMap$LinkedHashMapEntry;-><init>(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)V
HSPLjava/util/LinkedHashMap$LinkedKeyIterator;-><init>(Ljava/util/LinkedHashMap;)V
-HSPLjava/util/LinkedHashMap$LinkedKeyIterator;->next()Ljava/lang/Object;+]Ljava/util/LinkedHashMap$LinkedHashMapEntry;Ljava/util/LinkedHashMap$LinkedHashMapEntry;]Ljava/util/LinkedHashMap$LinkedKeyIterator;Ljava/util/LinkedHashMap$LinkedKeyIterator;
+HSPLjava/util/LinkedHashMap$LinkedKeyIterator;->next()Ljava/lang/Object;
HSPLjava/util/LinkedHashMap$LinkedKeySet;-><init>(Ljava/util/LinkedHashMap;)V
HSPLjava/util/LinkedHashMap$LinkedKeySet;->contains(Ljava/lang/Object;)Z
HSPLjava/util/LinkedHashMap$LinkedKeySet;->iterator()Ljava/util/Iterator;
@@ -5848,6 +5944,7 @@
HSPLjava/util/LinkedHashMap;->keySet()Ljava/util/Set;
HSPLjava/util/LinkedHashMap;->linkNodeLast(Ljava/util/LinkedHashMap$LinkedHashMapEntry;)V
HSPLjava/util/LinkedHashMap;->newNode(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node;
+HSPLjava/util/LinkedHashMap;->newTreeNode(ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode;
HSPLjava/util/LinkedHashMap;->reinitialize()V
HSPLjava/util/LinkedHashMap;->removeEldestEntry(Ljava/util/Map$Entry;)Z
HSPLjava/util/LinkedHashMap;->replacementTreeNode(Ljava/util/HashMap$Node;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode;
@@ -5856,13 +5953,13 @@
HSPLjava/util/LinkedHashSet;-><init>()V
HSPLjava/util/LinkedHashSet;-><init>(I)V
HSPLjava/util/LinkedHashSet;-><init>(Ljava/util/Collection;)V
-HSPLjava/util/LinkedList$ListItr;-><init>(Ljava/util/LinkedList;I)V+]Ljava/util/LinkedList;Ljava/util/LinkedList;
-HSPLjava/util/LinkedList$ListItr;->add(Ljava/lang/Object;)V+]Ljava/util/LinkedList;Ljava/util/LinkedList;]Ljava/util/LinkedList$ListItr;Ljava/util/LinkedList$ListItr;
+HSPLjava/util/LinkedList$ListItr;-><init>(Ljava/util/LinkedList;I)V
+HSPLjava/util/LinkedList$ListItr;->add(Ljava/lang/Object;)V
HSPLjava/util/LinkedList$ListItr;->checkForComodification()V
HSPLjava/util/LinkedList$ListItr;->hasNext()Z
HSPLjava/util/LinkedList$ListItr;->hasPrevious()Z
-HSPLjava/util/LinkedList$ListItr;->next()Ljava/lang/Object;+]Ljava/util/LinkedList$ListItr;Ljava/util/LinkedList$ListItr;
-HSPLjava/util/LinkedList$ListItr;->previous()Ljava/lang/Object;+]Ljava/util/LinkedList$ListItr;Ljava/util/LinkedList$ListItr;
+HSPLjava/util/LinkedList$ListItr;->next()Ljava/lang/Object;
+HSPLjava/util/LinkedList$ListItr;->previous()Ljava/lang/Object;
HSPLjava/util/LinkedList$ListItr;->remove()V
HSPLjava/util/LinkedList$ListItr;->set(Ljava/lang/Object;)V
HSPLjava/util/LinkedList$Node;-><init>(Ljava/util/LinkedList$Node;Ljava/lang/Object;Ljava/util/LinkedList$Node;)V
@@ -5870,7 +5967,7 @@
HSPLjava/util/LinkedList;-><init>(Ljava/util/Collection;)V
HSPLjava/util/LinkedList;->add(ILjava/lang/Object;)V
HSPLjava/util/LinkedList;->add(Ljava/lang/Object;)Z
-HSPLjava/util/LinkedList;->addAll(ILjava/util/Collection;)Z+]Ljava/util/Collection;Ljava/util/LinkedList;]Ljava/util/LinkedList;Ljava/util/LinkedList;
+HSPLjava/util/LinkedList;->addAll(ILjava/util/Collection;)Z
HSPLjava/util/LinkedList;->addAll(Ljava/util/Collection;)Z
HSPLjava/util/LinkedList;->addFirst(Ljava/lang/Object;)V
HSPLjava/util/LinkedList;->addLast(Ljava/lang/Object;)V
@@ -5896,6 +5993,7 @@
HSPLjava/util/LinkedList;->peekLast()Ljava/lang/Object;
HSPLjava/util/LinkedList;->poll()Ljava/lang/Object;
HSPLjava/util/LinkedList;->pollFirst()Ljava/lang/Object;
+HSPLjava/util/LinkedList;->pollLast()Ljava/lang/Object;
HSPLjava/util/LinkedList;->pop()Ljava/lang/Object;
HSPLjava/util/LinkedList;->push(Ljava/lang/Object;)V
HSPLjava/util/LinkedList;->remove()Ljava/lang/Object;
@@ -5910,10 +6008,13 @@
HSPLjava/util/LinkedList;->unlink(Ljava/util/LinkedList$Node;)Ljava/lang/Object;
HSPLjava/util/LinkedList;->unlinkFirst(Ljava/util/LinkedList$Node;)Ljava/lang/Object;
HSPLjava/util/LinkedList;->unlinkLast(Ljava/util/LinkedList$Node;)Ljava/lang/Object;
+HSPLjava/util/List;->copyOf(Ljava/util/Collection;)Ljava/util/List;
+HSPLjava/util/List;->of()Ljava/util/List;
HSPLjava/util/List;->of(Ljava/lang/Object;)Ljava/util/List;
HSPLjava/util/List;->of(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
HSPLjava/util/List;->of(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
-HSPLjava/util/List;->sort(Ljava/util/Comparator;)V+]Ljava/util/ListIterator;Ljava/util/LinkedList$ListItr;]Ljava/util/List;Ljava/util/LinkedList;
+HSPLjava/util/List;->of([Ljava/lang/Object;)Ljava/util/List;
+HSPLjava/util/List;->sort(Ljava/util/Comparator;)V
HSPLjava/util/List;->spliterator()Ljava/util/Spliterator;
HSPLjava/util/Locale$Builder;-><init>()V
HSPLjava/util/Locale$Builder;->build()Ljava/util/Locale;
@@ -5922,12 +6023,12 @@
HSPLjava/util/Locale$Builder;->setScript(Ljava/lang/String;)Ljava/util/Locale$Builder;
HSPLjava/util/Locale$Builder;->setVariant(Ljava/lang/String;)Ljava/util/Locale$Builder;
HSPLjava/util/Locale$Cache;->createObject(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLjava/util/Locale$Cache;->createObject(Ljava/util/Locale$LocaleKey;)Ljava/util/Locale;
+HSPLjava/util/Locale$Cache;->createObject(Ljava/lang/Object;)Ljava/util/Locale;
HSPLjava/util/Locale$LocaleKey;->-$$Nest$fgetbase(Ljava/util/Locale$LocaleKey;)Lsun/util/locale/BaseLocale;
HSPLjava/util/Locale$LocaleKey;->-$$Nest$fgetexts(Ljava/util/Locale$LocaleKey;)Lsun/util/locale/LocaleExtensions;
HSPLjava/util/Locale$LocaleKey;-><init>(Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)V
HSPLjava/util/Locale$LocaleKey;-><init>(Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;Ljava/util/Locale$LocaleKey-IA;)V
-HSPLjava/util/Locale$LocaleKey;->equals(Ljava/lang/Object;)Z+]Lsun/util/locale/BaseLocale;Lsun/util/locale/BaseLocale;
+HSPLjava/util/Locale$LocaleKey;->equals(Ljava/lang/Object;)Z
HSPLjava/util/Locale$LocaleKey;->hashCode()I
HSPLjava/util/Locale;-><init>(Ljava/lang/String;)V
HSPLjava/util/Locale;-><init>(Ljava/lang/String;Ljava/lang/String;)V
@@ -5942,9 +6043,9 @@
HSPLjava/util/Locale;->getAvailableLocales()[Ljava/util/Locale;
HSPLjava/util/Locale;->getBaseLocale()Lsun/util/locale/BaseLocale;
HSPLjava/util/Locale;->getCompatibilityExtensions(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/LocaleExtensions;
-HSPLjava/util/Locale;->getCountry()Ljava/lang/String;
+HSPLjava/util/Locale;->getCountry()Ljava/lang/String;+]Lsun/util/locale/BaseLocale;Lsun/util/locale/BaseLocale;
HSPLjava/util/Locale;->getDefault()Ljava/util/Locale;
-HSPLjava/util/Locale;->getDefault(Ljava/util/Locale$Category;)Ljava/util/Locale;+]Ljava/util/Locale$Category;Ljava/util/Locale$Category;
+HSPLjava/util/Locale;->getDefault(Ljava/util/Locale$Category;)Ljava/util/Locale;
HSPLjava/util/Locale;->getDisplayCountry(Ljava/util/Locale;)Ljava/lang/String;
HSPLjava/util/Locale;->getDisplayLanguage()Ljava/lang/String;
HSPLjava/util/Locale;->getDisplayLanguage(Ljava/util/Locale;)Ljava/lang/String;
@@ -5956,22 +6057,25 @@
HSPLjava/util/Locale;->getInstance(Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)Ljava/util/Locale;
HSPLjava/util/Locale;->getLanguage()Ljava/lang/String;+]Lsun/util/locale/BaseLocale;Lsun/util/locale/BaseLocale;
HSPLjava/util/Locale;->getScript()Ljava/lang/String;
-HSPLjava/util/Locale;->getVariant()Ljava/lang/String;
+HSPLjava/util/Locale;->getUnicodeLocaleType(Ljava/lang/String;)Ljava/lang/String;
+HSPLjava/util/Locale;->getVariant()Ljava/lang/String;+]Lsun/util/locale/BaseLocale;Lsun/util/locale/BaseLocale;
HSPLjava/util/Locale;->hasExtensions()Z
HSPLjava/util/Locale;->hashCode()I
+HSPLjava/util/Locale;->isUnicodeExtensionKey(Ljava/lang/String;)Z
HSPLjava/util/Locale;->isValidBcp47Alpha(Ljava/lang/String;II)Z
HSPLjava/util/Locale;->normalizeAndValidateLanguage(Ljava/lang/String;Z)Ljava/lang/String;
HSPLjava/util/Locale;->normalizeAndValidateRegion(Ljava/lang/String;Z)Ljava/lang/String;
-HSPLjava/util/Locale;->readObject(Ljava/io/ObjectInputStream;)V+]Ljava/io/ObjectInputStream;Landroid/os/Parcel$2;]Ljava/io/ObjectInputStream$GetField;Ljava/io/ObjectInputStream$GetFieldImpl;
-HSPLjava/util/Locale;->readResolve()Ljava/lang/Object;+]Lsun/util/locale/BaseLocale;Lsun/util/locale/BaseLocale;
+HSPLjava/util/Locale;->readObject(Ljava/io/ObjectInputStream;)V
+HSPLjava/util/Locale;->readResolve()Ljava/lang/Object;
HSPLjava/util/Locale;->setDefault(Ljava/util/Locale$Category;Ljava/util/Locale;)V
HSPLjava/util/Locale;->setDefault(Ljava/util/Locale;)V
-HSPLjava/util/Locale;->toLanguageTag()Ljava/lang/String;
+HSPLjava/util/Locale;->toLanguageTag()Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Lsun/util/locale/LanguageTag;Lsun/util/locale/LanguageTag;]Ljava/util/List;Ljava/util/Collections$EmptyList;]Ljava/util/Iterator;Ljava/util/Collections$EmptyIterator;
HSPLjava/util/Locale;->toString()Ljava/lang/String;
HSPLjava/util/Locale;->writeObject(Ljava/io/ObjectOutputStream;)V
HSPLjava/util/Map;->computeIfAbsent(Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;+]Ljava/util/function/Function;missing_types]Ljava/util/Map;Landroid/util/ArrayMap;
HSPLjava/util/Map;->forEach(Ljava/util/function/BiConsumer;)V
-HSPLjava/util/Map;->getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/Map;Landroid/util/ArrayMap;,Ljava/util/ImmutableCollections$MapN;
+HSPLjava/util/Map;->getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/Map;Landroid/util/ArrayMap;
+HSPLjava/util/Map;->of(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map;
HSPLjava/util/MissingResourceException;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
HSPLjava/util/NoSuchElementException;-><init>()V
HSPLjava/util/NoSuchElementException;-><init>(Ljava/lang/String;)V
@@ -6010,8 +6114,10 @@
HSPLjava/util/OptionalDouble;-><init>(D)V
HSPLjava/util/OptionalDouble;->of(D)Ljava/util/OptionalDouble;
HSPLjava/util/OptionalDouble;->orElseGet(Ljava/util/function/DoubleSupplier;)D
+HSPLjava/util/OptionalInt;-><init>(I)V
HSPLjava/util/OptionalInt;->empty()Ljava/util/OptionalInt;
HSPLjava/util/OptionalInt;->isPresent()Z
+HSPLjava/util/OptionalInt;->of(I)Ljava/util/OptionalInt;
HSPLjava/util/PriorityQueue$Itr;-><init>(Ljava/util/PriorityQueue;)V
HSPLjava/util/PriorityQueue$Itr;->hasNext()Z
HSPLjava/util/PriorityQueue$Itr;->next()Ljava/lang/Object;
@@ -6020,12 +6126,12 @@
HSPLjava/util/PriorityQueue;-><init>(ILjava/util/Comparator;)V
HSPLjava/util/PriorityQueue;-><init>(Ljava/util/Comparator;)V
HSPLjava/util/PriorityQueue;-><init>(Ljava/util/PriorityQueue;)V
-HSPLjava/util/PriorityQueue;->add(Ljava/lang/Object;)Z
+HSPLjava/util/PriorityQueue;->add(Ljava/lang/Object;)Z+]Ljava/util/PriorityQueue;Ljava/util/PriorityQueue;
HSPLjava/util/PriorityQueue;->clear()V
HSPLjava/util/PriorityQueue;->comparator()Ljava/util/Comparator;
HSPLjava/util/PriorityQueue;->contains(Ljava/lang/Object;)Z
HSPLjava/util/PriorityQueue;->grow(I)V
-HSPLjava/util/PriorityQueue;->indexOf(Ljava/lang/Object;)I
+HSPLjava/util/PriorityQueue;->indexOf(Ljava/lang/Object;)I+]Ljava/lang/Object;Ljava/lang/String;
HSPLjava/util/PriorityQueue;->initFromPriorityQueue(Ljava/util/PriorityQueue;)V
HSPLjava/util/PriorityQueue;->iterator()Ljava/util/Iterator;
HSPLjava/util/PriorityQueue;->offer(Ljava/lang/Object;)Z
@@ -6034,11 +6140,9 @@
HSPLjava/util/PriorityQueue;->remove(Ljava/lang/Object;)Z
HSPLjava/util/PriorityQueue;->removeAt(I)Ljava/lang/Object;
HSPLjava/util/PriorityQueue;->siftDown(ILjava/lang/Object;)V
-HSPLjava/util/PriorityQueue;->siftDownComparable(ILjava/lang/Object;)V
-HSPLjava/util/PriorityQueue;->siftDownUsingComparator(ILjava/lang/Object;)V
+HSPLjava/util/PriorityQueue;->siftDownComparable(ILjava/lang/Object;[Ljava/lang/Object;I)V+]Ljava/lang/Comparable;Ljava/lang/String;
+HSPLjava/util/PriorityQueue;->siftDownUsingComparator(ILjava/lang/Object;[Ljava/lang/Object;ILjava/util/Comparator;)V+]Ljava/util/Comparator;Ljava/util/Collections$ReverseComparator;
HSPLjava/util/PriorityQueue;->siftUp(ILjava/lang/Object;)V
-HSPLjava/util/PriorityQueue;->siftUpComparable(ILjava/lang/Object;)V
-HSPLjava/util/PriorityQueue;->siftUpUsingComparator(ILjava/lang/Object;)V
HSPLjava/util/PriorityQueue;->size()I
HSPLjava/util/PriorityQueue;->toArray()[Ljava/lang/Object;
HSPLjava/util/PriorityQueue;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
@@ -6053,14 +6157,14 @@
HSPLjava/util/Properties;->load(Ljava/io/Reader;)V
HSPLjava/util/Properties;->load0(Ljava/util/Properties$LineReader;)V
HSPLjava/util/Properties;->loadConvert([CII[C)Ljava/lang/String;
-HSPLjava/util/Properties;->saveConvert(Ljava/lang/String;ZZ)Ljava/lang/String;+]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;
+HSPLjava/util/Properties;->saveConvert(Ljava/lang/String;ZZ)Ljava/lang/String;
HSPLjava/util/Properties;->setProperty(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
HSPLjava/util/Properties;->store(Ljava/io/OutputStream;Ljava/lang/String;)V
HSPLjava/util/Properties;->store0(Ljava/io/BufferedWriter;Ljava/lang/String;Z)V
HSPLjava/util/Properties;->writeComments(Ljava/io/BufferedWriter;Ljava/lang/String;)V
HSPLjava/util/PropertyResourceBundle;-><init>(Ljava/io/Reader;)V
HSPLjava/util/Random;-><init>()V
-HSPLjava/util/Random;-><init>(J)V
+HSPLjava/util/Random;-><init>(J)V+]Ljava/lang/Object;Ljava/util/Random;
HSPLjava/util/Random;->initialScramble(J)J
HSPLjava/util/Random;->next(I)I
HSPLjava/util/Random;->nextBoolean()Z
@@ -6073,7 +6177,7 @@
HSPLjava/util/Random;->nextLong()J
HSPLjava/util/Random;->readObject(Ljava/io/ObjectInputStream;)V
HSPLjava/util/Random;->resetSeed(J)V
-HSPLjava/util/Random;->seedUniquifier()J
+HSPLjava/util/Random;->seedUniquifier()J+]Ljava/util/concurrent/atomic/AtomicLong;Ljava/util/concurrent/atomic/AtomicLong;
HSPLjava/util/Random;->setSeed(J)V
HSPLjava/util/Random;->writeObject(Ljava/io/ObjectOutputStream;)V
HSPLjava/util/RegularEnumSet$EnumSetIterator;-><init>(Ljava/util/RegularEnumSet;)V
@@ -6087,7 +6191,7 @@
HSPLjava/util/RegularEnumSet;->addAll(Ljava/util/Collection;)Z
HSPLjava/util/RegularEnumSet;->clear()V
HSPLjava/util/RegularEnumSet;->complement()V
-HSPLjava/util/RegularEnumSet;->contains(Ljava/lang/Object;)Z
+HSPLjava/util/RegularEnumSet;->contains(Ljava/lang/Object;)Z+]Ljava/lang/Object;missing_types]Ljava/lang/Enum;missing_types
HSPLjava/util/RegularEnumSet;->containsAll(Ljava/util/Collection;)Z
HSPLjava/util/RegularEnumSet;->equals(Ljava/lang/Object;)Z
HSPLjava/util/RegularEnumSet;->isEmpty()Z
@@ -6097,8 +6201,6 @@
HSPLjava/util/ResourceBundle$BundleReference;-><init>(Ljava/util/ResourceBundle;Ljava/lang/ref/ReferenceQueue;Ljava/util/ResourceBundle$CacheKey;)V
HSPLjava/util/ResourceBundle$BundleReference;->getCacheKey()Ljava/util/ResourceBundle$CacheKey;
HSPLjava/util/ResourceBundle$CacheKey;-><init>(Ljava/lang/String;Ljava/util/Locale;Ljava/lang/ClassLoader;)V
-HSPLjava/util/ResourceBundle$CacheKey;->calculateHashCode()V
-HSPLjava/util/ResourceBundle$CacheKey;->clone()Ljava/lang/Object;
HSPLjava/util/ResourceBundle$CacheKey;->equals(Ljava/lang/Object;)Z
HSPLjava/util/ResourceBundle$CacheKey;->getCause()Ljava/lang/Throwable;
HSPLjava/util/ResourceBundle$CacheKey;->getLoader()Ljava/lang/ClassLoader;
@@ -6107,7 +6209,6 @@
HSPLjava/util/ResourceBundle$CacheKey;->hashCode()I
HSPLjava/util/ResourceBundle$CacheKey;->setFormat(Ljava/lang/String;)V
HSPLjava/util/ResourceBundle$CacheKey;->setLocale(Ljava/util/Locale;)Ljava/util/ResourceBundle$CacheKey;
-HSPLjava/util/ResourceBundle$Control$1;-><init>(Ljava/util/ResourceBundle$Control;ZLjava/lang/ClassLoader;Ljava/lang/String;)V
HSPLjava/util/ResourceBundle$Control$1;->run()Ljava/io/InputStream;
HSPLjava/util/ResourceBundle$Control$1;->run()Ljava/lang/Object;
HSPLjava/util/ResourceBundle$Control$CandidateListCache;->createObject(Ljava/lang/Object;)Ljava/lang/Object;
@@ -6121,7 +6222,6 @@
HSPLjava/util/ResourceBundle$Control;->toBundleName(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String;
HSPLjava/util/ResourceBundle$Control;->toResourceName(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
HSPLjava/util/ResourceBundle$Control;->toResourceName0(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-HSPLjava/util/ResourceBundle$LoaderReference;-><init>(Ljava/lang/ClassLoader;Ljava/lang/ref/ReferenceQueue;Ljava/util/ResourceBundle$CacheKey;)V
HSPLjava/util/ResourceBundle;-><init>()V
HSPLjava/util/ResourceBundle;->findBundle(Ljava/util/ResourceBundle$CacheKey;Ljava/util/List;Ljava/util/List;ILjava/util/ResourceBundle$Control;Ljava/util/ResourceBundle;)Ljava/util/ResourceBundle;
HSPLjava/util/ResourceBundle;->findBundleInCache(Ljava/util/ResourceBundle$CacheKey;Ljava/util/ResourceBundle$Control;)Ljava/util/ResourceBundle;
@@ -6135,9 +6235,6 @@
HSPLjava/util/ResourceBundle;->putBundleInCache(Ljava/util/ResourceBundle$CacheKey;Ljava/util/ResourceBundle;Ljava/util/ResourceBundle$Control;)Ljava/util/ResourceBundle;
HSPLjava/util/ResourceBundle;->setExpirationTime(Ljava/util/ResourceBundle$CacheKey;Ljava/util/ResourceBundle$Control;)V
HSPLjava/util/ResourceBundle;->setParent(Ljava/util/ResourceBundle;)V
-HSPLjava/util/Scanner$1;-><init>(Ljava/util/Scanner;I)V
-HSPLjava/util/Scanner$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLjava/util/Scanner$1;->create(Ljava/lang/String;)Ljava/util/regex/Pattern;
HSPLjava/util/Scanner;-><init>(Ljava/io/InputStream;)V
HSPLjava/util/Scanner;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
HSPLjava/util/Scanner;-><init>(Ljava/lang/Readable;Ljava/util/regex/Pattern;)V
@@ -6183,8 +6280,8 @@
HSPLjava/util/SimpleTimeZone;->getOffsets(J[I)I
HSPLjava/util/SimpleTimeZone;->getRawOffset()I
HSPLjava/util/SimpleTimeZone;->hasSameRules(Ljava/util/TimeZone;)Z
-HSPLjava/util/Spliterator$OfInt;->forEachRemaining(Ljava/util/function/Consumer;)V+]Ljava/util/Spliterator$OfInt;Ljava/util/Spliterators$IntArraySpliterator;,Ljava/util/stream/Streams$RangeIntSpliterator;
-HSPLjava/util/Spliterator;->getExactSizeIfKnown()J+]Ljava/util/Spliterator;megamorphic_types
+HSPLjava/util/Spliterator$OfInt;->forEachRemaining(Ljava/util/function/Consumer;)V+]Ljava/util/Spliterator$OfInt;Ljava/util/Spliterators$IntArraySpliterator;
+HSPLjava/util/Spliterator;->getExactSizeIfKnown()J+]Ljava/util/Spliterator;Ljava/util/ArrayList$ArrayListSpliterator;,Ljava/util/HashMap$KeySpliterator;,Ljava/util/Spliterators$IntArraySpliterator;,Ljava/util/Spliterators$ArraySpliterator;,Ljava/util/Spliterators$IteratorSpliterator;
HSPLjava/util/Spliterators$ArraySpliterator;-><init>([Ljava/lang/Object;I)V
HSPLjava/util/Spliterators$ArraySpliterator;-><init>([Ljava/lang/Object;III)V
HSPLjava/util/Spliterators$ArraySpliterator;->characteristics()I
@@ -6193,13 +6290,15 @@
HSPLjava/util/Spliterators$ArraySpliterator;->tryAdvance(Ljava/util/function/Consumer;)Z
HSPLjava/util/Spliterators$EmptySpliterator$OfInt;->forEachRemaining(Ljava/util/function/IntConsumer;)V
HSPLjava/util/Spliterators$EmptySpliterator$OfRef;->forEachRemaining(Ljava/util/function/Consumer;)V
+HSPLjava/util/Spliterators$EmptySpliterator$OfRef;->tryAdvance(Ljava/util/function/Consumer;)Z
HSPLjava/util/Spliterators$EmptySpliterator;->characteristics()I
HSPLjava/util/Spliterators$EmptySpliterator;->estimateSize()J
HSPLjava/util/Spliterators$EmptySpliterator;->forEachRemaining(Ljava/lang/Object;)V
+HSPLjava/util/Spliterators$EmptySpliterator;->tryAdvance(Ljava/lang/Object;)Z
HSPLjava/util/Spliterators$IntArraySpliterator;-><init>([IIII)V
HSPLjava/util/Spliterators$IntArraySpliterator;->characteristics()I
HSPLjava/util/Spliterators$IntArraySpliterator;->estimateSize()J
-HSPLjava/util/Spliterators$IntArraySpliterator;->forEachRemaining(Ljava/util/function/IntConsumer;)V+]Ljava/util/function/IntConsumer;Ljava/util/stream/IntPipeline$4$1;
+HSPLjava/util/Spliterators$IntArraySpliterator;->forEachRemaining(Ljava/util/function/IntConsumer;)V
HSPLjava/util/Spliterators$IntArraySpliterator;->tryAdvance(Ljava/util/function/IntConsumer;)Z
HSPLjava/util/Spliterators$IteratorSpliterator;-><init>(Ljava/util/Collection;I)V
HSPLjava/util/Spliterators$IteratorSpliterator;->characteristics()I
@@ -6221,7 +6320,8 @@
HSPLjava/util/StringJoiner;-><init>(Ljava/lang/CharSequence;)V
HSPLjava/util/StringJoiner;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;)V
HSPLjava/util/StringJoiner;->add(Ljava/lang/CharSequence;)Ljava/util/StringJoiner;
-HSPLjava/util/StringJoiner;->prepareBuilder()Ljava/lang/StringBuilder;
+HSPLjava/util/StringJoiner;->compactElts()V
+HSPLjava/util/StringJoiner;->getChars(Ljava/lang/String;[CI)I+]Ljava/lang/String;Ljava/lang/String;
HSPLjava/util/StringJoiner;->toString()Ljava/lang/String;
HSPLjava/util/StringTokenizer;-><init>(Ljava/lang/String;)V
HSPLjava/util/StringTokenizer;-><init>(Ljava/lang/String;Ljava/lang/String;)V
@@ -6243,7 +6343,7 @@
HSPLjava/util/TaskQueue;->removeMin()V
HSPLjava/util/TaskQueue;->rescheduleMin(J)V
HSPLjava/util/TimSort;-><init>([Ljava/lang/Object;Ljava/util/Comparator;[Ljava/lang/Object;II)V
-HSPLjava/util/TimSort;->binarySort([Ljava/lang/Object;IIILjava/util/Comparator;)V+]Ljava/util/Comparator;missing_types
+HSPLjava/util/TimSort;->binarySort([Ljava/lang/Object;IIILjava/util/Comparator;)V
HSPLjava/util/TimSort;->countRunAndMakeAscending([Ljava/lang/Object;IILjava/util/Comparator;)I
HSPLjava/util/TimSort;->ensureCapacity(I)[Ljava/lang/Object;
HSPLjava/util/TimSort;->gallopLeft(Ljava/lang/Object;[Ljava/lang/Object;IIILjava/util/Comparator;)I
@@ -6252,7 +6352,7 @@
HSPLjava/util/TimSort;->mergeCollapse()V
HSPLjava/util/TimSort;->mergeForceCollapse()V
HSPLjava/util/TimSort;->mergeHi(IIII)V
-HSPLjava/util/TimSort;->mergeLo(IIII)V+]Ljava/util/Comparator;Lcom/android/internal/graphics/palette/WSMeansQuantizer$$ExternalSyntheticLambda0;
+HSPLjava/util/TimSort;->mergeLo(IIII)V
HSPLjava/util/TimSort;->minRunLength(I)I
HSPLjava/util/TimSort;->pushRun(II)V
HSPLjava/util/TimSort;->reverseRange([Ljava/lang/Object;II)V
@@ -6279,6 +6379,7 @@
HSPLjava/util/Timer;->cancel()V
HSPLjava/util/Timer;->sched(Ljava/util/TimerTask;JJ)V
HSPLjava/util/Timer;->schedule(Ljava/util/TimerTask;J)V
+HSPLjava/util/Timer;->schedule(Ljava/util/TimerTask;JJ)V
HSPLjava/util/Timer;->scheduleAtFixedRate(Ljava/util/TimerTask;JJ)V
HSPLjava/util/Timer;->serialNumber()I
HSPLjava/util/TimerTask;-><init>()V
@@ -6293,10 +6394,10 @@
HSPLjava/util/TreeMap$AscendingSubMap;->keyIterator()Ljava/util/Iterator;
HSPLjava/util/TreeMap$DescendingSubMap;-><init>(Ljava/util/TreeMap;ZLjava/lang/Object;ZZLjava/lang/Object;Z)V
HSPLjava/util/TreeMap$DescendingSubMap;->keyIterator()Ljava/util/Iterator;
-HSPLjava/util/TreeMap$DescendingSubMap;->subLowest()Ljava/util/TreeMap$TreeMapEntry;+]Ljava/util/TreeMap$DescendingSubMap;Ljava/util/TreeMap$DescendingSubMap;
+HSPLjava/util/TreeMap$DescendingSubMap;->subLowest()Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap$EntryIterator;-><init>(Ljava/util/TreeMap;Ljava/util/TreeMap$TreeMapEntry;)V
-HSPLjava/util/TreeMap$EntryIterator;->next()Ljava/lang/Object;
-HSPLjava/util/TreeMap$EntryIterator;->next()Ljava/util/Map$Entry;
+HSPLjava/util/TreeMap$EntryIterator;->next()Ljava/lang/Object;+]Ljava/util/TreeMap$EntryIterator;Ljava/util/TreeMap$EntryIterator;
+HSPLjava/util/TreeMap$EntryIterator;->next()Ljava/util/Map$Entry;+]Ljava/util/TreeMap$EntryIterator;Ljava/util/TreeMap$EntryIterator;
HSPLjava/util/TreeMap$EntrySet;-><init>(Ljava/util/TreeMap;)V
HSPLjava/util/TreeMap$EntrySet;->iterator()Ljava/util/Iterator;
HSPLjava/util/TreeMap$EntrySet;->size()I
@@ -6307,7 +6408,7 @@
HSPLjava/util/TreeMap$KeySet;->iterator()Ljava/util/Iterator;
HSPLjava/util/TreeMap$KeySet;->size()I
HSPLjava/util/TreeMap$NavigableSubMap$DescendingSubMapKeyIterator;-><init>(Ljava/util/TreeMap$NavigableSubMap;Ljava/util/TreeMap$TreeMapEntry;Ljava/util/TreeMap$TreeMapEntry;)V
-HSPLjava/util/TreeMap$NavigableSubMap$DescendingSubMapKeyIterator;->next()Ljava/lang/Object;+]Ljava/util/TreeMap$NavigableSubMap$DescendingSubMapKeyIterator;Ljava/util/TreeMap$NavigableSubMap$DescendingSubMapKeyIterator;
+HSPLjava/util/TreeMap$NavigableSubMap$DescendingSubMapKeyIterator;->next()Ljava/lang/Object;
HSPLjava/util/TreeMap$NavigableSubMap$EntrySetView;-><init>(Ljava/util/TreeMap$NavigableSubMap;)V
HSPLjava/util/TreeMap$NavigableSubMap$EntrySetView;->isEmpty()Z
HSPLjava/util/TreeMap$NavigableSubMap$EntrySetView;->size()I
@@ -6327,8 +6428,8 @@
HSPLjava/util/TreeMap$NavigableSubMap;->absHighest()Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap$NavigableSubMap;->absLowFence()Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap$NavigableSubMap;->absLowest()Ljava/util/TreeMap$TreeMapEntry;
-HSPLjava/util/TreeMap$NavigableSubMap;->firstKey()Ljava/lang/Object;+]Ljava/util/TreeMap$NavigableSubMap;Ljava/util/TreeMap$DescendingSubMap;
-HSPLjava/util/TreeMap$NavigableSubMap;->inRange(Ljava/lang/Object;)Z+]Ljava/util/TreeMap$NavigableSubMap;Ljava/util/TreeMap$AscendingSubMap;
+HSPLjava/util/TreeMap$NavigableSubMap;->firstKey()Ljava/lang/Object;
+HSPLjava/util/TreeMap$NavigableSubMap;->inRange(Ljava/lang/Object;)Z
HSPLjava/util/TreeMap$NavigableSubMap;->isEmpty()Z
HSPLjava/util/TreeMap$NavigableSubMap;->navigableKeySet()Ljava/util/NavigableSet;
HSPLjava/util/TreeMap$NavigableSubMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;
@@ -6374,10 +6475,10 @@
HSPLjava/util/TreeMap;->fixAfterInsertion(Ljava/util/TreeMap$TreeMapEntry;)V
HSPLjava/util/TreeMap;->floorEntry(Ljava/lang/Object;)Ljava/util/Map$Entry;
HSPLjava/util/TreeMap;->floorKey(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLjava/util/TreeMap;->get(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLjava/util/TreeMap;->get(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/TreeMap;Ljava/util/TreeMap;
HSPLjava/util/TreeMap;->getCeilingEntry(Ljava/lang/Object;)Ljava/util/TreeMap$TreeMapEntry;
-HSPLjava/util/TreeMap;->getEntry(Ljava/lang/Object;)Ljava/util/TreeMap$TreeMapEntry;+]Ljava/util/TreeMap;Ljava/util/TreeMap;]Ljava/lang/Comparable;missing_types
-HSPLjava/util/TreeMap;->getEntryUsingComparator(Ljava/lang/Object;)Ljava/util/TreeMap$TreeMapEntry;+]Ljava/util/Comparator;missing_types
+HSPLjava/util/TreeMap;->getEntry(Ljava/lang/Object;)Ljava/util/TreeMap$TreeMapEntry;+]Ljava/lang/Comparable;missing_types
+HSPLjava/util/TreeMap;->getEntryUsingComparator(Ljava/lang/Object;)Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap;->getFirstEntry()Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap;->getFloorEntry(Ljava/lang/Object;)Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap;->getHigherEntry(Ljava/lang/Object;)Ljava/util/TreeMap$TreeMapEntry;
@@ -6395,9 +6496,9 @@
HSPLjava/util/TreeMap;->parentOf(Ljava/util/TreeMap$TreeMapEntry;)Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap;->pollFirstEntry()Ljava/util/Map$Entry;
HSPLjava/util/TreeMap;->predecessor(Ljava/util/TreeMap$TreeMapEntry;)Ljava/util/TreeMap$TreeMapEntry;
-HSPLjava/util/TreeMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/Comparator;missing_types]Ljava/util/TreeMap$TreeMapEntry;Ljava/util/TreeMap$TreeMapEntry;]Ljava/util/TreeMap;missing_types]Ljava/lang/Comparable;missing_types
+HSPLjava/util/TreeMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/Comparator;Ljava/lang/String$CaseInsensitiveComparator;]Ljava/util/TreeMap$TreeMapEntry;Ljava/util/TreeMap$TreeMapEntry;]Ljava/util/TreeMap;Ljava/util/TreeMap;]Ljava/lang/Comparable;missing_types
HSPLjava/util/TreeMap;->putAll(Ljava/util/Map;)V
-HSPLjava/util/TreeMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/TreeMap;Ljava/util/TreeMap;
+HSPLjava/util/TreeMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/TreeMap;->rightOf(Ljava/util/TreeMap$TreeMapEntry;)Ljava/util/TreeMap$TreeMapEntry;
HSPLjava/util/TreeMap;->rotateLeft(Ljava/util/TreeMap$TreeMapEntry;)V
HSPLjava/util/TreeMap;->rotateRight(Ljava/util/TreeMap$TreeMapEntry;)V
@@ -6413,26 +6514,26 @@
HSPLjava/util/TreeSet;-><init>(Ljava/util/Comparator;)V
HSPLjava/util/TreeSet;-><init>(Ljava/util/NavigableMap;)V
HSPLjava/util/TreeSet;-><init>(Ljava/util/SortedSet;)V
-HSPLjava/util/TreeSet;->add(Ljava/lang/Object;)Z+]Ljava/util/NavigableMap;Ljava/util/TreeMap;
+HSPLjava/util/TreeSet;->add(Ljava/lang/Object;)Z
HSPLjava/util/TreeSet;->addAll(Ljava/util/Collection;)Z
HSPLjava/util/TreeSet;->ceiling(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/TreeSet;->clear()V
HSPLjava/util/TreeSet;->comparator()Ljava/util/Comparator;
-HSPLjava/util/TreeSet;->contains(Ljava/lang/Object;)Z+]Ljava/util/NavigableMap;Ljava/util/TreeMap;
-HSPLjava/util/TreeSet;->descendingSet()Ljava/util/NavigableSet;+]Ljava/util/NavigableMap;Ljava/util/TreeMap;
+HSPLjava/util/TreeSet;->contains(Ljava/lang/Object;)Z
+HSPLjava/util/TreeSet;->descendingSet()Ljava/util/NavigableSet;
HSPLjava/util/TreeSet;->first()Ljava/lang/Object;
-HSPLjava/util/TreeSet;->floor(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/NavigableMap;Ljava/util/TreeMap;
+HSPLjava/util/TreeSet;->floor(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/TreeSet;->isEmpty()Z
HSPLjava/util/TreeSet;->iterator()Ljava/util/Iterator;
HSPLjava/util/TreeSet;->last()Ljava/lang/Object;
-HSPLjava/util/TreeSet;->remove(Ljava/lang/Object;)Z+]Ljava/util/NavigableMap;Ljava/util/TreeMap;
+HSPLjava/util/TreeSet;->remove(Ljava/lang/Object;)Z
HSPLjava/util/TreeSet;->size()I
HSPLjava/util/TreeSet;->subSet(Ljava/lang/Object;ZLjava/lang/Object;Z)Ljava/util/NavigableSet;
HSPLjava/util/TreeSet;->tailSet(Ljava/lang/Object;Z)Ljava/util/NavigableSet;
HSPLjava/util/UUID;-><init>(JJ)V
HSPLjava/util/UUID;-><init>([B)V
HSPLjava/util/UUID;->digits(JI)Ljava/lang/String;
-HSPLjava/util/UUID;->equals(Ljava/lang/Object;)Z
+HSPLjava/util/UUID;->equals(Ljava/lang/Object;)Z+]Ljava/lang/Object;Ljava/util/UUID;
HSPLjava/util/UUID;->fromString(Ljava/lang/String;)Ljava/util/UUID;
HSPLjava/util/UUID;->getLeastSignificantBits()J
HSPLjava/util/UUID;->getMostSignificantBits()J
@@ -6451,6 +6552,7 @@
HSPLjava/util/Vector;-><init>(I)V
HSPLjava/util/Vector;-><init>(II)V
HSPLjava/util/Vector;->add(Ljava/lang/Object;)Z
+HSPLjava/util/Vector;->add(Ljava/lang/Object;[Ljava/lang/Object;I)V
HSPLjava/util/Vector;->addElement(Ljava/lang/Object;)V
HSPLjava/util/Vector;->clear()V
HSPLjava/util/Vector;->contains(Ljava/lang/Object;)Z
@@ -6458,13 +6560,12 @@
HSPLjava/util/Vector;->elementAt(I)Ljava/lang/Object;
HSPLjava/util/Vector;->elementData(I)Ljava/lang/Object;
HSPLjava/util/Vector;->elements()Ljava/util/Enumeration;
-HSPLjava/util/Vector;->ensureCapacityHelper(I)V
HSPLjava/util/Vector;->get(I)Ljava/lang/Object;
-HSPLjava/util/Vector;->grow(I)V
HSPLjava/util/Vector;->indexOf(Ljava/lang/Object;)I
HSPLjava/util/Vector;->indexOf(Ljava/lang/Object;I)I
HSPLjava/util/Vector;->isEmpty()Z
HSPLjava/util/Vector;->iterator()Ljava/util/Iterator;
+HSPLjava/util/Vector;->newCapacity(I)I
HSPLjava/util/Vector;->removeAllElements()V
HSPLjava/util/Vector;->removeElement(Ljava/lang/Object;)Z
HSPLjava/util/Vector;->removeElementAt(I)V
@@ -6473,7 +6574,7 @@
HSPLjava/util/Vector;->toArray()[Ljava/lang/Object;
HSPLjava/util/Vector;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/WeakHashMap$Entry;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;ILjava/util/WeakHashMap$Entry;)V
-HSPLjava/util/WeakHashMap$Entry;->getKey()Ljava/lang/Object;+]Ljava/util/WeakHashMap$Entry;Ljava/util/WeakHashMap$Entry;
+HSPLjava/util/WeakHashMap$Entry;->getKey()Ljava/lang/Object;
HSPLjava/util/WeakHashMap$Entry;->getValue()Ljava/lang/Object;
HSPLjava/util/WeakHashMap$EntryIterator;-><init>(Ljava/util/WeakHashMap;)V
HSPLjava/util/WeakHashMap$EntryIterator;->next()Ljava/lang/Object;
@@ -6481,11 +6582,11 @@
HSPLjava/util/WeakHashMap$EntrySet;-><init>(Ljava/util/WeakHashMap;)V
HSPLjava/util/WeakHashMap$EntrySet;->iterator()Ljava/util/Iterator;
HSPLjava/util/WeakHashMap$HashIterator;-><init>(Ljava/util/WeakHashMap;)V
-HSPLjava/util/WeakHashMap$HashIterator;->hasNext()Z+]Ljava/util/WeakHashMap$Entry;Ljava/util/WeakHashMap$Entry;
+HSPLjava/util/WeakHashMap$HashIterator;->hasNext()Z
HSPLjava/util/WeakHashMap$HashIterator;->nextEntry()Ljava/util/WeakHashMap$Entry;
HSPLjava/util/WeakHashMap$KeyIterator;-><init>(Ljava/util/WeakHashMap;)V
HSPLjava/util/WeakHashMap$KeyIterator;-><init>(Ljava/util/WeakHashMap;Ljava/util/WeakHashMap$KeyIterator-IA;)V
-HSPLjava/util/WeakHashMap$KeyIterator;->next()Ljava/lang/Object;+]Ljava/util/WeakHashMap$KeyIterator;Ljava/util/WeakHashMap$KeyIterator;]Ljava/util/WeakHashMap$Entry;Ljava/util/WeakHashMap$Entry;
+HSPLjava/util/WeakHashMap$KeyIterator;->next()Ljava/lang/Object;
HSPLjava/util/WeakHashMap$KeySet;-><init>(Ljava/util/WeakHashMap;)V
HSPLjava/util/WeakHashMap$KeySet;-><init>(Ljava/util/WeakHashMap;Ljava/util/WeakHashMap$KeySet-IA;)V
HSPLjava/util/WeakHashMap$KeySet;->iterator()Ljava/util/Iterator;
@@ -6505,13 +6606,13 @@
HSPLjava/util/WeakHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/WeakHashMap;->getEntry(Ljava/lang/Object;)Ljava/util/WeakHashMap$Entry;
HSPLjava/util/WeakHashMap;->getTable()[Ljava/util/WeakHashMap$Entry;
-HSPLjava/util/WeakHashMap;->hash(Ljava/lang/Object;)I+]Ljava/lang/Object;missing_types
+HSPLjava/util/WeakHashMap;->hash(Ljava/lang/Object;)I+]Ljava/lang/Object;megamorphic_types
HSPLjava/util/WeakHashMap;->indexFor(II)I
HSPLjava/util/WeakHashMap;->isEmpty()Z
HSPLjava/util/WeakHashMap;->keySet()Ljava/util/Set;
HSPLjava/util/WeakHashMap;->maskNull(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/WeakHashMap;->newTable(I)[Ljava/util/WeakHashMap$Entry;
-HSPLjava/util/WeakHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+HSPLjava/util/WeakHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/WeakHashMap;Ljava/util/WeakHashMap;
HSPLjava/util/WeakHashMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/WeakHashMap;->resize(I)V
HSPLjava/util/WeakHashMap;->size()I
@@ -6529,7 +6630,7 @@
HSPLjava/util/concurrent/ArrayBlockingQueue;-><init>(IZ)V
HSPLjava/util/concurrent/ArrayBlockingQueue;->add(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ArrayBlockingQueue;->dequeue()Ljava/lang/Object;
-HSPLjava/util/concurrent/ArrayBlockingQueue;->drainTo(Ljava/util/Collection;)I+]Ljava/util/concurrent/ArrayBlockingQueue;Ljava/util/concurrent/ArrayBlockingQueue;
+HSPLjava/util/concurrent/ArrayBlockingQueue;->drainTo(Ljava/util/Collection;)I
HSPLjava/util/concurrent/ArrayBlockingQueue;->drainTo(Ljava/util/Collection;I)I
HSPLjava/util/concurrent/ArrayBlockingQueue;->enqueue(Ljava/lang/Object;)V
HSPLjava/util/concurrent/ArrayBlockingQueue;->itemAt(I)Ljava/lang/Object;
@@ -6545,7 +6646,7 @@
HSPLjava/util/concurrent/CompletableFuture$AsyncRun;-><init>(Ljava/util/concurrent/CompletableFuture;Ljava/lang/Runnable;)V
HSPLjava/util/concurrent/CompletableFuture$AsyncRun;->run()V
HSPLjava/util/concurrent/CompletableFuture$AsyncSupply;-><init>(Ljava/util/concurrent/CompletableFuture;Ljava/util/function/Supplier;)V
-HSPLjava/util/concurrent/CompletableFuture$AsyncSupply;->run()V+]Ljava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;]Ljava/util/function/Supplier;Landroid/telephony/ims/feature/MmTelFeature$1$$ExternalSyntheticLambda0;,Landroid/telephony/ims/stub/ImsRegistrationImplBase$1$$ExternalSyntheticLambda3;,Landroid/telephony/ims/stub/ImsConfigImplBase$ImsConfigStub$$ExternalSyntheticLambda5;,Landroid/telephony/ims/stub/ImsCallSessionImplBase$1$$ExternalSyntheticLambda0;
+HSPLjava/util/concurrent/CompletableFuture$AsyncSupply;->run()V
HSPLjava/util/concurrent/CompletableFuture$Completion;-><init>()V
HSPLjava/util/concurrent/CompletableFuture$Signaller;-><init>(ZJJ)V
HSPLjava/util/concurrent/CompletableFuture$Signaller;->block()Z
@@ -6553,7 +6654,7 @@
HSPLjava/util/concurrent/CompletableFuture$Signaller;->tryFire(I)Ljava/util/concurrent/CompletableFuture;
HSPLjava/util/concurrent/CompletableFuture;-><init>()V
HSPLjava/util/concurrent/CompletableFuture;->asyncRunStage(Ljava/util/concurrent/Executor;Ljava/lang/Runnable;)Ljava/util/concurrent/CompletableFuture;
-HSPLjava/util/concurrent/CompletableFuture;->asyncSupplyStage(Ljava/util/concurrent/Executor;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;+]Ljava/util/concurrent/Executor;Ljava/util/concurrent/ForkJoinPool;,Ljava/util/concurrent/Executors$FinalizableDelegatedExecutorService;,Landroid/app/PendingIntent$$ExternalSyntheticLambda1;
+HSPLjava/util/concurrent/CompletableFuture;->asyncSupplyStage(Ljava/util/concurrent/Executor;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;
HSPLjava/util/concurrent/CompletableFuture;->complete(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/CompletableFuture;->completeNull()Z
HSPLjava/util/concurrent/CompletableFuture;->completeValue(Ljava/lang/Object;)Z
@@ -6608,14 +6709,15 @@
HSPLjava/util/concurrent/ConcurrentHashMap;-><init>(I)V
HSPLjava/util/concurrent/ConcurrentHashMap;-><init>(IFI)V
HSPLjava/util/concurrent/ConcurrentHashMap;-><init>(Ljava/util/Map;)V
-HSPLjava/util/concurrent/ConcurrentHashMap;->addCount(JI)V+]Ljdk/internal/misc/Unsafe;Ljdk/internal/misc/Unsafe;]Ljava/util/concurrent/ConcurrentHashMap;Ljava/util/concurrent/ConcurrentHashMap;
+HSPLjava/util/concurrent/ConcurrentHashMap;->addCount(JI)V
HSPLjava/util/concurrent/ConcurrentHashMap;->casTabAt([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$Node;)Z
HSPLjava/util/concurrent/ConcurrentHashMap;->clear()V
HSPLjava/util/concurrent/ConcurrentHashMap;->computeIfAbsent(Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;
HSPLjava/util/concurrent/ConcurrentHashMap;->containsKey(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ConcurrentHashMap;->entrySet()Ljava/util/Set;
+HSPLjava/util/concurrent/ConcurrentHashMap;->forEach(Ljava/util/function/BiConsumer;)V
HSPLjava/util/concurrent/ConcurrentHashMap;->fullAddCount(JZ)V
-HSPLjava/util/concurrent/ConcurrentHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/lang/Object;missing_types]Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$ForwardingNode;
+HSPLjava/util/concurrent/ConcurrentHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/lang/Object;megamorphic_types
HSPLjava/util/concurrent/ConcurrentHashMap;->getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/concurrent/ConcurrentHashMap;->helpTransfer([Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$Node;)[Ljava/util/concurrent/ConcurrentHashMap$Node;
HSPLjava/util/concurrent/ConcurrentHashMap;->initTable()[Ljava/util/concurrent/ConcurrentHashMap$Node;
@@ -6625,10 +6727,11 @@
HSPLjava/util/concurrent/ConcurrentHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/concurrent/ConcurrentHashMap;->putAll(Ljava/util/Map;)V
HSPLjava/util/concurrent/ConcurrentHashMap;->putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HSPLjava/util/concurrent/ConcurrentHashMap;->putVal(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;+]Ljava/lang/Object;missing_types
+HSPLjava/util/concurrent/ConcurrentHashMap;->putVal(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;
HSPLjava/util/concurrent/ConcurrentHashMap;->remove(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLjava/util/concurrent/ConcurrentHashMap;->remove(Ljava/lang/Object;Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ConcurrentHashMap;->replace(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
-HSPLjava/util/concurrent/ConcurrentHashMap;->replaceNode(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+HSPLjava/util/concurrent/ConcurrentHashMap;->replaceNode(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/lang/Object;Lsun/nio/ch/FileKey;
HSPLjava/util/concurrent/ConcurrentHashMap;->resizeStamp(I)I
HSPLjava/util/concurrent/ConcurrentHashMap;->setTabAt([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;)V
HSPLjava/util/concurrent/ConcurrentHashMap;->size()I
@@ -6670,16 +6773,16 @@
HSPLjava/util/concurrent/ConcurrentLinkedQueue$Node;->casItem(Ljava/lang/Object;Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ConcurrentLinkedQueue;-><init>()V
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->add(Ljava/lang/Object;)Z
-HSPLjava/util/concurrent/ConcurrentLinkedQueue;->bulkRemove(Ljava/util/function/Predicate;)Z+]Ljava/util/concurrent/ConcurrentLinkedQueue$Node;Ljava/util/concurrent/ConcurrentLinkedQueue$Node;]Ljava/util/function/Predicate;Ljava/util/concurrent/ConcurrentLinkedQueue$$ExternalSyntheticLambda0;,Ljava/util/concurrent/ConcurrentLinkedQueue$$ExternalSyntheticLambda2;
+HSPLjava/util/concurrent/ConcurrentLinkedQueue;->bulkRemove(Ljava/util/function/Predicate;)Z
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->clear()V
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->contains(Ljava/lang/Object;)Z
-HSPLjava/util/concurrent/ConcurrentLinkedQueue;->first()Ljava/util/concurrent/ConcurrentLinkedQueue$Node;+]Ljava/util/concurrent/ConcurrentLinkedQueue;Ljava/util/concurrent/ConcurrentLinkedQueue;
+HSPLjava/util/concurrent/ConcurrentLinkedQueue;->first()Ljava/util/concurrent/ConcurrentLinkedQueue$Node;
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->isEmpty()Z
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->iterator()Ljava/util/Iterator;
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->lambda$clear$2(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->offer(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->peek()Ljava/lang/Object;
-HSPLjava/util/concurrent/ConcurrentLinkedQueue;->poll()Ljava/lang/Object;+]Ljava/util/concurrent/ConcurrentLinkedQueue;Ljava/util/concurrent/ConcurrentLinkedQueue;]Ljava/util/concurrent/ConcurrentLinkedQueue$Node;Ljava/util/concurrent/ConcurrentLinkedQueue$Node;
+HSPLjava/util/concurrent/ConcurrentLinkedQueue;->poll()Ljava/lang/Object;
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->remove(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->size()I
HSPLjava/util/concurrent/ConcurrentLinkedQueue;->succ(Ljava/util/concurrent/ConcurrentLinkedQueue$Node;)Ljava/util/concurrent/ConcurrentLinkedQueue$Node;
@@ -6710,7 +6813,7 @@
HSPLjava/util/concurrent/CopyOnWriteArrayList$$ExternalSyntheticLambda2;->test(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/CopyOnWriteArrayList$COWIterator;-><init>([Ljava/lang/Object;I)V
HSPLjava/util/concurrent/CopyOnWriteArrayList$COWIterator;->hasNext()Z
-HSPLjava/util/concurrent/CopyOnWriteArrayList$COWIterator;->next()Ljava/lang/Object;+]Ljava/util/concurrent/CopyOnWriteArrayList$COWIterator;Ljava/util/concurrent/CopyOnWriteArrayList$COWIterator;
+HSPLjava/util/concurrent/CopyOnWriteArrayList$COWIterator;->next()Ljava/lang/Object;
HSPLjava/util/concurrent/CopyOnWriteArrayList;-><init>()V
HSPLjava/util/concurrent/CopyOnWriteArrayList;-><init>(Ljava/util/Collection;)V
HSPLjava/util/concurrent/CopyOnWriteArrayList;-><init>([Ljava/lang/Object;)V
@@ -6720,17 +6823,17 @@
HSPLjava/util/concurrent/CopyOnWriteArrayList;->addAllAbsent(Ljava/util/Collection;)I
HSPLjava/util/concurrent/CopyOnWriteArrayList;->addIfAbsent(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/CopyOnWriteArrayList;->addIfAbsent(Ljava/lang/Object;[Ljava/lang/Object;)Z
-HSPLjava/util/concurrent/CopyOnWriteArrayList;->bulkRemove(Ljava/util/function/Predicate;)Z+]Ljava/util/concurrent/CopyOnWriteArrayList;Ljava/util/concurrent/CopyOnWriteArrayList;
-HSPLjava/util/concurrent/CopyOnWriteArrayList;->bulkRemove(Ljava/util/function/Predicate;II)Z+]Ljava/util/concurrent/CopyOnWriteArrayList;Ljava/util/concurrent/CopyOnWriteArrayList;]Ljava/util/function/Predicate;Ljava/util/concurrent/CopyOnWriteArrayList$$ExternalSyntheticLambda2;
+HSPLjava/util/concurrent/CopyOnWriteArrayList;->bulkRemove(Ljava/util/function/Predicate;)Z
+HSPLjava/util/concurrent/CopyOnWriteArrayList;->bulkRemove(Ljava/util/function/Predicate;II)Z
HSPLjava/util/concurrent/CopyOnWriteArrayList;->clear()V
HSPLjava/util/concurrent/CopyOnWriteArrayList;->contains(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/CopyOnWriteArrayList;->elementAt([Ljava/lang/Object;I)Ljava/lang/Object;
HSPLjava/util/concurrent/CopyOnWriteArrayList;->get(I)Ljava/lang/Object;
HSPLjava/util/concurrent/CopyOnWriteArrayList;->getArray()[Ljava/lang/Object;
-HSPLjava/util/concurrent/CopyOnWriteArrayList;->indexOf(Ljava/lang/Object;)I+]Ljava/util/concurrent/CopyOnWriteArrayList;Ljava/util/concurrent/CopyOnWriteArrayList;
-HSPLjava/util/concurrent/CopyOnWriteArrayList;->indexOfRange(Ljava/lang/Object;[Ljava/lang/Object;II)I+]Ljava/lang/Object;missing_types
+HSPLjava/util/concurrent/CopyOnWriteArrayList;->indexOf(Ljava/lang/Object;)I
+HSPLjava/util/concurrent/CopyOnWriteArrayList;->indexOfRange(Ljava/lang/Object;[Ljava/lang/Object;II)I
HSPLjava/util/concurrent/CopyOnWriteArrayList;->isEmpty()Z
-HSPLjava/util/concurrent/CopyOnWriteArrayList;->iterator()Ljava/util/Iterator;+]Ljava/util/concurrent/CopyOnWriteArrayList;Ljava/util/concurrent/CopyOnWriteArrayList;
+HSPLjava/util/concurrent/CopyOnWriteArrayList;->iterator()Ljava/util/Iterator;
HSPLjava/util/concurrent/CopyOnWriteArrayList;->lambda$removeAll$0(Ljava/util/Collection;Ljava/lang/Object;)Z
HSPLjava/util/concurrent/CopyOnWriteArrayList;->remove(I)Ljava/lang/Object;
HSPLjava/util/concurrent/CopyOnWriteArrayList;->remove(Ljava/lang/Object;)Z
@@ -6742,7 +6845,7 @@
HSPLjava/util/concurrent/CopyOnWriteArrayList;->toArray([Ljava/lang/Object;)[Ljava/lang/Object;
HSPLjava/util/concurrent/CopyOnWriteArrayList;->toString()Ljava/lang/String;
HSPLjava/util/concurrent/CopyOnWriteArraySet;-><init>()V
-HSPLjava/util/concurrent/CopyOnWriteArraySet;-><init>(Ljava/util/Collection;)V+]Ljava/lang/Object;missing_types]Ljava/util/concurrent/CopyOnWriteArrayList;Ljava/util/concurrent/CopyOnWriteArrayList;
+HSPLjava/util/concurrent/CopyOnWriteArraySet;-><init>(Ljava/util/Collection;)V
HSPLjava/util/concurrent/CopyOnWriteArraySet;->add(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/CopyOnWriteArraySet;->addAll(Ljava/util/Collection;)Z
HSPLjava/util/concurrent/CopyOnWriteArraySet;->clear()V
@@ -6776,7 +6879,7 @@
HSPLjava/util/concurrent/Executors$DelegatedExecutorService;->submit(Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
HSPLjava/util/concurrent/Executors$DelegatedScheduledExecutorService;-><init>(Ljava/util/concurrent/ScheduledExecutorService;)V
HSPLjava/util/concurrent/Executors$DelegatedScheduledExecutorService;->schedule(Ljava/lang/Runnable;JLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture;
-HSPLjava/util/concurrent/Executors$DelegatedScheduledExecutorService;->schedule(Ljava/util/concurrent/Callable;JLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture;+]Ljava/util/concurrent/ScheduledExecutorService;Ljava/util/concurrent/ScheduledThreadPoolExecutor;
+HSPLjava/util/concurrent/Executors$DelegatedScheduledExecutorService;->schedule(Ljava/util/concurrent/Callable;JLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture;
HSPLjava/util/concurrent/Executors$DelegatedScheduledExecutorService;->scheduleAtFixedRate(Ljava/lang/Runnable;JJLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture;
HSPLjava/util/concurrent/Executors$DelegatedScheduledExecutorService;->scheduleWithFixedDelay(Ljava/lang/Runnable;JJLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture;
HSPLjava/util/concurrent/Executors$FinalizableDelegatedExecutorService;-><init>(Ljava/util/concurrent/ExecutorService;)V
@@ -6849,14 +6952,14 @@
HSPLjava/util/concurrent/LinkedBlockingQueue;->enqueue(Ljava/util/concurrent/LinkedBlockingQueue$Node;)V
HSPLjava/util/concurrent/LinkedBlockingQueue;->fullyLock()V
HSPLjava/util/concurrent/LinkedBlockingQueue;->fullyUnlock()V
-HSPLjava/util/concurrent/LinkedBlockingQueue;->offer(Ljava/lang/Object;)Z
-HSPLjava/util/concurrent/LinkedBlockingQueue;->poll()Ljava/lang/Object;+]Ljava/util/concurrent/locks/ReentrantLock;Ljava/util/concurrent/locks/ReentrantLock;]Ljava/util/concurrent/locks/Condition;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
+HSPLjava/util/concurrent/LinkedBlockingQueue;->offer(Ljava/lang/Object;)Z+]Ljava/util/concurrent/locks/ReentrantLock;Ljava/util/concurrent/locks/ReentrantLock;]Ljava/util/concurrent/locks/Condition;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
+HSPLjava/util/concurrent/LinkedBlockingQueue;->poll()Ljava/lang/Object;
HSPLjava/util/concurrent/LinkedBlockingQueue;->poll(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;
-HSPLjava/util/concurrent/LinkedBlockingQueue;->put(Ljava/lang/Object;)V+]Ljava/util/concurrent/locks/ReentrantLock;Ljava/util/concurrent/locks/ReentrantLock;]Ljava/util/concurrent/locks/Condition;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
+HSPLjava/util/concurrent/LinkedBlockingQueue;->put(Ljava/lang/Object;)V
HSPLjava/util/concurrent/LinkedBlockingQueue;->signalNotEmpty()V
HSPLjava/util/concurrent/LinkedBlockingQueue;->signalNotFull()V
HSPLjava/util/concurrent/LinkedBlockingQueue;->size()I
-HSPLjava/util/concurrent/LinkedBlockingQueue;->take()Ljava/lang/Object;
+HSPLjava/util/concurrent/LinkedBlockingQueue;->take()Ljava/lang/Object;+]Ljava/util/concurrent/locks/ReentrantLock;Ljava/util/concurrent/locks/ReentrantLock;]Ljava/util/concurrent/locks/Condition;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
HSPLjava/util/concurrent/PriorityBlockingQueue;-><init>()V
HSPLjava/util/concurrent/PriorityBlockingQueue;-><init>(ILjava/util/Comparator;)V
HSPLjava/util/concurrent/PriorityBlockingQueue;->add(Ljava/lang/Object;)Z
@@ -6895,7 +6998,7 @@
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->poll(JLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/RunnableScheduledFuture;
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->remove(Ljava/lang/Object;)Z
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->setIndex(Ljava/util/concurrent/RunnableScheduledFuture;I)V
-HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->siftDown(ILjava/util/concurrent/RunnableScheduledFuture;)V+]Ljava/util/concurrent/RunnableScheduledFuture;Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;
+HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->siftDown(ILjava/util/concurrent/RunnableScheduledFuture;)V
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->siftUp(ILjava/util/concurrent/RunnableScheduledFuture;)V
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->size()I
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;->take()Ljava/lang/Object;
@@ -6905,7 +7008,7 @@
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;-><init>(Ljava/util/concurrent/ScheduledThreadPoolExecutor;Ljava/lang/Runnable;Ljava/lang/Object;JJJ)V
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;-><init>(Ljava/util/concurrent/ScheduledThreadPoolExecutor;Ljava/util/concurrent/Callable;JJ)V
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;->cancel(Z)Z
-HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;->compareTo(Ljava/lang/Object;)I+]Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;
+HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;->compareTo(Ljava/lang/Object;)I
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;->compareTo(Ljava/util/concurrent/Delayed;)I
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;->getDelay(Ljava/util/concurrent/TimeUnit;)J
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;->isPeriodic()Z
@@ -6914,7 +7017,7 @@
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;-><init>(I)V
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;-><init>(ILjava/util/concurrent/ThreadFactory;)V
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;-><init>(ILjava/util/concurrent/ThreadFactory;Ljava/util/concurrent/RejectedExecutionHandler;)V
-HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;->canRunInCurrentRunState(Ljava/util/concurrent/RunnableScheduledFuture;)Z+]Ljava/util/concurrent/ScheduledThreadPoolExecutor;missing_types]Ljava/util/concurrent/RunnableScheduledFuture;Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;
+HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;->canRunInCurrentRunState(Ljava/util/concurrent/RunnableScheduledFuture;)Z
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;->decorateTask(Ljava/lang/Runnable;Ljava/util/concurrent/RunnableScheduledFuture;)Ljava/util/concurrent/RunnableScheduledFuture;
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;->decorateTask(Ljava/util/concurrent/Callable;Ljava/util/concurrent/RunnableScheduledFuture;)Ljava/util/concurrent/RunnableScheduledFuture;
HSPLjava/util/concurrent/ScheduledThreadPoolExecutor;->delayedExecute(Ljava/util/concurrent/RunnableScheduledFuture;)V
@@ -6955,14 +7058,11 @@
HSPLjava/util/concurrent/SynchronousQueue$TransferStack$SNode;-><init>(Ljava/lang/Object;)V
HSPLjava/util/concurrent/SynchronousQueue$TransferStack$SNode;->casNext(Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;)Z
HSPLjava/util/concurrent/SynchronousQueue$TransferStack$SNode;->isCancelled()Z
-HSPLjava/util/concurrent/SynchronousQueue$TransferStack$SNode;->tryCancel()V
HSPLjava/util/concurrent/SynchronousQueue$TransferStack$SNode;->tryMatch(Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;)Z
HSPLjava/util/concurrent/SynchronousQueue$TransferStack;-><init>()V
-HSPLjava/util/concurrent/SynchronousQueue$TransferStack;->awaitFulfill(Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;ZJ)Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;+]Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;]Ljava/lang/Thread;Ljava/lang/Thread;]Ljava/util/concurrent/SynchronousQueue$TransferStack;Ljava/util/concurrent/SynchronousQueue$TransferStack;
HSPLjava/util/concurrent/SynchronousQueue$TransferStack;->casHead(Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;)Z
HSPLjava/util/concurrent/SynchronousQueue$TransferStack;->clean(Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;)V
HSPLjava/util/concurrent/SynchronousQueue$TransferStack;->isFulfilling(I)Z
-HSPLjava/util/concurrent/SynchronousQueue$TransferStack;->shouldSpin(Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;)Z
HSPLjava/util/concurrent/SynchronousQueue$TransferStack;->snode(Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;Ljava/lang/Object;Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;I)Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;
HSPLjava/util/concurrent/SynchronousQueue$TransferStack;->transfer(Ljava/lang/Object;ZJ)Ljava/lang/Object;+]Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;Ljava/util/concurrent/SynchronousQueue$TransferStack$SNode;]Ljava/util/concurrent/SynchronousQueue$TransferStack;Ljava/util/concurrent/SynchronousQueue$TransferStack;
HSPLjava/util/concurrent/SynchronousQueue$Transferer;-><init>()V
@@ -6970,9 +7070,11 @@
HSPLjava/util/concurrent/SynchronousQueue;-><init>(Z)V
HSPLjava/util/concurrent/SynchronousQueue;->isEmpty()Z
HSPLjava/util/concurrent/SynchronousQueue;->offer(Ljava/lang/Object;)Z
-HSPLjava/util/concurrent/SynchronousQueue;->poll(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;+]Ljava/util/concurrent/SynchronousQueue$Transferer;Ljava/util/concurrent/SynchronousQueue$TransferStack;]Ljava/util/concurrent/TimeUnit;Ljava/util/concurrent/TimeUnit;
+HSPLjava/util/concurrent/SynchronousQueue;->poll(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;
HSPLjava/util/concurrent/SynchronousQueue;->size()I
HSPLjava/util/concurrent/SynchronousQueue;->take()Ljava/lang/Object;
+HSPLjava/util/concurrent/ThreadLocalRandom;-><clinit>()V
+HSPLjava/util/concurrent/ThreadLocalRandom;-><init>()V
HSPLjava/util/concurrent/ThreadLocalRandom;->current()Ljava/util/concurrent/ThreadLocalRandom;
HSPLjava/util/concurrent/ThreadLocalRandom;->getProbe()I
HSPLjava/util/concurrent/ThreadLocalRandom;->localInit()V
@@ -6981,17 +7083,18 @@
HSPLjava/util/concurrent/ThreadLocalRandom;->nextInt()I
HSPLjava/util/concurrent/ThreadLocalRandom;->nextSecondarySeed()I
HSPLjava/util/concurrent/ThreadLocalRandom;->nextSeed()J
+HSPLjava/util/concurrent/ThreadLocalRandom;->setSeed(J)V
HSPLjava/util/concurrent/ThreadPoolExecutor$DiscardPolicy;-><init>()V
HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;-><init>(Ljava/util/concurrent/ThreadPoolExecutor;Ljava/lang/Runnable;)V
HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->interruptIfStarted()V
HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->isHeldExclusively()Z
HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->isLocked()Z
-HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->lock()V
+HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->lock()V+]Ljava/util/concurrent/ThreadPoolExecutor$Worker;Ljava/util/concurrent/ThreadPoolExecutor$Worker;
HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->run()V
-HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->tryAcquire(I)Z
+HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->tryAcquire(I)Z+]Ljava/util/concurrent/ThreadPoolExecutor$Worker;Ljava/util/concurrent/ThreadPoolExecutor$Worker;
HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->tryLock()Z
-HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->tryRelease(I)Z
-HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->unlock()V
+HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->tryRelease(I)Z+]Ljava/util/concurrent/ThreadPoolExecutor$Worker;Ljava/util/concurrent/ThreadPoolExecutor$Worker;
+HSPLjava/util/concurrent/ThreadPoolExecutor$Worker;->unlock()V+]Ljava/util/concurrent/ThreadPoolExecutor$Worker;Ljava/util/concurrent/ThreadPoolExecutor$Worker;
HSPLjava/util/concurrent/ThreadPoolExecutor;-><init>(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;)V
HSPLjava/util/concurrent/ThreadPoolExecutor;-><init>(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/RejectedExecutionHandler;)V
HSPLjava/util/concurrent/ThreadPoolExecutor;-><init>(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/ThreadFactory;)V
@@ -7009,19 +7112,19 @@
HSPLjava/util/concurrent/ThreadPoolExecutor;->decrementWorkerCount()V
HSPLjava/util/concurrent/ThreadPoolExecutor;->drainQueue()Ljava/util/List;
HSPLjava/util/concurrent/ThreadPoolExecutor;->ensurePrestart()V
-HSPLjava/util/concurrent/ThreadPoolExecutor;->execute(Ljava/lang/Runnable;)V
+HSPLjava/util/concurrent/ThreadPoolExecutor;->execute(Ljava/lang/Runnable;)V+]Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/SynchronousQueue;,Ljava/util/concurrent/LinkedBlockingQueue;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
HSPLjava/util/concurrent/ThreadPoolExecutor;->finalize()V
HSPLjava/util/concurrent/ThreadPoolExecutor;->getCorePoolSize()I
HSPLjava/util/concurrent/ThreadPoolExecutor;->getMaximumPoolSize()I
HSPLjava/util/concurrent/ThreadPoolExecutor;->getQueue()Ljava/util/concurrent/BlockingQueue;
HSPLjava/util/concurrent/ThreadPoolExecutor;->getRejectedExecutionHandler()Ljava/util/concurrent/RejectedExecutionHandler;
-HSPLjava/util/concurrent/ThreadPoolExecutor;->getTask()Ljava/lang/Runnable;
+HSPLjava/util/concurrent/ThreadPoolExecutor;->getTask()Ljava/lang/Runnable;+]Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/ScheduledThreadPoolExecutor$DelayedWorkQueue;,Ljava/util/concurrent/SynchronousQueue;,Ljava/util/concurrent/LinkedBlockingQueue;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
HSPLjava/util/concurrent/ThreadPoolExecutor;->getThreadFactory()Ljava/util/concurrent/ThreadFactory;
HSPLjava/util/concurrent/ThreadPoolExecutor;->interruptIdleWorkers()V
HSPLjava/util/concurrent/ThreadPoolExecutor;->interruptIdleWorkers(Z)V
HSPLjava/util/concurrent/ThreadPoolExecutor;->interruptWorkers()V
HSPLjava/util/concurrent/ThreadPoolExecutor;->isRunning(I)Z
-HSPLjava/util/concurrent/ThreadPoolExecutor;->isShutdown()Z+]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
+HSPLjava/util/concurrent/ThreadPoolExecutor;->isShutdown()Z
HSPLjava/util/concurrent/ThreadPoolExecutor;->isTerminated()Z
HSPLjava/util/concurrent/ThreadPoolExecutor;->onShutdown()V
HSPLjava/util/concurrent/ThreadPoolExecutor;->prestartAllCoreThreads()I
@@ -7032,7 +7135,7 @@
HSPLjava/util/concurrent/ThreadPoolExecutor;->runStateAtLeast(II)Z
HSPLjava/util/concurrent/ThreadPoolExecutor;->runStateLessThan(II)Z
HSPLjava/util/concurrent/ThreadPoolExecutor;->runStateOf(I)I
-HSPLjava/util/concurrent/ThreadPoolExecutor;->runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V
+HSPLjava/util/concurrent/ThreadPoolExecutor;->runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V+]Ljava/util/concurrent/ThreadPoolExecutor;Ljava/util/concurrent/ThreadPoolExecutor;,Ljava/util/concurrent/ScheduledThreadPoolExecutor;]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;]Ljava/lang/Runnable;missing_types]Ljava/util/concurrent/ThreadPoolExecutor$Worker;Ljava/util/concurrent/ThreadPoolExecutor$Worker;
HSPLjava/util/concurrent/ThreadPoolExecutor;->setCorePoolSize(I)V
HSPLjava/util/concurrent/ThreadPoolExecutor;->setKeepAliveTime(JLjava/util/concurrent/TimeUnit;)V
HSPLjava/util/concurrent/ThreadPoolExecutor;->setMaximumPoolSize(I)V
@@ -7044,7 +7147,7 @@
HSPLjava/util/concurrent/ThreadPoolExecutor;->toString()Ljava/lang/String;
HSPLjava/util/concurrent/ThreadPoolExecutor;->tryTerminate()V
HSPLjava/util/concurrent/ThreadPoolExecutor;->workerCountOf(I)I
-HSPLjava/util/concurrent/TimeUnit;->convert(JLjava/util/concurrent/TimeUnit;)J+]Ljava/util/concurrent/TimeUnit;Ljava/util/concurrent/TimeUnit;
+HSPLjava/util/concurrent/TimeUnit;->convert(JLjava/util/concurrent/TimeUnit;)J
HSPLjava/util/concurrent/TimeUnit;->cvt(JJJ)J
HSPLjava/util/concurrent/TimeUnit;->sleep(J)V
HSPLjava/util/concurrent/TimeUnit;->toDays(J)J
@@ -7076,9 +7179,10 @@
HSPLjava/util/concurrent/atomic/AtomicInteger;->getAndIncrement()I
HSPLjava/util/concurrent/atomic/AtomicInteger;->getAndSet(I)I
HSPLjava/util/concurrent/atomic/AtomicInteger;->incrementAndGet()I
-HSPLjava/util/concurrent/atomic/AtomicInteger;->intValue()I+]Ljava/util/concurrent/atomic/AtomicInteger;Ljava/util/concurrent/atomic/AtomicInteger;
+HSPLjava/util/concurrent/atomic/AtomicInteger;->intValue()I
HSPLjava/util/concurrent/atomic/AtomicInteger;->lazySet(I)V
HSPLjava/util/concurrent/atomic/AtomicInteger;->set(I)V
+HSPLjava/util/concurrent/atomic/AtomicInteger;->weakCompareAndSetVolatile(II)Z
HSPLjava/util/concurrent/atomic/AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl;-><init>(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)V
HSPLjava/util/concurrent/atomic/AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl;->accessCheck(Ljava/lang/Object;)V
HSPLjava/util/concurrent/atomic/AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl;->compareAndSet(Ljava/lang/Object;II)Z
@@ -7122,10 +7226,12 @@
HSPLjava/util/concurrent/atomic/AtomicReferenceArray;-><init>(I)V
HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->compareAndSet(ILjava/lang/Object;Ljava/lang/Object;)Z
HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->get(I)Ljava/lang/Object;
+HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->getAcquire(I)Ljava/lang/Object;
HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->getAndSet(ILjava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->lazySet(ILjava/lang/Object;)V
HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->length()I
HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->set(ILjava/lang/Object;)V
+HSPLjava/util/concurrent/atomic/AtomicReferenceArray;->setRelease(ILjava/lang/Object;)V
HSPLjava/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl;-><init>(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)V
HSPLjava/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl;->accessCheck(Ljava/lang/Object;)V+]Ljava/lang/Class;Ljava/lang/Class;
HSPLjava/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl;->compareAndSet(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
@@ -7144,69 +7250,46 @@
HSPLjava/util/concurrent/atomic/Striped64;->casBase(JJ)Z
HSPLjava/util/concurrent/atomic/Striped64;->casCellsBusy()Z
HSPLjava/util/concurrent/atomic/Striped64;->getProbe()I
-HSPLjava/util/concurrent/atomic/Striped64;->longAccumulate(JLjava/util/function/LongBinaryOperator;Z)V
HSPLjava/util/concurrent/locks/AbstractOwnableSynchronizer;-><init>()V
HSPLjava/util/concurrent/locks/AbstractOwnableSynchronizer;->getExclusiveOwnerThread()Ljava/lang/Thread;
HSPLjava/util/concurrent/locks/AbstractOwnableSynchronizer;->setExclusiveOwnerThread(Ljava/lang/Thread;)V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;-><init>()V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;->block()Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;->isReleasable()Z+]Ljava/lang/Thread;missing_types
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;-><init>(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->addConditionWaiter()Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->await()V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->await()V+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->awaitNanos(J)J
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->checkInterruptWhileWaiting(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)I
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->doSignal(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->doSignalAll(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->hasWaiters()Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->enableWait(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;)I+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->hasWaiters()Z
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->isOwnedBy(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->reportInterruptAfterWait(I)V
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->signal()V+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->signalAll()V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;->unlinkCancelledWaiters()V
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;-><init>()V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;-><init>(I)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;-><init>(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;->compareAndSetNext(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;->compareAndSetWaitStatus(II)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;->isShared()Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;->predecessor()Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;->setPrevRelaxed(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;->setStatusRelaxed(I)V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->-$$Nest$sfgetU()Ljdk/internal/misc/Unsafe;
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;-><init>()V
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquire(I)V+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;,Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;,Ljava/util/concurrent/ThreadPoolExecutor$Worker;
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquireInterruptibly(I)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquireQueued(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;I)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquireShared(I)V+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquire(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;IZZZJ)I+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ExclusiveNode;,Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;,Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$SharedNode;]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;,Ljava/util/concurrent/CountDownLatch$Sync;,Ljava/util/concurrent/Semaphore$FairSync;,Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;,Ljava/util/concurrent/Semaphore$NonfairSync;,Ljava/util/concurrent/ThreadPoolExecutor$Worker;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquireInterruptibly(I)V+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquireShared(I)V
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->acquireSharedInterruptibly(I)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->addWaiter(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->apparentlyFirstQueuedIsExclusive()Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->cancelAcquire(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->casTail(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z+]Ljdk/internal/misc/Unsafe;Ljdk/internal/misc/Unsafe;
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->compareAndSetState(II)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->compareAndSetTail(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->doAcquireInterruptibly(I)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->doAcquireShared(I)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->doAcquireSharedInterruptibly(I)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->doAcquireSharedNanos(IJ)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->doReleaseShared()V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->enq(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->findNodeFromTail(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->fullyRelease(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)I
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->enqueue(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->getState()I
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->hasQueuedPredecessors()Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->hasWaiters(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->initializeSyncQueue()V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->isOnSyncQueue(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->owns(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->parkAndCheckInterrupt()Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->release(I)Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;,Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;,Ljava/util/concurrent/locks/ReentrantLock$FairSync;,Ljava/util/concurrent/ThreadPoolExecutor$Worker;
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->releaseShared(I)Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->selfInterrupt()V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->setHead(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->setHeadAndPropagate(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;I)V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->hasWaiters(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Z
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->owns(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Z
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->release(I)Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;,Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;,Ljava/util/concurrent/ThreadPoolExecutor$Worker;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->releaseShared(I)Z
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->setState(I)V
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->shouldParkAfterFailedAcquire(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->transferAfterCancelledWait(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->transferForSignal(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->tryAcquireNanos(IJ)Z+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->signalNext(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V+]Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;,Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$SharedNode;,Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ExclusiveNode;
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->tryAcquireNanos(IJ)Z
HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->tryAcquireSharedNanos(IJ)Z
-HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->unparkSuccessor(Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;)V
+HSPLjava/util/concurrent/locks/AbstractQueuedSynchronizer;->tryInitializeHead()V
+HSPLjava/util/concurrent/locks/LockSupport;->park()V+]Ljdk/internal/misc/Unsafe;Ljdk/internal/misc/Unsafe;
HSPLjava/util/concurrent/locks/LockSupport;->park(Ljava/lang/Object;)V
HSPLjava/util/concurrent/locks/LockSupport;->parkNanos(J)V
HSPLjava/util/concurrent/locks/LockSupport;->parkNanos(Ljava/lang/Object;J)V+]Ljdk/internal/misc/Unsafe;Ljdk/internal/misc/Unsafe;
@@ -7215,31 +7298,31 @@
HSPLjava/util/concurrent/locks/ReentrantLock$FairSync;-><init>()V
HSPLjava/util/concurrent/locks/ReentrantLock$FairSync;->tryAcquire(I)Z
HSPLjava/util/concurrent/locks/ReentrantLock$NonfairSync;-><init>()V
+HSPLjava/util/concurrent/locks/ReentrantLock$NonfairSync;->initialTryLock()Z+]Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/ReentrantLock$NonfairSync;->tryAcquire(I)Z+]Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/ReentrantLock$Sync;-><init>()V
HSPLjava/util/concurrent/locks/ReentrantLock$Sync;->isHeldExclusively()Z+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/ReentrantLock$Sync;->newCondition()Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;
-HSPLjava/util/concurrent/locks/ReentrantLock$Sync;->nonfairTryAcquire(I)Z+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/ReentrantLock$Sync;->tryRelease(I)Z+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/ReentrantLock;-><init>()V
HSPLjava/util/concurrent/locks/ReentrantLock;-><init>(Z)V
-HSPLjava/util/concurrent/locks/ReentrantLock;->hasWaiters(Ljava/util/concurrent/locks/Condition;)Z+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
-HSPLjava/util/concurrent/locks/ReentrantLock;->isHeldByCurrentThread()Z+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
+HSPLjava/util/concurrent/locks/ReentrantLock;->hasWaiters(Ljava/util/concurrent/locks/Condition;)Z
+HSPLjava/util/concurrent/locks/ReentrantLock;->isHeldByCurrentThread()Z
HSPLjava/util/concurrent/locks/ReentrantLock;->lock()V+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
-HSPLjava/util/concurrent/locks/ReentrantLock;->lockInterruptibly()V
+HSPLjava/util/concurrent/locks/ReentrantLock;->lockInterruptibly()V+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/ReentrantLock;->newCondition()Ljava/util/concurrent/locks/Condition;
HSPLjava/util/concurrent/locks/ReentrantLock;->tryLock()Z
-HSPLjava/util/concurrent/locks/ReentrantLock;->tryLock(JLjava/util/concurrent/TimeUnit;)Z+]Ljava/util/concurrent/TimeUnit;Ljava/util/concurrent/TimeUnit;]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
+HSPLjava/util/concurrent/locks/ReentrantLock;->tryLock(JLjava/util/concurrent/TimeUnit;)Z
HSPLjava/util/concurrent/locks/ReentrantLock;->unlock()V+]Ljava/util/concurrent/locks/ReentrantLock$Sync;Ljava/util/concurrent/locks/ReentrantLock$NonfairSync;
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$FairSync;-><init>()V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$FairSync;->readerShouldBlock()Z
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$FairSync;->writerShouldBlock()Z
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;-><init>()V
-HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;->readerShouldBlock()Z+]Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;
+HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;->readerShouldBlock()Z
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;->writerShouldBlock()Z
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$ReadLock;-><init>(Ljava/util/concurrent/locks/ReentrantReadWriteLock;)V
-HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$ReadLock;->lock()V+]Ljava/util/concurrent/locks/ReentrantReadWriteLock$Sync;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;
-HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$ReadLock;->unlock()V+]Ljava/util/concurrent/locks/ReentrantReadWriteLock$Sync;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;
+HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$ReadLock;->lock()V
+HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$ReadLock;->unlock()V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync$HoldCounter;-><init>()V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter;-><init>()V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter;->initialValue()Ljava/lang/Object;
@@ -7250,15 +7333,15 @@
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->isHeldExclusively()Z
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->sharedCount(I)I
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->tryAcquire(I)Z
-HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->tryAcquireShared(I)I+]Ljava/util/concurrent/locks/ReentrantReadWriteLock$Sync;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;
+HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->tryAcquireShared(I)I
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->tryRelease(I)Z
-HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->tryReleaseShared(I)Z+]Ljava/util/concurrent/locks/ReentrantReadWriteLock$Sync;Ljava/util/concurrent/locks/ReentrantReadWriteLock$NonfairSync;
+HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->tryReleaseShared(I)Z
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$WriteLock;-><init>(Ljava/util/concurrent/locks/ReentrantReadWriteLock;)V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$WriteLock;->lock()V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock$WriteLock;->unlock()V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock;-><init>()V
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock;-><init>(Z)V
-HSPLjava/util/concurrent/locks/ReentrantReadWriteLock;->readLock()Ljava/util/concurrent/locks/Lock;+]Ljava/util/concurrent/locks/ReentrantReadWriteLock;Ljava/util/concurrent/locks/ReentrantReadWriteLock;
+HSPLjava/util/concurrent/locks/ReentrantReadWriteLock;->readLock()Ljava/util/concurrent/locks/Lock;
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock;->readLock()Ljava/util/concurrent/locks/ReentrantReadWriteLock$ReadLock;
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock;->writeLock()Ljava/util/concurrent/locks/Lock;
HSPLjava/util/concurrent/locks/ReentrantReadWriteLock;->writeLock()Ljava/util/concurrent/locks/ReentrantReadWriteLock$WriteLock;
@@ -7267,7 +7350,7 @@
HSPLjava/util/function/DoubleUnaryOperator$$ExternalSyntheticLambda1;-><init>(Ljava/util/function/DoubleUnaryOperator;Ljava/util/function/DoubleUnaryOperator;)V
HSPLjava/util/function/DoubleUnaryOperator$$ExternalSyntheticLambda1;->applyAsDouble(D)D
HSPLjava/util/function/DoubleUnaryOperator;->andThen(Ljava/util/function/DoubleUnaryOperator;)Ljava/util/function/DoubleUnaryOperator;
-HSPLjava/util/function/DoubleUnaryOperator;->lambda$andThen$1(Ljava/util/function/DoubleUnaryOperator;Ljava/util/function/DoubleUnaryOperator;D)D+]Ljava/util/function/DoubleUnaryOperator;Landroid/graphics/ColorSpace$Rgb$$ExternalSyntheticLambda3;,Landroid/graphics/ColorSpace$Rgb$$ExternalSyntheticLambda1;,Landroid/graphics/ColorSpace$Rgb$$ExternalSyntheticLambda0;
+HSPLjava/util/function/DoubleUnaryOperator;->lambda$andThen$1(Ljava/util/function/DoubleUnaryOperator;Ljava/util/function/DoubleUnaryOperator;D)D
HSPLjava/util/function/Function$$ExternalSyntheticLambda1;-><init>()V
HSPLjava/util/function/Function$$ExternalSyntheticLambda1;->apply(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/function/Function$$ExternalSyntheticLambda2;->apply(Ljava/lang/Object;)Ljava/lang/Object;
@@ -7286,9 +7369,9 @@
HSPLjava/util/jar/Attributes;->entrySet()Ljava/util/Set;
HSPLjava/util/jar/Attributes;->get(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/jar/Attributes;->getValue(Ljava/util/jar/Attributes$Name;)Ljava/lang/String;
-HSPLjava/util/jar/Attributes;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/Map;Ljava/util/HashMap;
+HSPLjava/util/jar/Attributes;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/jar/Attributes;->putValue(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-HSPLjava/util/jar/Attributes;->read(Ljava/util/jar/Manifest$FastInputStream;[B)V+]Ljava/util/jar/Attributes;Ljava/util/jar/Attributes;]Ljava/util/jar/Manifest$FastInputStream;Ljava/util/jar/Manifest$FastInputStream;
+HSPLjava/util/jar/Attributes;->read(Ljava/util/jar/Manifest$FastInputStream;[B)V
HSPLjava/util/jar/Attributes;->size()I
HSPLjava/util/jar/JarEntry;-><init>(Ljava/util/zip/ZipEntry;)V
HSPLjava/util/jar/JarFile$JarFileEntry;-><init>(Ljava/util/jar/JarFile;Ljava/util/zip/ZipEntry;)V
@@ -7321,15 +7404,15 @@
HSPLjava/util/jar/Manifest$FastInputStream;-><init>(Ljava/io/InputStream;I)V
HSPLjava/util/jar/Manifest$FastInputStream;->fill()V
HSPLjava/util/jar/Manifest$FastInputStream;->peek()B
-HSPLjava/util/jar/Manifest$FastInputStream;->readLine([B)I+]Ljava/util/jar/Manifest$FastInputStream;Ljava/util/jar/Manifest$FastInputStream;
+HSPLjava/util/jar/Manifest$FastInputStream;->readLine([B)I
HSPLjava/util/jar/Manifest$FastInputStream;->readLine([BII)I
HSPLjava/util/jar/Manifest;-><init>()V
HSPLjava/util/jar/Manifest;-><init>(Ljava/io/InputStream;)V
-HSPLjava/util/jar/Manifest;->getAttributes(Ljava/lang/String;)Ljava/util/jar/Attributes;+]Ljava/util/jar/Manifest;Ljava/util/jar/Manifest;]Ljava/util/Map;Ljava/util/HashMap;
+HSPLjava/util/jar/Manifest;->getAttributes(Ljava/lang/String;)Ljava/util/jar/Attributes;
HSPLjava/util/jar/Manifest;->getEntries()Ljava/util/Map;
HSPLjava/util/jar/Manifest;->getMainAttributes()Ljava/util/jar/Attributes;
HSPLjava/util/jar/Manifest;->parseName([BI)Ljava/lang/String;
-HSPLjava/util/jar/Manifest;->read(Ljava/io/InputStream;)V+]Ljava/util/jar/Attributes;Ljava/util/jar/Attributes;]Ljava/util/jar/Manifest;Ljava/util/jar/Manifest;]Ljava/util/Map;Ljava/util/HashMap;]Ljava/util/jar/Manifest$FastInputStream;Ljava/util/jar/Manifest$FastInputStream;
+HSPLjava/util/jar/Manifest;->read(Ljava/io/InputStream;)V
HSPLjava/util/jar/Manifest;->toLower(I)I
HSPLjava/util/logging/ConsoleHandler;->close()V
HSPLjava/util/logging/ErrorManager;-><init>()V
@@ -7360,7 +7443,7 @@
HSPLjava/util/logging/Handler;->getFilter()Ljava/util/logging/Filter;
HSPLjava/util/logging/Handler;->getFormatter()Ljava/util/logging/Formatter;
HSPLjava/util/logging/Handler;->getLevel()Ljava/util/logging/Level;
-HSPLjava/util/logging/Handler;->isLoggable(Ljava/util/logging/LogRecord;)Z
+HSPLjava/util/logging/Handler;->isLoggable(Ljava/util/logging/LogRecord;)Z+]Ljava/util/logging/Handler;Ljava/util/logging/FileHandler;]Ljava/util/logging/Level;Ljava/util/logging/Level;]Ljava/util/logging/LogRecord;Ljava/util/logging/LogRecord;
HSPLjava/util/logging/Handler;->setEncoding(Ljava/lang/String;)V
HSPLjava/util/logging/Handler;->setErrorManager(Ljava/util/logging/ErrorManager;)V
HSPLjava/util/logging/Handler;->setFilter(Ljava/util/logging/Filter;)V
@@ -7453,7 +7536,7 @@
HSPLjava/util/logging/Logger;->findResourceBundle(Ljava/lang/String;Z)Ljava/util/ResourceBundle;
HSPLjava/util/logging/Logger;->findSystemResourceBundle(Ljava/util/Locale;)Ljava/util/ResourceBundle;
HSPLjava/util/logging/Logger;->getCallersClassLoader()Ljava/lang/ClassLoader;
-HSPLjava/util/logging/Logger;->getEffectiveLoggerBundle()Ljava/util/logging/Logger$LoggerBundle;
+HSPLjava/util/logging/Logger;->getEffectiveLoggerBundle()Ljava/util/logging/Logger$LoggerBundle;+]Ljava/util/logging/Logger$LoggerBundle;Ljava/util/logging/Logger$LoggerBundle;]Ljava/util/logging/Logger;Ljava/util/logging/LogManager$RootLogger;,Ljava/util/logging/Logger;
HSPLjava/util/logging/Logger;->getHandlers()[Ljava/util/logging/Handler;
HSPLjava/util/logging/Logger;->getLogger(Ljava/lang/String;)Ljava/util/logging/Logger;
HSPLjava/util/logging/Logger;->getName()Ljava/lang/String;
@@ -7463,9 +7546,9 @@
HSPLjava/util/logging/Logger;->getResourceBundleName()Ljava/lang/String;
HSPLjava/util/logging/Logger;->getUseParentHandlers()Z
HSPLjava/util/logging/Logger;->info(Ljava/lang/String;)V
-HSPLjava/util/logging/Logger;->isLoggable(Ljava/util/logging/Level;)Z
+HSPLjava/util/logging/Logger;->isLoggable(Ljava/util/logging/Level;)Z+]Ljava/util/logging/Level;Ljava/util/logging/Level;
HSPLjava/util/logging/Logger;->log(Ljava/util/logging/Level;Ljava/lang/String;)V
-HSPLjava/util/logging/Logger;->log(Ljava/util/logging/LogRecord;)V
+HSPLjava/util/logging/Logger;->log(Ljava/util/logging/LogRecord;)V+]Ljava/util/logging/Handler;Ljava/util/logging/FileHandler;]Ljava/util/logging/LogRecord;Ljava/util/logging/LogRecord;]Ljava/util/logging/Logger;Ljava/util/logging/Logger;
HSPLjava/util/logging/Logger;->logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
HSPLjava/util/logging/Logger;->logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V
HSPLjava/util/logging/Logger;->logp(Ljava/util/logging/Level;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V
@@ -7492,35 +7575,38 @@
HSPLjava/util/logging/StreamHandler;->setEncoding(Ljava/lang/String;)V
HSPLjava/util/logging/StreamHandler;->setOutputStream(Ljava/io/OutputStream;)V
HSPLjava/util/logging/XMLFormatter;-><init>()V
-HSPLjava/util/regex/Matcher;-><init>(Ljava/util/regex/Pattern;Ljava/lang/CharSequence;)V
-HSPLjava/util/regex/Matcher;->appendEvaluated(Ljava/lang/StringBuffer;Ljava/lang/String;)V
-HSPLjava/util/regex/Matcher;->appendReplacement(Ljava/lang/StringBuffer;Ljava/lang/String;)Ljava/util/regex/Matcher;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;]Ljava/util/regex/Matcher;Ljava/util/regex/Matcher;
+HSPLjava/util/regex/Matcher;-><init>(Ljava/util/regex/Pattern;Ljava/lang/CharSequence;)V+]Ljava/util/regex/Matcher;Ljava/util/regex/Matcher;
+HSPLjava/util/regex/Matcher;->appendEvaluated(Ljava/lang/StringBuilder;Ljava/lang/String;)V
+HSPLjava/util/regex/Matcher;->appendReplacement(Ljava/lang/StringBuffer;Ljava/lang/String;)Ljava/util/regex/Matcher;
+HSPLjava/util/regex/Matcher;->appendReplacement(Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/util/regex/Matcher;
+HSPLjava/util/regex/Matcher;->appendReplacementInternal(Ljava/lang/StringBuilder;Ljava/lang/String;)V+]Ljava/util/regex/Matcher;Ljava/util/regex/Matcher;
HSPLjava/util/regex/Matcher;->appendTail(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
+HSPLjava/util/regex/Matcher;->appendTail(Ljava/lang/StringBuilder;)Ljava/lang/StringBuilder;
HSPLjava/util/regex/Matcher;->end()I
-HSPLjava/util/regex/Matcher;->end(I)I+]Ljava/util/regex/Matcher;Ljava/util/regex/Matcher;
+HSPLjava/util/regex/Matcher;->end(I)I
HSPLjava/util/regex/Matcher;->ensureMatch()V
HSPLjava/util/regex/Matcher;->find()Z
HSPLjava/util/regex/Matcher;->find(I)Z
HSPLjava/util/regex/Matcher;->getSubSequence(II)Ljava/lang/CharSequence;
HSPLjava/util/regex/Matcher;->getTextLength()I
HSPLjava/util/regex/Matcher;->group()Ljava/lang/String;
-HSPLjava/util/regex/Matcher;->group(I)Ljava/lang/String;+]Ljava/lang/CharSequence;Ljava/lang/String;]Ljava/util/regex/Matcher;Ljava/util/regex/Matcher;
-HSPLjava/util/regex/Matcher;->groupCount()I
-HSPLjava/util/regex/Matcher;->hitEnd()Z+]Lcom/android/icu/util/regex/MatcherNative;Lcom/android/icu/util/regex/MatcherNative;
-HSPLjava/util/regex/Matcher;->lookingAt()Z+]Lcom/android/icu/util/regex/MatcherNative;Lcom/android/icu/util/regex/MatcherNative;
+HSPLjava/util/regex/Matcher;->group(I)Ljava/lang/String;
+HSPLjava/util/regex/Matcher;->groupCount()I+]Lcom/android/icu/util/regex/MatcherNative;Lcom/android/icu/util/regex/MatcherNative;
+HSPLjava/util/regex/Matcher;->hitEnd()Z
+HSPLjava/util/regex/Matcher;->lookingAt()Z
HSPLjava/util/regex/Matcher;->matches()Z+]Lcom/android/icu/util/regex/MatcherNative;Lcom/android/icu/util/regex/MatcherNative;
HSPLjava/util/regex/Matcher;->pattern()Ljava/util/regex/Pattern;
HSPLjava/util/regex/Matcher;->region(II)Ljava/util/regex/Matcher;
-HSPLjava/util/regex/Matcher;->replaceAll(Ljava/lang/String;)Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;]Ljava/util/regex/Matcher;Ljava/util/regex/Matcher;
+HSPLjava/util/regex/Matcher;->replaceAll(Ljava/lang/String;)Ljava/lang/String;
HSPLjava/util/regex/Matcher;->replaceFirst(Ljava/lang/String;)Ljava/lang/String;
HSPLjava/util/regex/Matcher;->reset()Ljava/util/regex/Matcher;
-HSPLjava/util/regex/Matcher;->reset(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;
-HSPLjava/util/regex/Matcher;->reset(Ljava/lang/CharSequence;II)Ljava/util/regex/Matcher;
-HSPLjava/util/regex/Matcher;->resetForInput()V
+HSPLjava/util/regex/Matcher;->reset(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;+]Ljava/lang/CharSequence;Ljava/lang/String;
+HSPLjava/util/regex/Matcher;->reset(Ljava/lang/CharSequence;II)Ljava/util/regex/Matcher;+]Ljava/lang/CharSequence;Ljava/lang/StringBuilder;,Ljava/lang/String;
+HSPLjava/util/regex/Matcher;->resetForInput()V+]Lcom/android/icu/util/regex/MatcherNative;Lcom/android/icu/util/regex/MatcherNative;
HSPLjava/util/regex/Matcher;->start()I
HSPLjava/util/regex/Matcher;->start(I)I
HSPLjava/util/regex/Matcher;->useAnchoringBounds(Z)Ljava/util/regex/Matcher;
-HSPLjava/util/regex/Matcher;->usePattern(Ljava/util/regex/Pattern;)Ljava/util/regex/Matcher;
+HSPLjava/util/regex/Matcher;->usePattern(Ljava/util/regex/Pattern;)Ljava/util/regex/Matcher;+]Ljava/util/regex/Matcher;Ljava/util/regex/Matcher;
HSPLjava/util/regex/Matcher;->useTransparentBounds(Z)Ljava/util/regex/Matcher;
HSPLjava/util/regex/Pattern;-><init>(Ljava/lang/String;I)V
HSPLjava/util/regex/Pattern;->compile()V
@@ -7535,14 +7621,13 @@
HSPLjava/util/regex/Pattern;->split(Ljava/lang/CharSequence;I)[Ljava/lang/String;
HSPLjava/util/regex/Pattern;->toString()Ljava/lang/String;
HSPLjava/util/stream/AbstractPipeline;-><init>(Ljava/util/Spliterator;IZ)V
-HSPLjava/util/stream/AbstractPipeline;-><init>(Ljava/util/stream/AbstractPipeline;I)V+]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/IntPipeline$4;,Ljava/util/stream/ReferencePipeline$4;
+HSPLjava/util/stream/AbstractPipeline;-><init>(Ljava/util/stream/AbstractPipeline;I)V
HSPLjava/util/stream/AbstractPipeline;->close()V
-HSPLjava/util/stream/AbstractPipeline;->copyInto(Ljava/util/stream/Sink;Ljava/util/Spliterator;)V+]Ljava/util/Spliterator;Ljava/util/Spliterators$IntArraySpliterator;,Ljava/util/HashMap$KeySpliterator;]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/IntPipeline$4;,Ljava/util/stream/ReferencePipeline$4;]Ljava/util/stream/Sink;Ljava/util/stream/ReferencePipeline$4$1;,Ljava/util/stream/IntPipeline$4$1;]Ljava/util/stream/StreamOpFlag;Ljava/util/stream/StreamOpFlag;
-HSPLjava/util/stream/AbstractPipeline;->copyIntoWithCancel(Ljava/util/stream/Sink;Ljava/util/Spliterator;)V
-HSPLjava/util/stream/AbstractPipeline;->evaluate(Ljava/util/Spliterator;ZLjava/util/function/IntFunction;)Ljava/util/stream/Node;+]Ljava/util/stream/Node$Builder;Ljava/util/stream/Nodes$IntFixedNodeBuilder;]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/ReferencePipeline$4;
-HSPLjava/util/stream/AbstractPipeline;->evaluate(Ljava/util/stream/TerminalOp;)Ljava/lang/Object;+]Ljava/util/stream/TerminalOp;Ljava/util/stream/ReduceOps$3;]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/IntPipeline$4;
-HSPLjava/util/stream/AbstractPipeline;->evaluateToArrayNode(Ljava/util/function/IntFunction;)Ljava/util/stream/Node;+]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/ReferencePipeline$4;
-HSPLjava/util/stream/AbstractPipeline;->exactOutputSizeIfKnown(Ljava/util/Spliterator;)J+]Ljava/util/Spliterator;Ljava/util/HashMap$KeySpliterator;]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/ReferencePipeline$4;]Ljava/util/stream/StreamOpFlag;Ljava/util/stream/StreamOpFlag;
+HSPLjava/util/stream/AbstractPipeline;->copyInto(Ljava/util/stream/Sink;Ljava/util/Spliterator;)V
+HSPLjava/util/stream/AbstractPipeline;->evaluate(Ljava/util/Spliterator;ZLjava/util/function/IntFunction;)Ljava/util/stream/Node;
+HSPLjava/util/stream/AbstractPipeline;->evaluate(Ljava/util/stream/TerminalOp;)Ljava/lang/Object;
+HSPLjava/util/stream/AbstractPipeline;->evaluateToArrayNode(Ljava/util/function/IntFunction;)Ljava/util/stream/Node;
+HSPLjava/util/stream/AbstractPipeline;->exactOutputSizeIfKnown(Ljava/util/Spliterator;)J
HSPLjava/util/stream/AbstractPipeline;->getStreamAndOpFlags()I
HSPLjava/util/stream/AbstractPipeline;->isParallel()Z
HSPLjava/util/stream/AbstractPipeline;->onClose(Ljava/lang/Runnable;)Ljava/util/stream/BaseStream;
@@ -7550,11 +7635,12 @@
HSPLjava/util/stream/AbstractPipeline;->sourceSpliterator(I)Ljava/util/Spliterator;
HSPLjava/util/stream/AbstractPipeline;->sourceStageSpliterator()Ljava/util/Spliterator;
HSPLjava/util/stream/AbstractPipeline;->spliterator()Ljava/util/Spliterator;
-HSPLjava/util/stream/AbstractPipeline;->wrapAndCopyInto(Ljava/util/stream/Sink;Ljava/util/Spliterator;)Ljava/util/stream/Sink;+]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/IntPipeline$4;,Ljava/util/stream/ReferencePipeline$4;
-HSPLjava/util/stream/AbstractPipeline;->wrapSink(Ljava/util/stream/Sink;)Ljava/util/stream/Sink;+]Ljava/util/stream/AbstractPipeline;Ljava/util/stream/IntPipeline$4;,Ljava/util/stream/ReferencePipeline$4;
+HSPLjava/util/stream/AbstractPipeline;->wrapAndCopyInto(Ljava/util/stream/Sink;Ljava/util/Spliterator;)Ljava/util/stream/Sink;
+HSPLjava/util/stream/AbstractPipeline;->wrapSink(Ljava/util/stream/Sink;)Ljava/util/stream/Sink;
HSPLjava/util/stream/AbstractSpinedBuffer;-><init>()V
HSPLjava/util/stream/AbstractSpinedBuffer;->count()J
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda0;-><init>()V
+HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;Ljava/lang/Object;)V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda15;-><init>()V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda1;-><init>()V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda20;->accept(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -7564,20 +7650,20 @@
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda41;-><init>()V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda41;->get()Ljava/lang/Object;
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda42;-><init>()V
-HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda42;->accept(Ljava/lang/Object;Ljava/lang/Object;)V+]Ljava/util/Set;Ljava/util/HashSet;
+HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda42;->accept(Ljava/lang/Object;Ljava/lang/Object;)V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda50;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;)V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda50;->get()Ljava/lang/Object;
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda51;-><init>()V
-HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda51;->accept(Ljava/lang/Object;Ljava/lang/Object;)V+]Ljava/util/StringJoiner;Ljava/util/StringJoiner;
+HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda51;->accept(Ljava/lang/Object;Ljava/lang/Object;)V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda52;-><init>()V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda53;-><init>()V
-HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda53;->apply(Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/util/StringJoiner;Ljava/util/StringJoiner;
+HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda53;->apply(Ljava/lang/Object;)Ljava/lang/Object;
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda65;->get()Ljava/lang/Object;
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda66;->get()Ljava/lang/Object;
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda74;-><init>()V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda74;->get()Ljava/lang/Object;
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda75;-><init>()V
-HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda75;->accept(Ljava/lang/Object;Ljava/lang/Object;)V+]Ljava/util/List;Ljava/util/ArrayList;
+HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda75;->accept(Ljava/lang/Object;Ljava/lang/Object;)V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda76;-><init>()V
HSPLjava/util/stream/Collectors$$ExternalSyntheticLambda87;-><init>()V
HSPLjava/util/stream/Collectors$CollectorImpl;-><init>(Ljava/util/function/Supplier;Ljava/util/function/BiConsumer;Ljava/util/function/BinaryOperator;Ljava/util/Set;)V
@@ -7595,7 +7681,7 @@
HSPLjava/util/stream/Collectors;->joining(Ljava/lang/CharSequence;)Ljava/util/stream/Collector;
HSPLjava/util/stream/Collectors;->joining(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/util/stream/Collector;
HSPLjava/util/stream/Collectors;->lambda$joining$11(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/util/StringJoiner;
-HSPLjava/util/stream/Collectors;->lambda$uniqKeysMapAccumulator$1(Ljava/util/function/Function;Ljava/util/function/Function;Ljava/util/Map;Ljava/lang/Object;)V+]Ljava/util/Map;Ljava/util/HashMap;
+HSPLjava/util/stream/Collectors;->lambda$uniqKeysMapAccumulator$1(Ljava/util/function/Function;Ljava/util/function/Function;Ljava/util/Map;Ljava/lang/Object;)V
HSPLjava/util/stream/Collectors;->mapMerger(Ljava/util/function/BinaryOperator;)Ljava/util/function/BinaryOperator;
HSPLjava/util/stream/Collectors;->toCollection(Ljava/util/function/Supplier;)Ljava/util/stream/Collector;
HSPLjava/util/stream/Collectors;->toList()Ljava/util/stream/Collector;
@@ -7637,16 +7723,18 @@
HSPLjava/util/stream/ForEachOps$ForEachOp;->get()Ljava/lang/Void;
HSPLjava/util/stream/ForEachOps$ForEachOp;->getOpFlags()I
HSPLjava/util/stream/ForEachOps;->makeRef(Ljava/util/function/Consumer;Z)Ljava/util/stream/TerminalOp;
+HSPLjava/util/stream/IntPipeline$$ExternalSyntheticLambda13;->applyAsInt(II)I
HSPLjava/util/stream/IntPipeline$$ExternalSyntheticLambda7;-><init>()V
HSPLjava/util/stream/IntPipeline$$ExternalSyntheticLambda7;->apply(I)Ljava/lang/Object;
+HSPLjava/util/stream/IntPipeline$$ExternalSyntheticLambda8;-><init>()V
HSPLjava/util/stream/IntPipeline$$ExternalSyntheticLambda8;->apply(I)Ljava/lang/Object;
HSPLjava/util/stream/IntPipeline$4$1;-><init>(Ljava/util/stream/IntPipeline$4;Ljava/util/stream/Sink;)V
HSPLjava/util/stream/IntPipeline$4$1;->accept(I)V
HSPLjava/util/stream/IntPipeline$4;-><init>(Ljava/util/stream/IntPipeline;Ljava/util/stream/AbstractPipeline;Ljava/util/stream/StreamShape;ILjava/util/function/IntFunction;)V
HSPLjava/util/stream/IntPipeline$4;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
HSPLjava/util/stream/IntPipeline$9$1;-><init>(Ljava/util/stream/IntPipeline$9;Ljava/util/stream/Sink;)V
-HSPLjava/util/stream/IntPipeline$9$1;->accept(I)V+]Ljava/util/stream/Sink;megamorphic_types]Ljava/util/function/IntPredicate;missing_types
-HSPLjava/util/stream/IntPipeline$9$1;->begin(J)V+]Ljava/util/stream/Sink;Ljava/util/stream/IntPipeline$4$1;,Ljava/util/stream/ReduceOps$6ReducingSink;,Ljava/util/stream/ForEachOps$ForEachOp$OfInt;,Ljava/util/stream/IntPipeline$3$1;
+HSPLjava/util/stream/IntPipeline$9$1;->accept(I)V
+HSPLjava/util/stream/IntPipeline$9$1;->begin(J)V
HSPLjava/util/stream/IntPipeline$9;-><init>(Ljava/util/stream/IntPipeline;Ljava/util/stream/AbstractPipeline;Ljava/util/stream/StreamShape;ILjava/util/function/IntPredicate;)V
HSPLjava/util/stream/IntPipeline$9;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
HSPLjava/util/stream/IntPipeline$Head;-><init>(Ljava/util/Spliterator;IZ)V
@@ -7659,15 +7747,14 @@
HSPLjava/util/stream/IntPipeline;->adapt(Ljava/util/Spliterator;)Ljava/util/Spliterator$OfInt;
HSPLjava/util/stream/IntPipeline;->adapt(Ljava/util/stream/Sink;)Ljava/util/function/IntConsumer;
HSPLjava/util/stream/IntPipeline;->allMatch(Ljava/util/function/IntPredicate;)Z
-HSPLjava/util/stream/IntPipeline;->boxed()Ljava/util/stream/Stream;+]Ljava/util/stream/IntPipeline;Ljava/util/stream/IntPipeline$Head;
+HSPLjava/util/stream/IntPipeline;->boxed()Ljava/util/stream/Stream;
HSPLjava/util/stream/IntPipeline;->distinct()Ljava/util/stream/IntStream;
HSPLjava/util/stream/IntPipeline;->filter(Ljava/util/function/IntPredicate;)Ljava/util/stream/IntStream;
-HSPLjava/util/stream/IntPipeline;->forEachWithCancel(Ljava/util/Spliterator;Ljava/util/stream/Sink;)V
HSPLjava/util/stream/IntPipeline;->makeNodeBuilder(JLjava/util/function/IntFunction;)Ljava/util/stream/Node$Builder;
HSPLjava/util/stream/IntPipeline;->mapToObj(Ljava/util/function/IntFunction;)Ljava/util/stream/Stream;
HSPLjava/util/stream/IntPipeline;->reduce(ILjava/util/function/IntBinaryOperator;)I
HSPLjava/util/stream/IntPipeline;->sum()I
-HSPLjava/util/stream/IntPipeline;->toArray()[I+]Ljava/util/stream/IntPipeline;Ljava/util/stream/ReferencePipeline$4;]Ljava/util/stream/Node$OfInt;Ljava/util/stream/Nodes$IntFixedNodeBuilder;
+HSPLjava/util/stream/IntPipeline;->toArray()[I
HSPLjava/util/stream/IntStream;->empty()Ljava/util/stream/IntStream;
HSPLjava/util/stream/IntStream;->of([I)Ljava/util/stream/IntStream;
HSPLjava/util/stream/IntStream;->range(II)Ljava/util/stream/IntStream;
@@ -7687,7 +7774,7 @@
HSPLjava/util/stream/MatchOps$1MatchSink;-><init>(Ljava/util/stream/MatchOps$MatchKind;Ljava/util/function/Predicate;)V
HSPLjava/util/stream/MatchOps$1MatchSink;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/MatchOps$2MatchSink;-><init>(Ljava/util/stream/MatchOps$MatchKind;Ljava/util/function/IntPredicate;)V
-HSPLjava/util/stream/MatchOps$2MatchSink;->accept(I)V+]Ljava/util/function/IntPredicate;missing_types
+HSPLjava/util/stream/MatchOps$2MatchSink;->accept(I)V
HSPLjava/util/stream/MatchOps$BooleanTerminalSink;-><init>(Ljava/util/stream/MatchOps$MatchKind;)V
HSPLjava/util/stream/MatchOps$BooleanTerminalSink;->cancellationRequested()Z
HSPLjava/util/stream/MatchOps$BooleanTerminalSink;->getAndClearState()Z
@@ -7720,6 +7807,11 @@
HSPLjava/util/stream/Nodes$IntFixedNodeBuilder;->end()V
HSPLjava/util/stream/Nodes$SpinedNodeBuilder;-><clinit>()V
HSPLjava/util/stream/Nodes$SpinedNodeBuilder;-><init>()V
+HSPLjava/util/stream/Nodes$SpinedNodeBuilder;->asArray(Ljava/util/function/IntFunction;)[Ljava/lang/Object;
+HSPLjava/util/stream/Nodes$SpinedNodeBuilder;->begin(J)V
+HSPLjava/util/stream/Nodes$SpinedNodeBuilder;->build()Ljava/util/stream/Node;
+HSPLjava/util/stream/Nodes$SpinedNodeBuilder;->copyInto([Ljava/lang/Object;I)V
+HSPLjava/util/stream/Nodes$SpinedNodeBuilder;->end()V
HSPLjava/util/stream/Nodes;->builder()Ljava/util/stream/Node$Builder;
HSPLjava/util/stream/Nodes;->builder(JLjava/util/function/IntFunction;)Ljava/util/stream/Node$Builder;
HSPLjava/util/stream/Nodes;->flatten(Ljava/util/stream/Node;Ljava/util/function/IntFunction;)Ljava/util/stream/Node;
@@ -7738,21 +7830,22 @@
HSPLjava/util/stream/ReduceOps$2;->makeSink()Ljava/util/stream/ReduceOps$2ReducingSink;
HSPLjava/util/stream/ReduceOps$2;->makeSink()Ljava/util/stream/ReduceOps$AccumulatingSink;
HSPLjava/util/stream/ReduceOps$2ReducingSink;-><init>(Ljava/util/function/BinaryOperator;)V
-HSPLjava/util/stream/ReduceOps$2ReducingSink;->accept(Ljava/lang/Object;)V+]Ljava/util/function/BinaryOperator;Ljava/util/function/BinaryOperator$$ExternalSyntheticLambda0;
+HSPLjava/util/stream/ReduceOps$2ReducingSink;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/ReduceOps$2ReducingSink;->begin(J)V
HSPLjava/util/stream/ReduceOps$2ReducingSink;->get()Ljava/lang/Object;
HSPLjava/util/stream/ReduceOps$2ReducingSink;->get()Ljava/util/Optional;
HSPLjava/util/stream/ReduceOps$3;-><init>(Ljava/util/stream/StreamShape;Ljava/util/function/BinaryOperator;Ljava/util/function/BiConsumer;Ljava/util/function/Supplier;Ljava/util/stream/Collector;)V
-HSPLjava/util/stream/ReduceOps$3;->getOpFlags()I+]Ljava/util/stream/Collector;Ljava/util/stream/Collectors$CollectorImpl;]Ljava/util/Set;Ljava/util/Collections$UnmodifiableSet;
+HSPLjava/util/stream/ReduceOps$3;->getOpFlags()I
HSPLjava/util/stream/ReduceOps$3;->makeSink()Ljava/util/stream/ReduceOps$3ReducingSink;
HSPLjava/util/stream/ReduceOps$3;->makeSink()Ljava/util/stream/ReduceOps$AccumulatingSink;
HSPLjava/util/stream/ReduceOps$3ReducingSink;-><init>(Ljava/util/function/Supplier;Ljava/util/function/BiConsumer;Ljava/util/function/BinaryOperator;)V
HSPLjava/util/stream/ReduceOps$3ReducingSink;->accept(Ljava/lang/Object;)V
-HSPLjava/util/stream/ReduceOps$3ReducingSink;->begin(J)V+]Ljava/util/function/Supplier;Ljava/util/stream/Collectors$$ExternalSyntheticLambda74;
+HSPLjava/util/stream/ReduceOps$3ReducingSink;->begin(J)V
HSPLjava/util/stream/ReduceOps$5;-><init>(Ljava/util/stream/StreamShape;Ljava/util/function/IntBinaryOperator;I)V
HSPLjava/util/stream/ReduceOps$5;->makeSink()Ljava/util/stream/ReduceOps$5ReducingSink;
HSPLjava/util/stream/ReduceOps$5;->makeSink()Ljava/util/stream/ReduceOps$AccumulatingSink;
HSPLjava/util/stream/ReduceOps$5ReducingSink;-><init>(ILjava/util/function/IntBinaryOperator;)V
+HSPLjava/util/stream/ReduceOps$5ReducingSink;->accept(I)V
HSPLjava/util/stream/ReduceOps$5ReducingSink;->begin(J)V
HSPLjava/util/stream/ReduceOps$5ReducingSink;->get()Ljava/lang/Integer;
HSPLjava/util/stream/ReduceOps$5ReducingSink;->get()Ljava/lang/Object;
@@ -7767,16 +7860,16 @@
HSPLjava/util/stream/ReduceOps$Box;-><init>()V
HSPLjava/util/stream/ReduceOps$Box;->get()Ljava/lang/Object;
HSPLjava/util/stream/ReduceOps$ReduceOp;-><init>(Ljava/util/stream/StreamShape;)V
-HSPLjava/util/stream/ReduceOps$ReduceOp;->evaluateSequential(Ljava/util/stream/PipelineHelper;Ljava/util/Spliterator;)Ljava/lang/Object;+]Ljava/util/stream/ReduceOps$AccumulatingSink;Ljava/util/stream/ReduceOps$3ReducingSink;]Ljava/util/stream/PipelineHelper;Ljava/util/stream/IntPipeline$4;]Ljava/util/stream/ReduceOps$ReduceOp;Ljava/util/stream/ReduceOps$3;
+HSPLjava/util/stream/ReduceOps$ReduceOp;->evaluateSequential(Ljava/util/stream/PipelineHelper;Ljava/util/Spliterator;)Ljava/lang/Object;
HSPLjava/util/stream/ReduceOps;->makeDouble(Ljava/util/function/DoubleBinaryOperator;)Ljava/util/stream/TerminalOp;
HSPLjava/util/stream/ReduceOps;->makeInt(ILjava/util/function/IntBinaryOperator;)Ljava/util/stream/TerminalOp;
HSPLjava/util/stream/ReduceOps;->makeLong(JLjava/util/function/LongBinaryOperator;)Ljava/util/stream/TerminalOp;
HSPLjava/util/stream/ReduceOps;->makeRef(Ljava/util/function/BinaryOperator;)Ljava/util/stream/TerminalOp;
-HSPLjava/util/stream/ReduceOps;->makeRef(Ljava/util/stream/Collector;)Ljava/util/stream/TerminalOp;+]Ljava/util/stream/Collector;Ljava/util/stream/Collectors$CollectorImpl;
+HSPLjava/util/stream/ReduceOps;->makeRef(Ljava/util/stream/Collector;)Ljava/util/stream/TerminalOp;
HSPLjava/util/stream/ReferencePipeline$$ExternalSyntheticLambda2;-><init>()V
HSPLjava/util/stream/ReferencePipeline$$ExternalSyntheticLambda2;->applyAsLong(Ljava/lang/Object;)J
HSPLjava/util/stream/ReferencePipeline$2$1;-><init>(Ljava/util/stream/ReferencePipeline$2;Ljava/util/stream/Sink;)V
-HSPLjava/util/stream/ReferencePipeline$2$1;->accept(Ljava/lang/Object;)V+]Ljava/util/stream/Sink;megamorphic_types
+HSPLjava/util/stream/ReferencePipeline$2$1;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/ReferencePipeline$2$1;->begin(J)V
HSPLjava/util/stream/ReferencePipeline$2;-><init>(Ljava/util/stream/ReferencePipeline;Ljava/util/stream/AbstractPipeline;Ljava/util/stream/StreamShape;ILjava/util/function/Predicate;)V
HSPLjava/util/stream/ReferencePipeline$2;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
@@ -7785,7 +7878,7 @@
HSPLjava/util/stream/ReferencePipeline$3;-><init>(Ljava/util/stream/ReferencePipeline;Ljava/util/stream/AbstractPipeline;Ljava/util/stream/StreamShape;ILjava/util/function/Function;)V
HSPLjava/util/stream/ReferencePipeline$3;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
HSPLjava/util/stream/ReferencePipeline$4$1;-><init>(Ljava/util/stream/ReferencePipeline$4;Ljava/util/stream/Sink;)V
-HSPLjava/util/stream/ReferencePipeline$4$1;->accept(Ljava/lang/Object;)V+]Ljava/util/function/ToIntFunction;Landroid/media/AudioPort$$ExternalSyntheticLambda0;]Ljava/util/stream/Sink;Ljava/util/stream/Nodes$IntFixedNodeBuilder;
+HSPLjava/util/stream/ReferencePipeline$4$1;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/ReferencePipeline$4;-><init>(Ljava/util/stream/ReferencePipeline;Ljava/util/stream/AbstractPipeline;Ljava/util/stream/StreamShape;ILjava/util/function/ToIntFunction;)V
HSPLjava/util/stream/ReferencePipeline$4;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
HSPLjava/util/stream/ReferencePipeline$5$1;-><init>(Ljava/util/stream/ReferencePipeline$5;Ljava/util/stream/Sink;)V
@@ -7797,7 +7890,7 @@
HSPLjava/util/stream/ReferencePipeline$6;-><init>(Ljava/util/stream/ReferencePipeline;Ljava/util/stream/AbstractPipeline;Ljava/util/stream/StreamShape;ILjava/util/function/ToDoubleFunction;)V
HSPLjava/util/stream/ReferencePipeline$6;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
HSPLjava/util/stream/ReferencePipeline$7$1;-><init>(Ljava/util/stream/ReferencePipeline$7;Ljava/util/stream/Sink;)V
-HSPLjava/util/stream/ReferencePipeline$7$1;->accept(Ljava/lang/Object;)V+]Ljava/util/function/Function;missing_types]Ljava/util/stream/Stream;Ljava/util/stream/IntPipeline$4;,Ljava/util/stream/ReferencePipeline$Head;,Ljava/util/stream/ReferencePipeline$3;
+HSPLjava/util/stream/ReferencePipeline$7$1;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/ReferencePipeline$7$1;->begin(J)V
HSPLjava/util/stream/ReferencePipeline$7;-><init>(Ljava/util/stream/ReferencePipeline;Ljava/util/stream/AbstractPipeline;Ljava/util/stream/StreamShape;ILjava/util/function/Function;)V
HSPLjava/util/stream/ReferencePipeline$7;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
@@ -7809,17 +7902,17 @@
HSPLjava/util/stream/ReferencePipeline$StatelessOp;->opIsStateful()Z
HSPLjava/util/stream/ReferencePipeline;-><init>(Ljava/util/Spliterator;IZ)V
HSPLjava/util/stream/ReferencePipeline;-><init>(Ljava/util/stream/AbstractPipeline;I)V
-HSPLjava/util/stream/ReferencePipeline;->allMatch(Ljava/util/function/Predicate;)Z+]Ljava/util/stream/ReferencePipeline;Ljava/util/stream/ReferencePipeline$Head;]Ljava/lang/Boolean;Ljava/lang/Boolean;
+HSPLjava/util/stream/ReferencePipeline;->allMatch(Ljava/util/function/Predicate;)Z
HSPLjava/util/stream/ReferencePipeline;->anyMatch(Ljava/util/function/Predicate;)Z
-HSPLjava/util/stream/ReferencePipeline;->collect(Ljava/util/stream/Collector;)Ljava/lang/Object;+]Ljava/util/stream/Collector;Ljava/util/stream/Collectors$CollectorImpl;]Ljava/util/stream/ReferencePipeline;Ljava/util/stream/IntPipeline$4;]Ljava/util/Set;Ljava/util/Collections$UnmodifiableSet;
+HSPLjava/util/stream/ReferencePipeline;->collect(Ljava/util/stream/Collector;)Ljava/lang/Object;
HSPLjava/util/stream/ReferencePipeline;->count()J
HSPLjava/util/stream/ReferencePipeline;->distinct()Ljava/util/stream/Stream;
HSPLjava/util/stream/ReferencePipeline;->filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
HSPLjava/util/stream/ReferencePipeline;->findAny()Ljava/util/Optional;
-HSPLjava/util/stream/ReferencePipeline;->findFirst()Ljava/util/Optional;+]Ljava/util/stream/ReferencePipeline;Ljava/util/stream/ReferencePipeline$2;
+HSPLjava/util/stream/ReferencePipeline;->findFirst()Ljava/util/Optional;
HSPLjava/util/stream/ReferencePipeline;->flatMap(Ljava/util/function/Function;)Ljava/util/stream/Stream;
HSPLjava/util/stream/ReferencePipeline;->forEach(Ljava/util/function/Consumer;)V
-HSPLjava/util/stream/ReferencePipeline;->forEachWithCancel(Ljava/util/Spliterator;Ljava/util/stream/Sink;)V+]Ljava/util/Spliterator;Ljava/util/Spliterators$ArraySpliterator;,Ljava/util/ArrayList$ArrayListSpliterator;]Ljava/util/stream/Sink;Ljava/util/stream/ReferencePipeline$2$1;,Ljava/util/stream/MatchOps$1MatchSink;
+HSPLjava/util/stream/ReferencePipeline;->forEachWithCancel(Ljava/util/Spliterator;Ljava/util/stream/Sink;)Z
HSPLjava/util/stream/ReferencePipeline;->lambda$count$2(Ljava/lang/Object;)J
HSPLjava/util/stream/ReferencePipeline;->makeNodeBuilder(JLjava/util/function/IntFunction;)Ljava/util/stream/Node$Builder;
HSPLjava/util/stream/ReferencePipeline;->map(Ljava/util/function/Function;)Ljava/util/stream/Stream;
@@ -7838,26 +7931,32 @@
HSPLjava/util/stream/Sink$ChainedInt;->end()V
HSPLjava/util/stream/Sink$ChainedReference;-><init>(Ljava/util/stream/Sink;)V
HSPLjava/util/stream/Sink$ChainedReference;->begin(J)V
-HSPLjava/util/stream/Sink$ChainedReference;->cancellationRequested()Z+]Ljava/util/stream/Sink;Ljava/util/stream/FindOps$FindSink$OfRef;,Ljava/util/stream/ReferencePipeline$3$1;,Ljava/util/stream/ReferencePipeline$2$1;,Ljava/util/stream/MatchOps$1MatchSink;
-HSPLjava/util/stream/Sink$ChainedReference;->end()V+]Ljava/util/stream/Sink;Ljava/util/stream/Nodes$IntFixedNodeBuilder;
+HSPLjava/util/stream/Sink$ChainedReference;->cancellationRequested()Z
+HSPLjava/util/stream/Sink$ChainedReference;->end()V
HSPLjava/util/stream/Sink;->begin(J)V
HSPLjava/util/stream/Sink;->end()V
HSPLjava/util/stream/SortedOps$AbstractRefSortingSink;-><init>(Ljava/util/stream/Sink;Ljava/util/Comparator;)V
HSPLjava/util/stream/SortedOps$OfRef;-><init>(Ljava/util/stream/AbstractPipeline;Ljava/util/Comparator;)V
HSPLjava/util/stream/SortedOps$OfRef;->opWrapSink(ILjava/util/stream/Sink;)Ljava/util/stream/Sink;
HSPLjava/util/stream/SortedOps$RefSortingSink$$ExternalSyntheticLambda0;-><init>(Ljava/util/stream/Sink;)V
+HSPLjava/util/stream/SortedOps$RefSortingSink$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/SortedOps$RefSortingSink;-><init>(Ljava/util/stream/Sink;Ljava/util/Comparator;)V
+HSPLjava/util/stream/SortedOps$RefSortingSink;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/SortedOps$RefSortingSink;->begin(J)V
HSPLjava/util/stream/SortedOps$RefSortingSink;->end()V
HSPLjava/util/stream/SortedOps$SizedRefSortingSink;-><init>(Ljava/util/stream/Sink;Ljava/util/Comparator;)V
HSPLjava/util/stream/SortedOps$SizedRefSortingSink;->accept(Ljava/lang/Object;)V
HSPLjava/util/stream/SortedOps$SizedRefSortingSink;->begin(J)V
-HSPLjava/util/stream/SortedOps$SizedRefSortingSink;->end()V+]Ljava/util/stream/Sink;Ljava/util/stream/ReduceOps$3ReducingSink;,Ljava/util/stream/ForEachOps$ForEachOp$OfRef;
+HSPLjava/util/stream/SortedOps$SizedRefSortingSink;->end()V
HSPLjava/util/stream/SortedOps;->makeRef(Ljava/util/stream/AbstractPipeline;Ljava/util/Comparator;)Ljava/util/stream/Stream;
HSPLjava/util/stream/SpinedBuffer;-><init>()V
-HSPLjava/util/stream/SpinedBuffer;->accept(Ljava/lang/Object;)V+]Ljava/util/stream/SpinedBuffer;Ljava/util/stream/Nodes$SpinedNodeBuilder;
+HSPLjava/util/stream/SpinedBuffer;->accept(Ljava/lang/Object;)V
+HSPLjava/util/stream/SpinedBuffer;->asArray(Ljava/util/function/IntFunction;)[Ljava/lang/Object;
+HSPLjava/util/stream/SpinedBuffer;->capacity()J
HSPLjava/util/stream/SpinedBuffer;->clear()V
+HSPLjava/util/stream/SpinedBuffer;->copyInto([Ljava/lang/Object;I)V
HSPLjava/util/stream/SpinedBuffer;->count()J
+HSPLjava/util/stream/SpinedBuffer;->ensureCapacity(J)V
HSPLjava/util/stream/Stream;->builder()Ljava/util/stream/Stream$Builder;
HSPLjava/util/stream/Stream;->concat(Ljava/util/stream/Stream;Ljava/util/stream/Stream;)Ljava/util/stream/Stream;
HSPLjava/util/stream/Stream;->of([Ljava/lang/Object;)Ljava/util/stream/Stream;
@@ -7889,13 +7988,15 @@
HSPLjava/util/zip/CRC32;->update(I)V
HSPLjava/util/zip/CRC32;->update([B)V
HSPLjava/util/zip/CRC32;->update([BII)V
+HSPLjava/util/zip/CRC32;->updateByteBuffer(IJII)I
+HSPLjava/util/zip/CRC32;->updateBytes(I[BII)I
HSPLjava/util/zip/CheckedInputStream;-><init>(Ljava/io/InputStream;Ljava/util/zip/Checksum;)V
HSPLjava/util/zip/CheckedInputStream;->read()I
HSPLjava/util/zip/CheckedInputStream;->read([BII)I
HSPLjava/util/zip/Deflater;-><init>()V
HSPLjava/util/zip/Deflater;-><init>(IZ)V
HSPLjava/util/zip/Deflater;->deflate([BII)I
-HSPLjava/util/zip/Deflater;->deflate([BIII)I+]Ljava/util/zip/ZStreamRef;Ljava/util/zip/ZStreamRef;
+HSPLjava/util/zip/Deflater;->deflate([BIII)I
HSPLjava/util/zip/Deflater;->end()V
HSPLjava/util/zip/Deflater;->ensureOpen()V
HSPLjava/util/zip/Deflater;->finalize()V
@@ -7940,38 +8041,38 @@
HSPLjava/util/zip/Inflater;-><init>(Z)V
HSPLjava/util/zip/Inflater;->end()V
HSPLjava/util/zip/Inflater;->ended()Z
-HSPLjava/util/zip/Inflater;->ensureOpen()V+]Ljava/util/zip/ZStreamRef;Ljava/util/zip/ZStreamRef;
+HSPLjava/util/zip/Inflater;->ensureOpen()V
HSPLjava/util/zip/Inflater;->finalize()V
HSPLjava/util/zip/Inflater;->finished()Z
HSPLjava/util/zip/Inflater;->getBytesRead()J
HSPLjava/util/zip/Inflater;->getBytesWritten()J
HSPLjava/util/zip/Inflater;->getRemaining()I
HSPLjava/util/zip/Inflater;->getTotalOut()I
-HSPLjava/util/zip/Inflater;->inflate([BII)I+]Ljava/util/zip/ZStreamRef;Ljava/util/zip/ZStreamRef;
+HSPLjava/util/zip/Inflater;->inflate([BII)I
HSPLjava/util/zip/Inflater;->needsDictionary()Z
HSPLjava/util/zip/Inflater;->needsInput()Z
-HSPLjava/util/zip/Inflater;->reset()V+]Ljava/util/zip/ZStreamRef;Ljava/util/zip/ZStreamRef;
+HSPLjava/util/zip/Inflater;->reset()V
HSPLjava/util/zip/Inflater;->setInput([BII)V
HSPLjava/util/zip/InflaterInputStream;-><init>(Ljava/io/InputStream;Ljava/util/zip/Inflater;)V
HSPLjava/util/zip/InflaterInputStream;-><init>(Ljava/io/InputStream;Ljava/util/zip/Inflater;I)V
HSPLjava/util/zip/InflaterInputStream;->available()I
HSPLjava/util/zip/InflaterInputStream;->close()V
HSPLjava/util/zip/InflaterInputStream;->ensureOpen()V
-HSPLjava/util/zip/InflaterInputStream;->fill()V+]Ljava/io/InputStream;Ljava/io/BufferedInputStream;,Ljava/io/ByteArrayInputStream;,Lcom/android/okhttp/okio/RealBufferedSource$1;,Ljava/io/PushbackInputStream;]Ljava/util/zip/Inflater;Ljava/util/zip/Inflater;
+HSPLjava/util/zip/InflaterInputStream;->fill()V
HSPLjava/util/zip/InflaterInputStream;->read()I
-HSPLjava/util/zip/InflaterInputStream;->read([BII)I+]Ljava/util/zip/InflaterInputStream;Ljava/util/zip/GZIPInputStream;,Ljava/util/zip/ZipFile$ZipFileInflaterInputStream;,Ljava/util/zip/ZipInputStream;]Ljava/util/zip/Inflater;Ljava/util/zip/Inflater;
+HSPLjava/util/zip/InflaterInputStream;->read([BII)I
HSPLjava/util/zip/ZStreamRef;-><init>(J)V
HSPLjava/util/zip/ZStreamRef;->address()J
HSPLjava/util/zip/ZStreamRef;->clear()V
HSPLjava/util/zip/ZipCoder;-><init>(Ljava/nio/charset/Charset;)V
-HSPLjava/util/zip/ZipCoder;->decoder()Ljava/nio/charset/CharsetDecoder;
+HSPLjava/util/zip/ZipCoder;->decoder()Ljava/nio/charset/CharsetDecoder;+]Ljava/nio/charset/Charset;Lcom/android/icu/charset/CharsetICU;]Ljava/nio/charset/CharsetDecoder;Lcom/android/icu/charset/CharsetDecoderICU;
HSPLjava/util/zip/ZipCoder;->encoder()Ljava/nio/charset/CharsetEncoder;
HSPLjava/util/zip/ZipCoder;->get(Ljava/nio/charset/Charset;)Ljava/util/zip/ZipCoder;
-HSPLjava/util/zip/ZipCoder;->getBytes(Ljava/lang/String;)[B+]Ljava/lang/String;Ljava/lang/String;]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;]Ljava/nio/charset/CharsetEncoder;Lcom/android/icu/charset/CharsetEncoderICU;]Ljava/nio/charset/CoderResult;Ljava/nio/charset/CoderResult;
+HSPLjava/util/zip/ZipCoder;->getBytes(Ljava/lang/String;)[B
HSPLjava/util/zip/ZipCoder;->isUTF8()Z
HSPLjava/util/zip/ZipCoder;->toString([BI)Ljava/lang/String;+]Ljava/nio/CharBuffer;Ljava/nio/HeapCharBuffer;]Ljava/nio/charset/CoderResult;Ljava/nio/charset/CoderResult;]Ljava/nio/charset/CharsetDecoder;Lcom/android/icu/charset/CharsetDecoderICU;
HSPLjava/util/zip/ZipEntry;-><init>()V
-HSPLjava/util/zip/ZipEntry;-><init>(Ljava/lang/String;)V+]Ljava/lang/String;Ljava/lang/String;
+HSPLjava/util/zip/ZipEntry;-><init>(Ljava/lang/String;)V
HSPLjava/util/zip/ZipEntry;-><init>(Ljava/util/zip/ZipEntry;)V
HSPLjava/util/zip/ZipEntry;->getCompressedSize()J
HSPLjava/util/zip/ZipEntry;->getMethod()I
@@ -8007,10 +8108,11 @@
HSPLjava/util/zip/ZipFile;->ensureOpenOrZipException()V
HSPLjava/util/zip/ZipFile;->entries()Ljava/util/Enumeration;
HSPLjava/util/zip/ZipFile;->finalize()V
-HSPLjava/util/zip/ZipFile;->getEntry(Ljava/lang/String;)Ljava/util/zip/ZipEntry;+]Ljava/util/zip/ZipCoder;Ljava/util/zip/ZipCoder;
+HSPLjava/util/zip/ZipFile;->getEntry(Ljava/lang/String;)Ljava/util/zip/ZipEntry;
HSPLjava/util/zip/ZipFile;->getInflater()Ljava/util/zip/Inflater;
HSPLjava/util/zip/ZipFile;->getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;
-HSPLjava/util/zip/ZipFile;->getZipEntry(Ljava/lang/String;J)Ljava/util/zip/ZipEntry;+]Ljava/util/zip/ZipEntry;Ljava/util/zip/ZipEntry;]Ljava/util/zip/ZipCoder;Ljava/util/zip/ZipCoder;
+HSPLjava/util/zip/ZipFile;->getZipEntry(Ljava/lang/String;J)Ljava/util/zip/ZipEntry;
+HSPLjava/util/zip/ZipFile;->onZipEntryAccess([BI)V+]Ldalvik/system/ZipPathValidator$Callback;Lcom/android/internal/os/SafeZipPathValidatorCallback;]Ljava/util/zip/ZipCoder;Ljava/util/zip/ZipCoder;
HSPLjava/util/zip/ZipFile;->releaseInflater(Ljava/util/zip/Inflater;)V
HSPLjava/util/zip/ZipInputStream;-><init>(Ljava/io/InputStream;)V
HSPLjava/util/zip/ZipInputStream;-><init>(Ljava/io/InputStream;Ljava/nio/charset/Charset;)V
@@ -8018,11 +8120,11 @@
HSPLjava/util/zip/ZipInputStream;->closeEntry()V
HSPLjava/util/zip/ZipInputStream;->createZipEntry(Ljava/lang/String;)Ljava/util/zip/ZipEntry;
HSPLjava/util/zip/ZipInputStream;->ensureOpen()V
-HSPLjava/util/zip/ZipInputStream;->getNextEntry()Ljava/util/zip/ZipEntry;+]Ljava/util/zip/CRC32;Ljava/util/zip/CRC32;]Ljava/util/zip/Inflater;Ljava/util/zip/Inflater;
-HSPLjava/util/zip/ZipInputStream;->read([BII)I+]Ljava/util/zip/CRC32;Ljava/util/zip/CRC32;]Ljava/io/InputStream;Ljava/io/PushbackInputStream;
-HSPLjava/util/zip/ZipInputStream;->readEnd(Ljava/util/zip/ZipEntry;)V+]Ljava/util/zip/CRC32;Ljava/util/zip/CRC32;]Ljava/io/PushbackInputStream;Ljava/io/PushbackInputStream;]Ljava/util/zip/Inflater;Ljava/util/zip/Inflater;
-HSPLjava/util/zip/ZipInputStream;->readFully([BII)V+]Ljava/io/InputStream;Ljava/io/PushbackInputStream;
-HSPLjava/util/zip/ZipInputStream;->readLOC()Ljava/util/zip/ZipEntry;+]Ljava/util/zip/ZipEntry;Ljava/util/zip/ZipEntry;]Ljava/util/zip/ZipInputStream;Ljava/util/zip/ZipInputStream;]Ljava/util/zip/ZipCoder;Ljava/util/zip/ZipCoder;
+HSPLjava/util/zip/ZipInputStream;->getNextEntry()Ljava/util/zip/ZipEntry;
+HSPLjava/util/zip/ZipInputStream;->read([BII)I
+HSPLjava/util/zip/ZipInputStream;->readEnd(Ljava/util/zip/ZipEntry;)V
+HSPLjava/util/zip/ZipInputStream;->readFully([BII)V
+HSPLjava/util/zip/ZipInputStream;->readLOC()Ljava/util/zip/ZipEntry;
HSPLjava/util/zip/ZipUtils;->get16([BI)I
HSPLjava/util/zip/ZipUtils;->get32([BI)J
HSPLjava/util/zip/ZipUtils;->unixTimeToFileTime(J)Ljava/nio/file/attribute/FileTime;
@@ -8038,7 +8140,7 @@
HSPLjavax/crypto/Cipher;->chooseProvider(Ljavax/crypto/Cipher$InitType;ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/AlgorithmParameters;Ljava/security/SecureRandom;)V
HSPLjavax/crypto/Cipher;->createCipher(Ljava/lang/String;Ljava/security/Provider;)Ljavax/crypto/Cipher;
HSPLjavax/crypto/Cipher;->doFinal()[B
-HSPLjavax/crypto/Cipher;->doFinal(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I+]Ljavax/crypto/Cipher;Ljavax/crypto/Cipher;]Ljava/nio/ByteBuffer;Ljava/nio/HeapByteBuffer;]Ljavax/crypto/CipherSpi;Lcom/android/org/conscrypt/OpenSSLEvpCipherAES$AES$CBC$NoPadding;,Lcom/android/org/conscrypt/OpenSSLAeadCipherAES$GCM;
+HSPLjavax/crypto/Cipher;->doFinal(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I
HSPLjavax/crypto/Cipher;->doFinal([B)[B
HSPLjavax/crypto/Cipher;->doFinal([BI)I
HSPLjavax/crypto/Cipher;->doFinal([BII)[B
@@ -8054,14 +8156,16 @@
HSPLjavax/crypto/Cipher;->init(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)V
HSPLjavax/crypto/Cipher;->matchAttribute(Ljava/security/Provider$Service;Ljava/lang/String;Ljava/lang/String;)Z
HSPLjavax/crypto/Cipher;->tokenizeTransformation(Ljava/lang/String;)[Ljava/lang/String;
-HSPLjavax/crypto/Cipher;->tryCombinations(Ljavax/crypto/Cipher$InitParams;Ljava/security/Provider;[Ljava/lang/String;)Ljavax/crypto/Cipher$CipherSpiAndProvider;
+HSPLjavax/crypto/Cipher;->tryCombinations(Ljavax/crypto/Cipher$InitParams;Ljava/security/Provider;[Ljava/lang/String;)Ljavax/crypto/Cipher$CipherSpiAndProvider;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/security/Provider$Service;Ljava/security/Provider$Service;]Ljava/security/Provider;missing_types]Ljava/util/ArrayList;Ljava/util/ArrayList;]Ljava/util/Iterator;Ljava/util/ArrayList$Itr;
HSPLjavax/crypto/Cipher;->tryTransformWithProvider(Ljavax/crypto/Cipher$InitParams;[Ljava/lang/String;Ljavax/crypto/Cipher$NeedToSet;Ljava/security/Provider$Service;)Ljavax/crypto/Cipher$CipherSpiAndProvider;
HSPLjavax/crypto/Cipher;->unwrap([BLjava/lang/String;I)Ljava/security/Key;
-HSPLjavax/crypto/Cipher;->update([BII[BI)I+]Ljavax/crypto/Cipher;Ljavax/crypto/Cipher;]Ljavax/crypto/CipherSpi;missing_types
+HSPLjavax/crypto/Cipher;->update([BII[BI)I
HSPLjavax/crypto/Cipher;->updateAAD([B)V
HSPLjavax/crypto/Cipher;->updateAAD([BII)V
HSPLjavax/crypto/Cipher;->updateProviderIfNeeded()V
HSPLjavax/crypto/CipherSpi;-><init>()V
+HSPLjavax/crypto/CipherSpi;->bufferCrypt(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;Z)I
+HSPLjavax/crypto/CipherSpi;->engineDoFinal(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I
HSPLjavax/crypto/JarVerifier;-><init>(Ljava/net/URL;Z)V
HSPLjavax/crypto/JarVerifier;->verify()V
HSPLjavax/crypto/JceSecurity$1;-><init>(Ljava/lang/Class;)V
@@ -8082,12 +8186,12 @@
HSPLjavax/crypto/Mac;-><init>(Ljava/lang/String;)V
HSPLjavax/crypto/Mac;-><init>(Ljavax/crypto/MacSpi;Ljava/security/Provider;Ljava/lang/String;)V
HSPLjavax/crypto/Mac;->chooseFirstProvider()V
-HSPLjavax/crypto/Mac;->chooseProvider(Ljava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V+]Ljava/security/Provider$Service;Ljava/security/Provider$Service;]Ljava/util/List;Lsun/security/jca/ProviderList$ServiceList;]Ljava/util/Iterator;Lsun/security/jca/ProviderList$ServiceList$1;
+HSPLjavax/crypto/Mac;->chooseProvider(Ljava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V
HSPLjavax/crypto/Mac;->doFinal()[B
HSPLjavax/crypto/Mac;->doFinal([B)[B
HSPLjavax/crypto/Mac;->doFinal([BI)V
HSPLjavax/crypto/Mac;->getAlgorithm()Ljava/lang/String;
-HSPLjavax/crypto/Mac;->getInstance(Ljava/lang/String;)Ljavax/crypto/Mac;+]Ljava/security/Provider$Service;Ljava/security/Provider$Service;]Ljava/util/List;Lsun/security/jca/ProviderList$ServiceList;]Ljava/util/Iterator;Lsun/security/jca/ProviderList$ServiceList$1;
+HSPLjavax/crypto/Mac;->getInstance(Ljava/lang/String;)Ljavax/crypto/Mac;
HSPLjavax/crypto/Mac;->getInstance(Ljava/lang/String;Ljava/security/Provider;)Ljavax/crypto/Mac;
HSPLjavax/crypto/Mac;->getMacLength()I
HSPLjavax/crypto/Mac;->init(Ljava/security/Key;)V
@@ -8229,11 +8333,11 @@
HSPLjdk/internal/math/FDBigInteger;-><init>(J[CII)V
HSPLjdk/internal/math/FDBigInteger;-><init>([II)V
HSPLjdk/internal/math/FDBigInteger;->add(Ljdk/internal/math/FDBigInteger;)Ljdk/internal/math/FDBigInteger;
-HSPLjdk/internal/math/FDBigInteger;->addAndCmp(Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;)I+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
+HSPLjdk/internal/math/FDBigInteger;->addAndCmp(Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;)I
HSPLjdk/internal/math/FDBigInteger;->big5pow(I)Ljdk/internal/math/FDBigInteger;
HSPLjdk/internal/math/FDBigInteger;->checkZeroTail([II)I
HSPLjdk/internal/math/FDBigInteger;->cmp(Ljdk/internal/math/FDBigInteger;)I
-HSPLjdk/internal/math/FDBigInteger;->cmpPow52(II)I+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
+HSPLjdk/internal/math/FDBigInteger;->cmpPow52(II)I
HSPLjdk/internal/math/FDBigInteger;->getNormalizationBias()I
HSPLjdk/internal/math/FDBigInteger;->leftInplaceSub(Ljdk/internal/math/FDBigInteger;)Ljdk/internal/math/FDBigInteger;
HSPLjdk/internal/math/FDBigInteger;->leftShift(I)Ljdk/internal/math/FDBigInteger;
@@ -8244,26 +8348,26 @@
HSPLjdk/internal/math/FDBigInteger;->multAddMe(II)V
HSPLjdk/internal/math/FDBigInteger;->multAndCarryBy10([II[I)I
HSPLjdk/internal/math/FDBigInteger;->multBy10()Ljdk/internal/math/FDBigInteger;
-HSPLjdk/internal/math/FDBigInteger;->multByPow52(II)Ljdk/internal/math/FDBigInteger;+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
+HSPLjdk/internal/math/FDBigInteger;->multByPow52(II)Ljdk/internal/math/FDBigInteger;
HSPLjdk/internal/math/FDBigInteger;->multDiffMe(JLjdk/internal/math/FDBigInteger;)J
HSPLjdk/internal/math/FDBigInteger;->quoRemIteration(Ljdk/internal/math/FDBigInteger;)I
HSPLjdk/internal/math/FDBigInteger;->rightInplaceSub(Ljdk/internal/math/FDBigInteger;)Ljdk/internal/math/FDBigInteger;
HSPLjdk/internal/math/FDBigInteger;->size()I
HSPLjdk/internal/math/FDBigInteger;->trimLeadingZeros()V
-HSPLjdk/internal/math/FDBigInteger;->valueOfMulPow52(JII)Ljdk/internal/math/FDBigInteger;+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
+HSPLjdk/internal/math/FDBigInteger;->valueOfMulPow52(JII)Ljdk/internal/math/FDBigInteger;
HSPLjdk/internal/math/FDBigInteger;->valueOfPow2(I)Ljdk/internal/math/FDBigInteger;
-HSPLjdk/internal/math/FDBigInteger;->valueOfPow52(II)Ljdk/internal/math/FDBigInteger;+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
+HSPLjdk/internal/math/FDBigInteger;->valueOfPow52(II)Ljdk/internal/math/FDBigInteger;
HSPLjdk/internal/math/FloatingDecimal$1;->initialValue()Ljava/lang/Object;
HSPLjdk/internal/math/FloatingDecimal$1;->initialValue()Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;
HSPLjdk/internal/math/FloatingDecimal$ASCIIToBinaryBuffer;-><init>(ZI[CI)V
-HSPLjdk/internal/math/FloatingDecimal$ASCIIToBinaryBuffer;->doubleValue()D+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
-HSPLjdk/internal/math/FloatingDecimal$ASCIIToBinaryBuffer;->floatValue()F+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
+HSPLjdk/internal/math/FloatingDecimal$ASCIIToBinaryBuffer;->doubleValue()D
+HSPLjdk/internal/math/FloatingDecimal$ASCIIToBinaryBuffer;->floatValue()F
HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->-$$Nest$mdtoa(Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;IJIZ)V
HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->-$$Nest$msetSign(Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;Z)V
HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;-><init>()V
-HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->appendTo(Ljava/lang/Appendable;)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/StringBuffer;Ljava/lang/StringBuffer;
+HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->appendTo(Ljava/lang/Appendable;)V
HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->developLongDigits(IJI)V
-HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->dtoa(IJIZ)V+]Ljdk/internal/math/FDBigInteger;Ljdk/internal/math/FDBigInteger;
+HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->dtoa(IJIZ)V
HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->estimateDecExp(JI)I
HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->getChars([C)I
HSPLjdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;->getDecimalExponent()I
@@ -8276,13 +8380,13 @@
HSPLjdk/internal/math/FloatingDecimal$PreparedASCIIToBinaryBuffer;->doubleValue()D
HSPLjdk/internal/math/FloatingDecimal$PreparedASCIIToBinaryBuffer;->floatValue()F
HSPLjdk/internal/math/FloatingDecimal;->appendTo(FLjava/lang/Appendable;)V
-HSPLjdk/internal/math/FloatingDecimal;->getBinaryToASCIIBuffer()Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;+]Ljava/lang/ThreadLocal;Ljdk/internal/math/FloatingDecimal$1;
+HSPLjdk/internal/math/FloatingDecimal;->getBinaryToASCIIBuffer()Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;
HSPLjdk/internal/math/FloatingDecimal;->getBinaryToASCIIConverter(D)Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;
HSPLjdk/internal/math/FloatingDecimal;->getBinaryToASCIIConverter(DZ)Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;
HSPLjdk/internal/math/FloatingDecimal;->getBinaryToASCIIConverter(F)Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;
-HSPLjdk/internal/math/FloatingDecimal;->parseDouble(Ljava/lang/String;)D+]Ljdk/internal/math/FloatingDecimal$ASCIIToBinaryConverter;Ljdk/internal/math/FloatingDecimal$ASCIIToBinaryBuffer;
-HSPLjdk/internal/math/FloatingDecimal;->parseFloat(Ljava/lang/String;)F+]Ljdk/internal/math/FloatingDecimal$ASCIIToBinaryConverter;Ljdk/internal/math/FloatingDecimal$ASCIIToBinaryBuffer;
-HSPLjdk/internal/math/FloatingDecimal;->readJavaFormatString(Ljava/lang/String;)Ljdk/internal/math/FloatingDecimal$ASCIIToBinaryConverter;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLjdk/internal/math/FloatingDecimal;->parseDouble(Ljava/lang/String;)D
+HSPLjdk/internal/math/FloatingDecimal;->parseFloat(Ljava/lang/String;)F
+HSPLjdk/internal/math/FloatingDecimal;->readJavaFormatString(Ljava/lang/String;)Ljdk/internal/math/FloatingDecimal$ASCIIToBinaryConverter;+]Ljava/lang/String;Ljava/lang/String;
HSPLjdk/internal/math/FloatingDecimal;->toJavaFormatString(D)Ljava/lang/String;
HSPLjdk/internal/math/FloatingDecimal;->toJavaFormatString(F)Ljava/lang/String;
HSPLjdk/internal/math/FormattedFloatingDecimal$1;-><init>()V
@@ -8292,13 +8396,16 @@
HSPLjdk/internal/math/FormattedFloatingDecimal$Form;-><init>(Ljava/lang/String;I)V
HSPLjdk/internal/math/FormattedFloatingDecimal$Form;->values()[Ljdk/internal/math/FormattedFloatingDecimal$Form;
HSPLjdk/internal/math/FormattedFloatingDecimal;-><clinit>()V
-HSPLjdk/internal/math/FormattedFloatingDecimal;-><init>(ILjdk/internal/math/FormattedFloatingDecimal$Form;Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;)V+]Ljdk/internal/math/FormattedFloatingDecimal$Form;Ljdk/internal/math/FormattedFloatingDecimal$Form;]Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;
+HSPLjdk/internal/math/FormattedFloatingDecimal;-><init>(ILjdk/internal/math/FormattedFloatingDecimal$Form;Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;)V
HSPLjdk/internal/math/FormattedFloatingDecimal;->applyPrecision(I[CII)I
HSPLjdk/internal/math/FormattedFloatingDecimal;->create(ZI)[C
HSPLjdk/internal/math/FormattedFloatingDecimal;->fillDecimal(I[CIIZ)V
-HSPLjdk/internal/math/FormattedFloatingDecimal;->getBuffer()[C+]Ljava/lang/ThreadLocal;Ljdk/internal/math/FormattedFloatingDecimal$1;
+HSPLjdk/internal/math/FormattedFloatingDecimal;->getBuffer()[C
+HSPLjdk/internal/math/FormattedFloatingDecimal;->getExponent()[C
+HSPLjdk/internal/math/FormattedFloatingDecimal;->getExponentRounded()I
HSPLjdk/internal/math/FormattedFloatingDecimal;->getMantissa()[C
HSPLjdk/internal/math/FormattedFloatingDecimal;->valueOf(DILjdk/internal/math/FormattedFloatingDecimal$Form;)Ljdk/internal/math/FormattedFloatingDecimal;
+HSPLjdk/internal/misc/Unsafe;->compareAndSetObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
HSPLjdk/internal/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I
HSPLjdk/internal/misc/Unsafe;->getAndAddLong(Ljava/lang/Object;JJ)J
HSPLjdk/internal/misc/Unsafe;->getAndSetInt(Ljava/lang/Object;JI)I
@@ -8308,17 +8415,28 @@
HSPLjdk/internal/misc/Unsafe;->getIntUnaligned(Ljava/lang/Object;J)I
HSPLjdk/internal/misc/Unsafe;->getLongAcquire(Ljava/lang/Object;J)J
HSPLjdk/internal/misc/Unsafe;->getLongUnaligned(Ljava/lang/Object;J)J
+HSPLjdk/internal/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object;
HSPLjdk/internal/misc/Unsafe;->getObjectAcquire(Ljava/lang/Object;J)Ljava/lang/Object;
+HSPLjdk/internal/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;
+HSPLjdk/internal/misc/Unsafe;->getReferenceAcquire(Ljava/lang/Object;J)Ljava/lang/Object;
+HSPLjdk/internal/misc/Unsafe;->getUnsafe()Ljdk/internal/misc/Unsafe;
HSPLjdk/internal/misc/Unsafe;->makeLong(II)J
+HSPLjdk/internal/misc/Unsafe;->objectFieldOffset(Ljava/lang/Class;Ljava/lang/String;)J
HSPLjdk/internal/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J
+HSPLjdk/internal/misc/Unsafe;->pickPos(II)I
HSPLjdk/internal/misc/Unsafe;->putIntRelease(Ljava/lang/Object;JI)V
HSPLjdk/internal/misc/Unsafe;->putLongRelease(Ljava/lang/Object;JJ)V
+HSPLjdk/internal/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V
HSPLjdk/internal/misc/Unsafe;->putObjectRelease(Ljava/lang/Object;JLjava/lang/Object;)V
+HSPLjdk/internal/misc/Unsafe;->putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V
+HSPLjdk/internal/misc/Unsafe;->putReferenceRelease(Ljava/lang/Object;JLjava/lang/Object;)V+]Ljdk/internal/misc/Unsafe;Ljdk/internal/misc/Unsafe;
HSPLjdk/internal/misc/Unsafe;->toUnsignedLong(I)J
+HSPLjdk/internal/misc/VM;->getSavedProperty(Ljava/lang/String;)Ljava/lang/String;
HSPLjdk/internal/reflect/Reflection;->getCallerClass()Ljava/lang/Class;
HSPLjdk/internal/util/ArraysSupport;->mismatch([B[BI)I
HSPLjdk/internal/util/ArraysSupport;->mismatch([FI[FII)I
HSPLjdk/internal/util/ArraysSupport;->mismatch([I[II)I
+HSPLjdk/internal/util/ArraysSupport;->mismatch([J[JI)I
HSPLjdk/internal/util/ArraysSupport;->mismatch([Z[ZI)I
HSPLjdk/internal/util/ArraysSupport;->vectorizedMismatch(Ljava/lang/Object;JLjava/lang/Object;JII)I+]Ljdk/internal/misc/Unsafe;Ljdk/internal/misc/Unsafe;
HSPLjdk/internal/util/Preconditions;->checkFromIndexSize(IIILjava/util/function/BiFunction;)I
@@ -8326,7 +8444,7 @@
HSPLlibcore/content/type/MimeMap$Builder$Element;-><init>(Ljava/lang/String;Z)V
HSPLlibcore/content/type/MimeMap$Builder$Element;->ofExtensionSpec(Ljava/lang/String;)Llibcore/content/type/MimeMap$Builder$Element;
HSPLlibcore/content/type/MimeMap$Builder$Element;->ofMimeSpec(Ljava/lang/String;)Llibcore/content/type/MimeMap$Builder$Element;
-HSPLlibcore/content/type/MimeMap$Builder;->addMimeMapping(Ljava/lang/String;Ljava/util/List;)Llibcore/content/type/MimeMap$Builder;+]Ljava/util/List;Ljava/util/ArrayList$SubList;]Ljava/util/Iterator;Ljava/util/ArrayList$SubList$1;
+HSPLlibcore/content/type/MimeMap$Builder;->addMimeMapping(Ljava/lang/String;Ljava/util/List;)Llibcore/content/type/MimeMap$Builder;
HSPLlibcore/content/type/MimeMap$Builder;->maybePut(Ljava/util/Map;Llibcore/content/type/MimeMap$Builder$Element;Ljava/lang/String;)Ljava/lang/String;
HSPLlibcore/content/type/MimeMap$MemoizingSupplier;->get()Ljava/lang/Object;
HSPLlibcore/content/type/MimeMap;-><init>(Ljava/util/Map;Ljava/util/Map;)V
@@ -8344,7 +8462,7 @@
HSPLlibcore/icu/DecimalFormatData;->getExponentSeparator()Ljava/lang/String;
HSPLlibcore/icu/DecimalFormatData;->getGroupingSeparator()C
HSPLlibcore/icu/DecimalFormatData;->getInfinity()Ljava/lang/String;
-HSPLlibcore/icu/DecimalFormatData;->getInstance(Ljava/util/Locale;)Llibcore/icu/DecimalFormatData;+]Ljava/util/Locale;Ljava/util/Locale;]Ljava/util/concurrent/ConcurrentHashMap;Ljava/util/concurrent/ConcurrentHashMap;
+HSPLlibcore/icu/DecimalFormatData;->getInstance(Ljava/util/Locale;)Llibcore/icu/DecimalFormatData;
HSPLlibcore/icu/DecimalFormatData;->getMinusSign()Ljava/lang/String;
HSPLlibcore/icu/DecimalFormatData;->getNaN()Ljava/lang/String;
HSPLlibcore/icu/DecimalFormatData;->getNumberPattern()Ljava/lang/String;
@@ -8372,13 +8490,13 @@
HSPLlibcore/icu/LocaleData;->initializeDateFormatData(Ljava/util/Locale;)V
HSPLlibcore/icu/LocaleData;->mapInvalidAndNullLocales(Ljava/util/Locale;)Ljava/util/Locale;
HSPLlibcore/icu/SimpleDateFormatData;->getDateFormat(I)Ljava/lang/String;
-HSPLlibcore/icu/SimpleDateFormatData;->getInstance(Ljava/util/Locale;)Llibcore/icu/SimpleDateFormatData;+]Ljava/util/Locale;Ljava/util/Locale;]Ljava/util/concurrent/ConcurrentHashMap;Ljava/util/concurrent/ConcurrentHashMap;
-HSPLlibcore/icu/SimpleDateFormatData;->getTimeFormat(I)Ljava/lang/String;+]Ljava/lang/Boolean;Ljava/lang/Boolean;
+HSPLlibcore/icu/SimpleDateFormatData;->getInstance(Ljava/util/Locale;)Llibcore/icu/SimpleDateFormatData;
+HSPLlibcore/icu/SimpleDateFormatData;->getTimeFormat(I)Ljava/lang/String;
HSPLlibcore/internal/StringPool;-><init>()V
HSPLlibcore/internal/StringPool;->contentEquals(Ljava/lang/String;[CII)Z
HSPLlibcore/internal/StringPool;->get([CII)Ljava/lang/String;
HSPLlibcore/io/BlockGuardOs;->accept(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)Ljava/io/FileDescriptor;
-HSPLlibcore/io/BlockGuardOs;->access(Ljava/lang/String;I)Z
+HSPLlibcore/io/BlockGuardOs;->access(Ljava/lang/String;I)Z+]Ldalvik/system/BlockGuard$VmPolicy;Landroid/os/StrictMode$5;]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;,Landroid/os/StrictMode$AndroidBlockGuardPolicy;
HSPLlibcore/io/BlockGuardOs;->android_getaddrinfo(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;
HSPLlibcore/io/BlockGuardOs;->chmod(Ljava/lang/String;I)V
HSPLlibcore/io/BlockGuardOs;->close(Ljava/io/FileDescriptor;)V
@@ -8400,7 +8518,7 @@
HSPLlibcore/io/BlockGuardOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
HSPLlibcore/io/BlockGuardOs;->poll([Landroid/system/StructPollfd;I)I
HSPLlibcore/io/BlockGuardOs;->posix_fallocate(Ljava/io/FileDescriptor;JJ)V
-HSPLlibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I+]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;
+HSPLlibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I
HSPLlibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;[BII)I+]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;,Landroid/os/StrictMode$AndroidBlockGuardPolicy;
HSPLlibcore/io/BlockGuardOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
HSPLlibcore/io/BlockGuardOs;->recvfrom(Ljava/io/FileDescriptor;[BIIILjava/net/InetSocketAddress;)I
@@ -8412,7 +8530,7 @@
HSPLlibcore/io/BlockGuardOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
HSPLlibcore/io/BlockGuardOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
HSPLlibcore/io/BlockGuardOs;->tagSocket(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;
-HSPLlibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I+]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;,Landroid/os/StrictMode$AndroidBlockGuardPolicy;
+HSPLlibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I
HSPLlibcore/io/ClassPathURLStreamHandler$ClassPathURLConnection$1;-><init>(Llibcore/io/ClassPathURLStreamHandler$ClassPathURLConnection;Ljava/io/InputStream;)V
HSPLlibcore/io/ClassPathURLStreamHandler$ClassPathURLConnection$1;->close()V
HSPLlibcore/io/ClassPathURLStreamHandler$ClassPathURLConnection;-><init>(Llibcore/io/ClassPathURLStreamHandler;Ljava/net/URL;)V
@@ -8424,7 +8542,7 @@
HSPLlibcore/io/ClassPathURLStreamHandler;->openConnection(Ljava/net/URL;)Ljava/net/URLConnection;
HSPLlibcore/io/ForwardingOs;-><init>(Llibcore/io/Os;)V
HSPLlibcore/io/ForwardingOs;->accept(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)Ljava/io/FileDescriptor;
-HSPLlibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z
+HSPLlibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z+]Llibcore/io/Os;Llibcore/io/BlockGuardOs;,Llibcore/io/Linux;
HSPLlibcore/io/ForwardingOs;->android_fdsan_exchange_owner_tag(Ljava/io/FileDescriptor;JJ)V
HSPLlibcore/io/ForwardingOs;->android_getaddrinfo(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;
HSPLlibcore/io/ForwardingOs;->bind(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V
@@ -8445,11 +8563,11 @@
HSPLlibcore/io/ForwardingOs;->getnameinfo(Ljava/net/InetAddress;I)Ljava/lang/String;
HSPLlibcore/io/ForwardingOs;->getpeername(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;
HSPLlibcore/io/ForwardingOs;->getpgid(I)I
-HSPLlibcore/io/ForwardingOs;->getpid()I
+HSPLlibcore/io/ForwardingOs;->getpid()I+]Llibcore/io/Os;Llibcore/io/BlockGuardOs;,Llibcore/io/Linux;
HSPLlibcore/io/ForwardingOs;->getsockname(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;
HSPLlibcore/io/ForwardingOs;->getsockoptInt(Ljava/io/FileDescriptor;II)I
HSPLlibcore/io/ForwardingOs;->getsockoptLinger(Ljava/io/FileDescriptor;II)Landroid/system/StructLinger;
-HSPLlibcore/io/ForwardingOs;->gettid()I
+HSPLlibcore/io/ForwardingOs;->gettid()I+]Llibcore/io/Os;Llibcore/io/BlockGuardOs;,Llibcore/io/Linux;
HSPLlibcore/io/ForwardingOs;->getuid()I+]Llibcore/io/Os;Llibcore/io/BlockGuardOs;,Llibcore/io/Linux;
HSPLlibcore/io/ForwardingOs;->getxattr(Ljava/lang/String;Ljava/lang/String;)[B
HSPLlibcore/io/ForwardingOs;->if_nametoindex(Ljava/lang/String;)I
@@ -8487,7 +8605,7 @@
HSPLlibcore/io/IoBridge;->bind(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V
HSPLlibcore/io/IoBridge;->booleanFromInt(I)Z
HSPLlibcore/io/IoBridge;->booleanToInt(Z)I
-HSPLlibcore/io/IoBridge;->closeAndSignalBlockedThreads(Ljava/io/FileDescriptor;)V+]Llibcore/io/Os;Landroid/app/ActivityThread$AndroidOs;]Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;
+HSPLlibcore/io/IoBridge;->closeAndSignalBlockedThreads(Ljava/io/FileDescriptor;)V
HSPLlibcore/io/IoBridge;->connect(Ljava/io/FileDescriptor;Ljava/net/InetAddress;II)V
HSPLlibcore/io/IoBridge;->connectErrno(Ljava/io/FileDescriptor;Ljava/net/InetAddress;II)V
HSPLlibcore/io/IoBridge;->createMessageForException(Ljava/io/FileDescriptor;Ljava/net/InetAddress;IILjava/lang/Exception;)Ljava/lang/String;
@@ -8507,7 +8625,7 @@
HSPLlibcore/io/IoBridge;->write(Ljava/io/FileDescriptor;[BII)V+]Llibcore/io/Os;Landroid/app/ActivityThread$AndroidOs;
HSPLlibcore/io/IoTracker;-><init>()V
HSPLlibcore/io/IoTracker;->reset()V
-HSPLlibcore/io/IoTracker;->trackIo(I)V+]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;,Landroid/os/StrictMode$AndroidBlockGuardPolicy;
+HSPLlibcore/io/IoTracker;->trackIo(I)V+]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;
HSPLlibcore/io/IoTracker;->trackIo(ILlibcore/io/IoTracker$Mode;)V+]Llibcore/io/IoTracker;Llibcore/io/IoTracker;
HSPLlibcore/io/IoUtils;->acquireRawFd(Ljava/io/FileDescriptor;)I
HSPLlibcore/io/IoUtils;->canOpenReadOnly(Ljava/lang/String;)Z
@@ -8557,12 +8675,12 @@
HSPLlibcore/reflect/AnnotationFactory;-><init>(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)V
HSPLlibcore/reflect/AnnotationFactory;->createAnnotation(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;
HSPLlibcore/reflect/AnnotationFactory;->getElementsDescription(Ljava/lang/Class;)[Llibcore/reflect/AnnotationMember;
-HSPLlibcore/reflect/AnnotationFactory;->invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;+]Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;]Llibcore/reflect/AnnotationMember;Llibcore/reflect/AnnotationMember;
+HSPLlibcore/reflect/AnnotationFactory;->invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
HSPLlibcore/reflect/AnnotationMember;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
HSPLlibcore/reflect/AnnotationMember;-><init>(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V
HSPLlibcore/reflect/AnnotationMember;->copyValue()Ljava/lang/Object;
HSPLlibcore/reflect/AnnotationMember;->setDefinition(Llibcore/reflect/AnnotationMember;)Llibcore/reflect/AnnotationMember;
-HSPLlibcore/reflect/AnnotationMember;->validateValue()Ljava/lang/Object;+]Ljava/lang/Object;missing_types]Llibcore/reflect/AnnotationMember;Llibcore/reflect/AnnotationMember;
+HSPLlibcore/reflect/AnnotationMember;->validateValue()Ljava/lang/Object;
HSPLlibcore/reflect/GenericArrayTypeImpl;->getGenericComponentType()Ljava/lang/reflect/Type;
HSPLlibcore/reflect/GenericSignatureParser;-><init>(Ljava/lang/ClassLoader;)V
HSPLlibcore/reflect/GenericSignatureParser;->expect(C)V
@@ -8571,8 +8689,8 @@
HSPLlibcore/reflect/GenericSignatureParser;->parseClassTypeSignature()Ljava/lang/reflect/Type;
HSPLlibcore/reflect/GenericSignatureParser;->parseFieldTypeSignature()Ljava/lang/reflect/Type;
HSPLlibcore/reflect/GenericSignatureParser;->parseForClass(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;)V
-HSPLlibcore/reflect/GenericSignatureParser;->parseForConstructor(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;[Ljava/lang/Class;)V+]Llibcore/reflect/GenericSignatureParser;Llibcore/reflect/GenericSignatureParser;]Ljava/lang/reflect/Constructor;Ljava/lang/reflect/Constructor;
-HSPLlibcore/reflect/GenericSignatureParser;->parseForField(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;)V
+HSPLlibcore/reflect/GenericSignatureParser;->parseForConstructor(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;[Ljava/lang/Class;)V
+HSPLlibcore/reflect/GenericSignatureParser;->parseForField(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;)V+]Llibcore/reflect/GenericSignatureParser;Llibcore/reflect/GenericSignatureParser;
HSPLlibcore/reflect/GenericSignatureParser;->parseForMethod(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;[Ljava/lang/Class;)V
HSPLlibcore/reflect/GenericSignatureParser;->parseFormalTypeParameter()Llibcore/reflect/TypeVariableImpl;
HSPLlibcore/reflect/GenericSignatureParser;->parseMethodTypeSignature([Ljava/lang/Class;)V
@@ -8595,6 +8713,7 @@
HSPLlibcore/reflect/ListOfVariables;->add(Ljava/lang/reflect/TypeVariable;)V
HSPLlibcore/reflect/ListOfVariables;->getArray()[Ljava/lang/reflect/TypeVariable;
HSPLlibcore/reflect/ParameterizedTypeImpl;-><init>(Llibcore/reflect/ParameterizedTypeImpl;Ljava/lang/String;Llibcore/reflect/ListOfTypes;Ljava/lang/ClassLoader;)V
+HSPLlibcore/reflect/ParameterizedTypeImpl;->equals(Ljava/lang/Object;)Z
HSPLlibcore/reflect/ParameterizedTypeImpl;->getActualTypeArguments()[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/ParameterizedTypeImpl;->getOwnerType()Ljava/lang/reflect/Type;
HSPLlibcore/reflect/ParameterizedTypeImpl;->getRawType()Ljava/lang/Class;
@@ -8604,7 +8723,7 @@
HSPLlibcore/reflect/TypeVariableImpl;-><init>(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;Llibcore/reflect/ListOfTypes;)V
HSPLlibcore/reflect/TypeVariableImpl;->equals(Ljava/lang/Object;)Z
HSPLlibcore/reflect/TypeVariableImpl;->findFormalVar(Ljava/lang/reflect/GenericDeclaration;Ljava/lang/String;)Ljava/lang/reflect/TypeVariable;
-HSPLlibcore/reflect/TypeVariableImpl;->getBounds()[Ljava/lang/reflect/Type;+]Llibcore/reflect/ListOfTypes;Llibcore/reflect/ListOfTypes;]Llibcore/reflect/TypeVariableImpl;Llibcore/reflect/TypeVariableImpl;][Ljava/lang/reflect/Type;[Ljava/lang/reflect/Type;
+HSPLlibcore/reflect/TypeVariableImpl;->getBounds()[Ljava/lang/reflect/Type;
HSPLlibcore/reflect/TypeVariableImpl;->getGenericDeclaration()Ljava/lang/reflect/GenericDeclaration;
HSPLlibcore/reflect/TypeVariableImpl;->getName()Ljava/lang/String;
HSPLlibcore/reflect/TypeVariableImpl;->hashCode()I
@@ -8681,9 +8800,12 @@
HSPLorg/apache/harmony/xml/ExpatParser;->startDocument()V
HSPLorg/apache/harmony/xml/ExpatParser;->startElement(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JI)V
HSPLorg/apache/harmony/xml/ExpatReader;-><init>()V
-HSPLorg/apache/harmony/xml/ExpatReader;->parse(Lorg/xml/sax/InputSource;)V+]Lorg/xml/sax/InputSource;Lorg/xml/sax/InputSource;
+HSPLorg/apache/harmony/xml/ExpatReader;->parse(Lorg/xml/sax/InputSource;)V
HSPLorg/apache/harmony/xml/ExpatReader;->setContentHandler(Lorg/xml/sax/ContentHandler;)V
-HSPLorg/apache/harmony/xml/dom/CharacterDataImpl;-><init>(Lorg/apache/harmony/xml/dom/DocumentImpl;Ljava/lang/String;)V+]Lorg/apache/harmony/xml/dom/CharacterDataImpl;Lorg/apache/harmony/xml/dom/TextImpl;,Lorg/apache/harmony/xml/dom/CommentImpl;
+HSPLorg/apache/harmony/xml/dom/AttrImpl;->getNodeType()S
+HSPLorg/apache/harmony/xml/dom/AttrImpl;->getOwnerElement()Lorg/w3c/dom/Element;
+HSPLorg/apache/harmony/xml/dom/AttrImpl;->setValue(Ljava/lang/String;)V
+HSPLorg/apache/harmony/xml/dom/CharacterDataImpl;-><init>(Lorg/apache/harmony/xml/dom/DocumentImpl;Ljava/lang/String;)V
HSPLorg/apache/harmony/xml/dom/CharacterDataImpl;->getData()Ljava/lang/String;
HSPLorg/apache/harmony/xml/dom/CharacterDataImpl;->getNodeValue()Ljava/lang/String;
HSPLorg/apache/harmony/xml/dom/CharacterDataImpl;->setData(Ljava/lang/String;)V
@@ -8716,7 +8838,7 @@
HSPLorg/apache/harmony/xml/dom/LeafNodeImpl;->isParentOf(Lorg/w3c/dom/Node;)Z
HSPLorg/apache/harmony/xml/dom/NodeImpl;-><init>(Lorg/apache/harmony/xml/dom/DocumentImpl;)V
HSPLorg/apache/harmony/xml/dom/NodeImpl;->getTextContent()Ljava/lang/String;
-HSPLorg/apache/harmony/xml/dom/NodeImpl;->setName(Lorg/apache/harmony/xml/dom/NodeImpl;Ljava/lang/String;)V+]Lorg/apache/harmony/xml/dom/NodeImpl;Lorg/apache/harmony/xml/dom/AttrImpl;,Lorg/apache/harmony/xml/dom/ElementImpl;]Ljava/lang/String;Ljava/lang/String;
+HSPLorg/apache/harmony/xml/dom/NodeImpl;->setName(Lorg/apache/harmony/xml/dom/NodeImpl;Ljava/lang/String;)V
HSPLorg/apache/harmony/xml/dom/NodeListImpl;-><init>()V
HSPLorg/apache/harmony/xml/dom/NodeListImpl;->add(Lorg/apache/harmony/xml/dom/NodeImpl;)V
HSPLorg/apache/harmony/xml/dom/NodeListImpl;->getLength()I
@@ -8727,9 +8849,9 @@
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl;->newDocumentBuilder()Ljavax/xml/parsers/DocumentBuilder;
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;-><clinit>()V
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;-><init>()V
-HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->appendText(Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/w3c/dom/Node;ILjava/lang/String;)V+]Lorg/w3c/dom/Node;Lorg/apache/harmony/xml/dom/ElementImpl;,Lorg/apache/harmony/xml/dom/CommentImpl;,Lorg/apache/harmony/xml/dom/TextImpl;]Lorg/w3c/dom/Text;Lorg/apache/harmony/xml/dom/TextImpl;
-HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->parse(Lcom/android/org/kxml2/io/KXmlParser;Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/w3c/dom/Node;I)V+]Lorg/w3c/dom/Node;Lorg/apache/harmony/xml/dom/ElementImpl;,Lorg/apache/harmony/xml/dom/DocumentImpl;]Lorg/w3c/dom/Element;Lorg/apache/harmony/xml/dom/ElementImpl;]Lcom/android/org/kxml2/io/KXmlParser;Lcom/android/org/kxml2/io/KXmlParser;]Lorg/w3c/dom/Attr;Lorg/apache/harmony/xml/dom/AttrImpl;]Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/apache/harmony/xml/dom/DocumentImpl;]Ljava/lang/String;Ljava/lang/String;
-HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->parse(Lorg/xml/sax/InputSource;)Lorg/w3c/dom/Document;+]Lcom/android/org/kxml2/io/KXmlParser;Lcom/android/org/kxml2/io/KXmlParser;]Lorg/xml/sax/InputSource;Lorg/xml/sax/InputSource;]Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/apache/harmony/xml/dom/DocumentImpl;
+HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->appendText(Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/w3c/dom/Node;ILjava/lang/String;)V
+HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->parse(Lcom/android/org/kxml2/io/KXmlParser;Lorg/apache/harmony/xml/dom/DocumentImpl;Lorg/w3c/dom/Node;I)V
+HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->parse(Lorg/xml/sax/InputSource;)Lorg/w3c/dom/Document;
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->setCoalescing(Z)V
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->setIgnoreComments(Z)V
HSPLorg/apache/harmony/xml/parsers/DocumentBuilderImpl;->setIgnoreElementContentWhitespace(Z)V
@@ -8741,7 +8863,7 @@
HSPLorg/json/JSON;->toInteger(Ljava/lang/Object;)Ljava/lang/Integer;
HSPLorg/json/JSON;->toLong(Ljava/lang/Object;)Ljava/lang/Long;
HSPLorg/json/JSON;->toString(Ljava/lang/Object;)Ljava/lang/String;
-HSPLorg/json/JSON;->typeMismatch(Ljava/lang/Object;Ljava/lang/String;)Lorg/json/JSONException;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/Object;Ljava/lang/String;,Lorg/json/JSONObject$1;]Ljava/lang/Class;Ljava/lang/Class;
+HSPLorg/json/JSON;->typeMismatch(Ljava/lang/Object;Ljava/lang/String;)Lorg/json/JSONException;
HSPLorg/json/JSONArray;-><init>()V
HSPLorg/json/JSONArray;-><init>(Ljava/lang/String;)V
HSPLorg/json/JSONArray;-><init>(Ljava/util/Collection;)V
@@ -8818,18 +8940,18 @@
HSPLorg/json/JSONStringer;->open(Lorg/json/JSONStringer$Scope;Ljava/lang/String;)Lorg/json/JSONStringer;
HSPLorg/json/JSONStringer;->peek()Lorg/json/JSONStringer$Scope;
HSPLorg/json/JSONStringer;->replaceTop(Lorg/json/JSONStringer$Scope;)V
-HSPLorg/json/JSONStringer;->string(Ljava/lang/String;)V+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLorg/json/JSONStringer;->string(Ljava/lang/String;)V
HSPLorg/json/JSONStringer;->toString()Ljava/lang/String;
HSPLorg/json/JSONStringer;->value(Ljava/lang/Object;)Lorg/json/JSONStringer;
HSPLorg/json/JSONTokener;-><init>(Ljava/lang/String;)V
HSPLorg/json/JSONTokener;->nextCleanInternal()I
HSPLorg/json/JSONTokener;->nextString(C)Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
-HSPLorg/json/JSONTokener;->nextToInternal(Ljava/lang/String;)Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;
+HSPLorg/json/JSONTokener;->nextToInternal(Ljava/lang/String;)Ljava/lang/String;
HSPLorg/json/JSONTokener;->nextValue()Ljava/lang/Object;
HSPLorg/json/JSONTokener;->readArray()Lorg/json/JSONArray;
HSPLorg/json/JSONTokener;->readEscapeCharacter()C
-HSPLorg/json/JSONTokener;->readLiteral()Ljava/lang/Object;+]Ljava/lang/String;Ljava/lang/String;
-HSPLorg/json/JSONTokener;->readObject()Lorg/json/JSONObject;+]Lorg/json/JSONObject;Lorg/json/JSONObject;]Lorg/json/JSONTokener;Lorg/json/JSONTokener;
+HSPLorg/json/JSONTokener;->readLiteral()Ljava/lang/Object;
+HSPLorg/json/JSONTokener;->readObject()Lorg/json/JSONObject;
HSPLorg/json/JSONTokener;->syntaxError(Ljava/lang/String;)Lorg/json/JSONException;
HSPLorg/json/JSONTokener;->toString()Ljava/lang/String;
HSPLorg/xml/sax/InputSource;-><init>(Ljava/io/InputStream;)V
@@ -8866,7 +8988,7 @@
HSPLsun/misc/ASCIICaseInsensitiveComparator;->toLower(I)I
HSPLsun/misc/Cleaner;-><init>(Ljava/lang/Object;Ljava/lang/Runnable;)V
HSPLsun/misc/Cleaner;->add(Lsun/misc/Cleaner;)Lsun/misc/Cleaner;
-HSPLsun/misc/Cleaner;->clean()V
+HSPLsun/misc/Cleaner;->clean()V+]Ljava/lang/Runnable;Llibcore/util/NativeAllocationRegistry$CleanerThunk;
HSPLsun/misc/Cleaner;->create(Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner;
HSPLsun/misc/Cleaner;->remove(Lsun/misc/Cleaner;)Z
HSPLsun/misc/CompoundEnumeration;-><init>([Ljava/util/Enumeration;)V
@@ -8936,7 +9058,7 @@
HSPLsun/nio/ch/FileChannelImpl;->open(Ljava/io/FileDescriptor;Ljava/lang/String;ZZLjava/lang/Object;)Ljava/nio/channels/FileChannel;
HSPLsun/nio/ch/FileChannelImpl;->open(Ljava/io/FileDescriptor;Ljava/lang/String;ZZZLjava/lang/Object;)Ljava/nio/channels/FileChannel;
HSPLsun/nio/ch/FileChannelImpl;->position()J
-HSPLsun/nio/ch/FileChannelImpl;->position(J)Ljava/nio/channels/FileChannel;+]Lsun/nio/ch/NativeThreadSet;Lsun/nio/ch/NativeThreadSet;]Lsun/nio/ch/FileChannelImpl;Lsun/nio/ch/FileChannelImpl;]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;
+HSPLsun/nio/ch/FileChannelImpl;->position(J)Ljava/nio/channels/FileChannel;+]Lsun/nio/ch/NativeThreadSet;Lsun/nio/ch/NativeThreadSet;]Lsun/nio/ch/FileChannelImpl;Lsun/nio/ch/FileChannelImpl;]Ldalvik/system/BlockGuard$Policy;Ldalvik/system/BlockGuard$1;,Landroid/os/StrictMode$AndroidBlockGuardPolicy;
HSPLsun/nio/ch/FileChannelImpl;->read(Ljava/nio/ByteBuffer;)I
HSPLsun/nio/ch/FileChannelImpl;->release(Lsun/nio/ch/FileLockImpl;)V
HSPLsun/nio/ch/FileChannelImpl;->size()J
@@ -9055,7 +9177,7 @@
HSPLsun/nio/ch/Util$1;->initialValue()Lsun/nio/ch/Util$BufferCache;
HSPLsun/nio/ch/Util$3;-><init>(Ljava/util/Set;)V
HSPLsun/nio/ch/Util$BufferCache;-><init>()V
-HSPLsun/nio/ch/Util$BufferCache;->get(I)Ljava/nio/ByteBuffer;+]Ljava/nio/ByteBuffer;Ljava/nio/DirectByteBuffer;
+HSPLsun/nio/ch/Util$BufferCache;->get(I)Ljava/nio/ByteBuffer;
HSPLsun/nio/ch/Util$BufferCache;->isEmpty()Z
HSPLsun/nio/ch/Util$BufferCache;->next(I)I
HSPLsun/nio/ch/Util$BufferCache;->offerFirst(Ljava/nio/ByteBuffer;)Z
@@ -9122,7 +9244,7 @@
HSPLsun/nio/fs/NativeBuffer;->setOwner(Ljava/lang/Object;)V
HSPLsun/nio/fs/NativeBuffer;->size()I
HSPLsun/nio/fs/NativeBuffers;->allocNativeBuffer(I)Lsun/nio/fs/NativeBuffer;
-HSPLsun/nio/fs/NativeBuffers;->copyCStringToNativeBuffer([BLsun/nio/fs/NativeBuffer;)V+]Lsun/nio/fs/NativeBuffer;Lsun/nio/fs/NativeBuffer;]Lsun/misc/Unsafe;Lsun/misc/Unsafe;
+HSPLsun/nio/fs/NativeBuffers;->copyCStringToNativeBuffer([BLsun/nio/fs/NativeBuffer;)V
HSPLsun/nio/fs/NativeBuffers;->getNativeBufferFromCache(I)Lsun/nio/fs/NativeBuffer;
HSPLsun/nio/fs/NativeBuffers;->releaseNativeBuffer(Lsun/nio/fs/NativeBuffer;)V
HSPLsun/nio/fs/UnixChannelFactory$1;-><clinit>()V
@@ -9135,6 +9257,8 @@
HSPLsun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator;-><init>(Lsun/nio/fs/UnixDirectoryStream;Ljava/nio/file/DirectoryStream;)V
HSPLsun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator;->hasNext()Z
HSPLsun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator;->isSelfOrParent([B)Z
+HSPLsun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator;->next()Ljava/lang/Object;
+HSPLsun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator;->next()Ljava/nio/file/Path;
HSPLsun/nio/fs/UnixDirectoryStream$UnixDirectoryIterator;->readNextEntry()Ljava/nio/file/Path;
HSPLsun/nio/fs/UnixDirectoryStream;->-$$Nest$fgetdp(Lsun/nio/fs/UnixDirectoryStream;)J
HSPLsun/nio/fs/UnixDirectoryStream;-><init>(Lsun/nio/fs/UnixPath;JLjava/nio/file/DirectoryStream$Filter;)V
@@ -9147,6 +9271,7 @@
HSPLsun/nio/fs/UnixDirectoryStream;->writeLock()Ljava/util/concurrent/locks/Lock;
HSPLsun/nio/fs/UnixException;-><init>(I)V
HSPLsun/nio/fs/UnixException;->errno()I
+HSPLsun/nio/fs/UnixException;->fillInStackTrace()Ljava/lang/Throwable;
HSPLsun/nio/fs/UnixException;->rethrowAsIOException(Lsun/nio/fs/UnixPath;)V
HSPLsun/nio/fs/UnixException;->rethrowAsIOException(Lsun/nio/fs/UnixPath;Lsun/nio/fs/UnixPath;)V
HSPLsun/nio/fs/UnixException;->translateToIOException(Ljava/lang/String;Ljava/lang/String;)Ljava/io/IOException;
@@ -9157,6 +9282,7 @@
HSPLsun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes;->creationTime()Ljava/nio/file/attribute/FileTime;
HSPLsun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes;->isDirectory()Z
HSPLsun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes;->isRegularFile()Z
+HSPLsun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes;->isSymbolicLink()Z
HSPLsun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes;->lastAccessTime()Ljava/nio/file/attribute/FileTime;
HSPLsun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes;->lastModifiedTime()Ljava/nio/file/attribute/FileTime;
HSPLsun/nio/fs/UnixFileAttributes$UnixAsBasicFileAttributes;->size()J
@@ -9191,7 +9317,7 @@
HSPLsun/nio/fs/UnixNativeDispatcher;->lstat(Lsun/nio/fs/UnixPath;Lsun/nio/fs/UnixFileAttributes;)V
HSPLsun/nio/fs/UnixNativeDispatcher;->open(Lsun/nio/fs/UnixPath;II)I
HSPLsun/nio/fs/UnixNativeDispatcher;->openatSupported()Z
-HSPLsun/nio/fs/UnixNativeDispatcher;->stat(Lsun/nio/fs/UnixPath;Lsun/nio/fs/UnixFileAttributes;)V+]Lsun/nio/fs/NativeBuffer;Lsun/nio/fs/NativeBuffer;
+HSPLsun/nio/fs/UnixNativeDispatcher;->stat(Lsun/nio/fs/UnixPath;Lsun/nio/fs/UnixFileAttributes;)V
HSPLsun/nio/fs/UnixPath;-><init>(Lsun/nio/fs/UnixFileSystem;Ljava/lang/String;)V
HSPLsun/nio/fs/UnixPath;-><init>(Lsun/nio/fs/UnixFileSystem;[B)V
HSPLsun/nio/fs/UnixPath;->asByteArray()[B
@@ -9207,7 +9333,7 @@
HSPLsun/nio/fs/UnixPath;->getPathForExceptionMessage()Ljava/lang/String;
HSPLsun/nio/fs/UnixPath;->initOffsets()V
HSPLsun/nio/fs/UnixPath;->isEmpty()Z
-HSPLsun/nio/fs/UnixPath;->normalize(Ljava/lang/String;II)Ljava/lang/String;+]Ljava/lang/String;Ljava/lang/String;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;
+HSPLsun/nio/fs/UnixPath;->normalize(Ljava/lang/String;II)Ljava/lang/String;
HSPLsun/nio/fs/UnixPath;->normalizeAndCheck(Ljava/lang/String;)Ljava/lang/String;
HSPLsun/nio/fs/UnixPath;->resolve(Ljava/nio/file/Path;)Ljava/nio/file/Path;
HSPLsun/nio/fs/UnixPath;->resolve(Ljava/nio/file/Path;)Lsun/nio/fs/UnixPath;
@@ -9262,12 +9388,12 @@
HSPLsun/security/jca/ProviderList$ServiceList;-><init>(Lsun/security/jca/ProviderList;Ljava/lang/String;Ljava/lang/String;)V
HSPLsun/security/jca/ProviderList$ServiceList;->addService(Ljava/security/Provider$Service;)V
HSPLsun/security/jca/ProviderList$ServiceList;->iterator()Ljava/util/Iterator;
-HSPLsun/security/jca/ProviderList$ServiceList;->tryGet(I)Ljava/security/Provider$Service;+]Lsun/security/jca/ProviderList;Lsun/security/jca/ProviderList;]Ljava/util/List;Ljava/util/ArrayList;]Ljava/security/Provider;missing_types
+HSPLsun/security/jca/ProviderList$ServiceList;->tryGet(I)Ljava/security/Provider$Service;
HSPLsun/security/jca/ProviderList;->-$$Nest$fgetconfigs(Lsun/security/jca/ProviderList;)[Lsun/security/jca/ProviderConfig;
HSPLsun/security/jca/ProviderList;-><init>([Lsun/security/jca/ProviderConfig;Z)V
HSPLsun/security/jca/ProviderList;->getIndex(Ljava/lang/String;)I
HSPLsun/security/jca/ProviderList;->getJarList([Ljava/lang/String;)Lsun/security/jca/ProviderList;
-HSPLsun/security/jca/ProviderList;->getProvider(I)Ljava/security/Provider;+]Lsun/security/jca/ProviderConfig;Lsun/security/jca/ProviderConfig;
+HSPLsun/security/jca/ProviderList;->getProvider(I)Ljava/security/Provider;
HSPLsun/security/jca/ProviderList;->getProvider(Ljava/lang/String;)Ljava/security/Provider;
HSPLsun/security/jca/ProviderList;->getProviderConfig(Ljava/lang/String;)Lsun/security/jca/ProviderConfig;
HSPLsun/security/jca/ProviderList;->getService(Ljava/lang/String;Ljava/lang/String;)Ljava/security/Provider$Service;
@@ -9471,7 +9597,7 @@
HSPLsun/security/util/DerInputBuffer;->peek()I
HSPLsun/security/util/DerInputBuffer;->toByteArray()[B
HSPLsun/security/util/DerInputBuffer;->truncate(I)V
-HSPLsun/security/util/DerInputStream;-><init>(Lsun/security/util/DerInputBuffer;)V+]Lsun/security/util/DerInputBuffer;Lsun/security/util/DerInputBuffer;
+HSPLsun/security/util/DerInputStream;-><init>(Lsun/security/util/DerInputBuffer;)V
HSPLsun/security/util/DerInputStream;-><init>([B)V
HSPLsun/security/util/DerInputStream;->available()I
HSPLsun/security/util/DerInputStream;->getBigInteger()Ljava/math/BigInteger;
@@ -9492,11 +9618,11 @@
HSPLsun/security/util/DerInputStream;->getSet(IZZ)[Lsun/security/util/DerValue;
HSPLsun/security/util/DerInputStream;->getUTCTime()Ljava/util/Date;
HSPLsun/security/util/DerInputStream;->getUnalignedBitString()Lsun/security/util/BitArray;
-HSPLsun/security/util/DerInputStream;->init([BIIZ)V+]Lsun/security/util/DerInputBuffer;Lsun/security/util/DerInputBuffer;
+HSPLsun/security/util/DerInputStream;->init([BIIZ)V
HSPLsun/security/util/DerInputStream;->mark(I)V
HSPLsun/security/util/DerInputStream;->peekByte()I
HSPLsun/security/util/DerInputStream;->readVector(I)[Lsun/security/util/DerValue;
-HSPLsun/security/util/DerInputStream;->readVector(IZ)[Lsun/security/util/DerValue;+]Lsun/security/util/DerInputBuffer;Lsun/security/util/DerInputBuffer;]Ljava/util/Vector;Ljava/util/Vector;]Lsun/security/util/DerInputStream;Lsun/security/util/DerInputStream;
+HSPLsun/security/util/DerInputStream;->readVector(IZ)[Lsun/security/util/DerValue;
HSPLsun/security/util/DerInputStream;->reset()V
HSPLsun/security/util/DerInputStream;->subStream(IZ)Lsun/security/util/DerInputStream;
HSPLsun/security/util/DerInputStream;->toByteArray()[B
@@ -9516,7 +9642,7 @@
HSPLsun/security/util/DerValue;-><init>(Ljava/lang/String;)V
HSPLsun/security/util/DerValue;-><init>(Lsun/security/util/DerInputBuffer;Z)V+]Lsun/security/util/DerInputBuffer;Lsun/security/util/DerInputBuffer;
HSPLsun/security/util/DerValue;-><init>([B)V
-HSPLsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V+]Lsun/security/util/DerInputBuffer;Lsun/security/util/DerInputBuffer;]Lsun/security/util/DerOutputStream;Lsun/security/util/DerOutputStream;
+HSPLsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V
HSPLsun/security/util/DerValue;->getBigInteger()Ljava/math/BigInteger;
HSPLsun/security/util/DerValue;->getBitString()[B
HSPLsun/security/util/DerValue;->getBoolean()Z
@@ -9530,7 +9656,7 @@
HSPLsun/security/util/DerValue;->getTag()B
HSPLsun/security/util/DerValue;->getUnalignedBitString()Lsun/security/util/BitArray;
HSPLsun/security/util/DerValue;->init(BLjava/lang/String;)Lsun/security/util/DerInputStream;
-HSPLsun/security/util/DerValue;->init(ZLjava/io/InputStream;)Lsun/security/util/DerInputStream;+]Ljava/io/InputStream;Lsun/security/util/DerInputBuffer;,Ljava/io/ByteArrayInputStream;
+HSPLsun/security/util/DerValue;->init(ZLjava/io/InputStream;)Lsun/security/util/DerInputStream;
HSPLsun/security/util/DerValue;->isConstructed()Z
HSPLsun/security/util/DerValue;->isContextSpecific()Z
HSPLsun/security/util/DerValue;->isContextSpecific(B)Z
@@ -9572,7 +9698,7 @@
HSPLsun/security/util/MemoryCache;->newEntry(Ljava/lang/Object;Ljava/lang/Object;JLjava/lang/ref/ReferenceQueue;)Lsun/security/util/MemoryCache$CacheEntry;
HSPLsun/security/util/MemoryCache;->put(Ljava/lang/Object;Ljava/lang/Object;)V
HSPLsun/security/util/ObjectIdentifier;-><init>(Lsun/security/util/DerInputBuffer;)V
-HSPLsun/security/util/ObjectIdentifier;-><init>(Lsun/security/util/DerInputStream;)V+]Lsun/security/util/DerInputStream;Lsun/security/util/DerInputStream;
+HSPLsun/security/util/ObjectIdentifier;-><init>(Lsun/security/util/DerInputStream;)V
HSPLsun/security/util/ObjectIdentifier;->check([B)V
HSPLsun/security/util/ObjectIdentifier;->encode(Lsun/security/util/DerOutputStream;)V
HSPLsun/security/util/ObjectIdentifier;->equals(Ljava/lang/Object;)Z
@@ -9591,14 +9717,14 @@
HSPLsun/security/util/SignatureFileVerifier;->verifyManifestHash(Ljava/util/jar/Manifest;Lsun/security/util/ManifestDigester;Ljava/util/List;)Z
HSPLsun/security/x509/AVA;-><init>(Ljava/io/Reader;ILjava/util/Map;)V
HSPLsun/security/x509/AVA;-><init>(Ljava/io/Reader;Ljava/util/Map;)V
-HSPLsun/security/x509/AVA;-><init>(Lsun/security/util/DerValue;)V+]Lsun/security/util/DerInputStream;Lsun/security/util/DerInputStream;
+HSPLsun/security/x509/AVA;-><init>(Lsun/security/util/DerValue;)V
HSPLsun/security/x509/AVA;->derEncode(Ljava/io/OutputStream;)V
HSPLsun/security/x509/AVA;->isDerString(Lsun/security/util/DerValue;Z)Z
HSPLsun/security/x509/AVA;->isTerminator(II)Z
HSPLsun/security/x509/AVA;->parseString(Ljava/io/Reader;IILjava/lang/StringBuilder;)Lsun/security/util/DerValue;
HSPLsun/security/x509/AVA;->readChar(Ljava/io/Reader;Ljava/lang/String;)I
HSPLsun/security/x509/AVA;->toKeyword(ILjava/util/Map;)Ljava/lang/String;
-HSPLsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String;+]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Ljava/lang/String;Ljava/lang/String;]Lsun/security/util/DerValue;Lsun/security/util/DerValue;
+HSPLsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String;
HSPLsun/security/x509/AVA;->toRFC2253String(Ljava/util/Map;)Ljava/lang/String;
HSPLsun/security/x509/AVAKeyword;->getKeyword(Lsun/security/util/ObjectIdentifier;ILjava/util/Map;)Ljava/lang/String;
HSPLsun/security/x509/AVAKeyword;->getOID(Ljava/lang/String;ILjava/util/Map;)Lsun/security/util/ObjectIdentifier;
@@ -9683,7 +9809,7 @@
HSPLsun/security/x509/PolicyInformation;->getPolicyIdentifier()Lsun/security/x509/CertificatePolicyId;
HSPLsun/security/x509/PolicyInformation;->getPolicyQualifiers()Ljava/util/Set;
HSPLsun/security/x509/RDN;-><init>(Ljava/lang/String;Ljava/util/Map;)V
-HSPLsun/security/x509/RDN;-><init>(Lsun/security/util/DerValue;)V+]Lsun/security/util/DerValue;Lsun/security/util/DerValue;]Lsun/security/util/DerInputStream;Lsun/security/util/DerInputStream;
+HSPLsun/security/x509/RDN;-><init>(Lsun/security/util/DerValue;)V
HSPLsun/security/x509/RDN;->encode(Lsun/security/util/DerOutputStream;)V
HSPLsun/security/x509/RDN;->toRFC2253String(Ljava/util/Map;)Ljava/lang/String;
HSPLsun/security/x509/RDN;->toRFC2253String(Z)Ljava/lang/String;
@@ -9777,7 +9903,7 @@
HSPLsun/util/calendar/BaseCalendar$Date;->hit(J)Z
HSPLsun/util/calendar/BaseCalendar$Date;->setCache(IJI)V
HSPLsun/util/calendar/BaseCalendar;-><init>()V
-HSPLsun/util/calendar/BaseCalendar;->getCalendarDateFromFixedDate(Lsun/util/calendar/CalendarDate;J)V+]Lsun/util/calendar/BaseCalendar$Date;Lsun/util/calendar/Gregorian$Date;]Lsun/util/calendar/BaseCalendar;Lsun/util/calendar/Gregorian;
+HSPLsun/util/calendar/BaseCalendar;->getCalendarDateFromFixedDate(Lsun/util/calendar/CalendarDate;J)V
HSPLsun/util/calendar/BaseCalendar;->getDayOfWeekFromFixedDate(J)I
HSPLsun/util/calendar/BaseCalendar;->getDayOfYear(III)J
HSPLsun/util/calendar/BaseCalendar;->getFixedDate(IIILsun/util/calendar/BaseCalendar$Date;)J
@@ -9853,17 +9979,16 @@
HSPLsun/util/locale/BaseLocale$Cache;->createObject(Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale;
HSPLsun/util/locale/BaseLocale$Cache;->normalizeKey(Ljava/lang/Object;)Ljava/lang/Object;
HSPLsun/util/locale/BaseLocale$Cache;->normalizeKey(Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale$Key;
-HSPLsun/util/locale/BaseLocale$Key;->-$$Nest$fgetlang(Lsun/util/locale/BaseLocale$Key;)Ljava/lang/ref/SoftReference;
-HSPLsun/util/locale/BaseLocale$Key;->-$$Nest$fgetregn(Lsun/util/locale/BaseLocale$Key;)Ljava/lang/ref/SoftReference;
-HSPLsun/util/locale/BaseLocale$Key;->-$$Nest$fgetscrt(Lsun/util/locale/BaseLocale$Key;)Ljava/lang/ref/SoftReference;
-HSPLsun/util/locale/BaseLocale$Key;->-$$Nest$fgetvart(Lsun/util/locale/BaseLocale$Key;)Ljava/lang/ref/SoftReference;
-HSPLsun/util/locale/BaseLocale$Key;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+HSPLsun/util/locale/BaseLocale$Key;->-$$Nest$mgetBaseLocale(Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale;
HSPLsun/util/locale/BaseLocale$Key;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
-HSPLsun/util/locale/BaseLocale$Key;->equals(Ljava/lang/Object;)Z+]Ljava/lang/ref/SoftReference;Ljava/lang/ref/SoftReference;
+HSPLsun/util/locale/BaseLocale$Key;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLsun/util/locale/BaseLocale$Key-IA;)V
+HSPLsun/util/locale/BaseLocale$Key;->equals(Ljava/lang/Object;)Z
+HSPLsun/util/locale/BaseLocale$Key;->getBaseLocale()Lsun/util/locale/BaseLocale;
HSPLsun/util/locale/BaseLocale$Key;->hashCode()I
+HSPLsun/util/locale/BaseLocale$Key;->hashCode(Lsun/util/locale/BaseLocale;)I
HSPLsun/util/locale/BaseLocale$Key;->normalize(Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale$Key;
-HSPLsun/util/locale/BaseLocale;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-HSPLsun/util/locale/BaseLocale;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lsun/util/locale/BaseLocale-IA;)V
+HSPLsun/util/locale/BaseLocale;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
+HSPLsun/util/locale/BaseLocale;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLsun/util/locale/BaseLocale-IA;)V
HSPLsun/util/locale/BaseLocale;->cleanCache()V
HSPLsun/util/locale/BaseLocale;->equals(Ljava/lang/Object;)Z
HSPLsun/util/locale/BaseLocale;->getInstance(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/BaseLocale;
@@ -9942,6 +10067,7 @@
HSPLsun/util/locale/StringTokenIterator;->next()Ljava/lang/String;
HSPLsun/util/locale/StringTokenIterator;->nextDelimiter(I)I
HSPLsun/util/locale/StringTokenIterator;->setStart(I)Lsun/util/locale/StringTokenIterator;
+HSPLsun/util/locale/provider/CalendarDataUtility;->retrieveFirstDayOfWeek(Ljava/util/Locale;I)I
HSPLsun/util/logging/LoggingSupport$2;-><init>()V
HSPLsun/util/logging/LoggingSupport$2;->run()Ljava/lang/Object;
HSPLsun/util/logging/LoggingSupport$2;->run()Ljava/lang/String;
@@ -9951,7 +10077,7 @@
HSPLsun/util/logging/PlatformLogger$JavaLoggerProxy;-><init>(Ljava/lang/String;Lsun/util/logging/PlatformLogger$Level;)V
HSPLsun/util/logging/PlatformLogger$LoggerProxy;-><init>(Ljava/lang/String;)V
HSPLsun/util/logging/PlatformLogger;-><init>(Ljava/lang/String;)V
-HSPLsun/util/logging/PlatformLogger;->getLogger(Ljava/lang/String;)Lsun/util/logging/PlatformLogger;+]Ljava/lang/ref/WeakReference;Ljava/lang/ref/WeakReference;]Ljava/util/Map;Ljava/util/HashMap;
+HSPLsun/util/logging/PlatformLogger;->getLogger(Ljava/lang/String;)Lsun/util/logging/PlatformLogger;
Landroid/compat/Compatibility$1;
Landroid/compat/Compatibility$BehaviorChangeDelegate;
Landroid/compat/Compatibility$ChangeConfig;
@@ -10019,6 +10145,7 @@
Lcom/android/okhttp/HttpUrl;
Lcom/android/okhttp/HttpsHandler;
Lcom/android/okhttp/Interceptor$Chain;
+Lcom/android/okhttp/MediaType;
Lcom/android/okhttp/OkCacheContainer;
Lcom/android/okhttp/OkHttpClient$1;
Lcom/android/okhttp/OkHttpClient;
@@ -10052,8 +10179,11 @@
Lcom/android/okhttp/internal/Version;
Lcom/android/okhttp/internal/framed/FrameWriter;
Lcom/android/okhttp/internal/framed/FramedConnection$Builder;
+Lcom/android/okhttp/internal/framed/FramedConnection$Listener$1;
Lcom/android/okhttp/internal/framed/FramedConnection$Listener;
Lcom/android/okhttp/internal/framed/FramedConnection;
+Lcom/android/okhttp/internal/framed/Header;
+Lcom/android/okhttp/internal/framed/PushObserver$1;
Lcom/android/okhttp/internal/framed/PushObserver;
Lcom/android/okhttp/internal/framed/Settings;
Lcom/android/okhttp/internal/http/AuthenticatorAdapter;
@@ -10360,6 +10490,7 @@
Lcom/android/org/bouncycastle/jcajce/provider/symmetric/util/BaseWrapCipher;
Lcom/android/org/bouncycastle/jcajce/provider/symmetric/util/BlockCipherProvider;
Lcom/android/org/bouncycastle/jcajce/provider/symmetric/util/ClassUtil;
+Lcom/android/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil$2;
Lcom/android/org/bouncycastle/jcajce/provider/symmetric/util/GcmSpecUtil;
Lcom/android/org/bouncycastle/jcajce/provider/symmetric/util/PBE$Util;
Lcom/android/org/bouncycastle/jcajce/provider/symmetric/util/PBE;
@@ -10406,6 +10537,7 @@
Ldalvik/annotation/optimization/CriticalNative;
Ldalvik/annotation/optimization/FastNative;
Ldalvik/annotation/optimization/NeverCompile;
+Ldalvik/annotation/optimization/NeverInline;
Ldalvik/system/AppSpecializationHooks;
Ldalvik/system/BaseDexClassLoader$Reporter;
Ldalvik/system/BaseDexClassLoader;
@@ -10440,13 +10572,16 @@
Ldalvik/system/SocketTagger;
Ldalvik/system/VMDebug;
Ldalvik/system/VMRuntime$HiddenApiUsageLogger;
+Ldalvik/system/VMRuntime$SdkVersionContainer;
Ldalvik/system/VMRuntime;
Ldalvik/system/VMStack;
+Ldalvik/system/ZipPathValidator$1;
+Ldalvik/system/ZipPathValidator$Callback;
+Ldalvik/system/ZipPathValidator;
Ldalvik/system/ZygoteHooks;
Ljava/awt/font/NumericShaper;
Ljava/awt/font/TextAttribute;
Ljava/io/Bits;
-Ljava/io/BufferedInputStream$$ExternalSyntheticBackportWithForwarding0;
Ljava/io/BufferedInputStream;
Ljava/io/BufferedOutputStream;
Ljava/io/BufferedReader;
@@ -10564,6 +10699,8 @@
Ljava/io/WriteAbortedException;
Ljava/io/Writer;
Ljava/lang/AbstractMethodError;
+Ljava/lang/AbstractStringBuilder$$ExternalSyntheticLambda0;
+Ljava/lang/AbstractStringBuilder$$ExternalSyntheticLambda1;
Ljava/lang/AbstractStringBuilder;
Ljava/lang/AndroidHardcodedSystemProperties;
Ljava/lang/Appendable;
@@ -10624,8 +10761,6 @@
Ljava/lang/InheritableThreadLocal;
Ljava/lang/InstantiationError;
Ljava/lang/InstantiationException;
-Ljava/lang/Integer$$ExternalSyntheticBackport0;
-Ljava/lang/Integer$$ExternalSyntheticBackport1;
Ljava/lang/Integer$IntegerCache;
Ljava/lang/Integer;
Ljava/lang/InternalError;
@@ -10671,9 +10806,18 @@
Ljava/lang/SecurityManager;
Ljava/lang/Short$ShortCache;
Ljava/lang/Short;
+Ljava/lang/StackFrameInfo;
Ljava/lang/StackOverflowError;
+Ljava/lang/StackStreamFactory$AbstractStackWalker;
+Ljava/lang/StackStreamFactory;
Ljava/lang/StackTraceElement;
+Ljava/lang/StackWalker$StackFrame;
+Ljava/lang/StackWalker;
Ljava/lang/StrictMath;
+Ljava/lang/String$$ExternalSyntheticLambda0;
+Ljava/lang/String$$ExternalSyntheticLambda1;
+Ljava/lang/String$$ExternalSyntheticLambda2;
+Ljava/lang/String$$ExternalSyntheticLambda3;
Ljava/lang/String$CaseInsensitiveComparator-IA;
Ljava/lang/String$CaseInsensitiveComparator;
Ljava/lang/String;
@@ -10681,8 +10825,13 @@
Ljava/lang/StringBuilder;
Ljava/lang/StringFactory;
Ljava/lang/StringIndexOutOfBoundsException;
+Ljava/lang/StringLatin1$CharsSpliterator;
+Ljava/lang/StringLatin1$LinesSpliterator;
+Ljava/lang/StringLatin1;
Ljava/lang/StringUTF16$CharsSpliterator;
+Ljava/lang/StringUTF16$CharsSpliteratorForString;
Ljava/lang/StringUTF16$CodePointsSpliterator;
+Ljava/lang/StringUTF16$CodePointsSpliteratorForString;
Ljava/lang/StringUTF16;
Ljava/lang/System$PropertiesWithNonOverrideableDefaults;
Ljava/lang/System;
@@ -10713,6 +10862,7 @@
Ljava/lang/UNIXProcess$ProcessReaperThreadFactory;
Ljava/lang/UNIXProcess;
Ljava/lang/UnsatisfiedLinkError;
+Ljava/lang/UnsupportedClassVersionError;
Ljava/lang/UnsupportedOperationException;
Ljava/lang/VMClassLoader;
Ljava/lang/VerifyError;
@@ -10790,6 +10940,7 @@
Ljava/lang/invoke/Transformers$ReferenceArrayElementSetter;
Ljava/lang/invoke/Transformers$ReferenceIdentity;
Ljava/lang/invoke/Transformers$Spreader;
+Ljava/lang/invoke/Transformers$TableSwitch;
Ljava/lang/invoke/Transformers$Transformer;
Ljava/lang/invoke/Transformers$TryFinally;
Ljava/lang/invoke/Transformers$VarargsCollector;
@@ -10850,6 +11001,7 @@
Ljava/lang/reflect/WildcardType;
Ljava/math/BigDecimal$1;
Ljava/math/BigDecimal$LongOverflow;
+Ljava/math/BigDecimal$StringBuilderHelper;
Ljava/math/BigDecimal;
Ljava/math/BigInteger$UnsafeHolder;
Ljava/math/BigInteger;
@@ -10926,6 +11078,7 @@
Ljava/net/Proxy;
Ljava/net/ProxySelector;
Ljava/net/ResponseCache;
+Ljava/net/ServerSocket$1;
Ljava/net/ServerSocket;
Ljava/net/Socket$1;
Ljava/net/Socket$2;
@@ -10959,6 +11112,7 @@
Ljava/net/UnknownServiceException;
Ljava/nio/Bits;
Ljava/nio/Buffer;
+Ljava/nio/BufferMismatch;
Ljava/nio/BufferOverflowException;
Ljava/nio/BufferUnderflowException;
Ljava/nio/ByteBuffer;
@@ -11039,8 +11193,6 @@
Ljava/nio/charset/CharsetDecoder;
Ljava/nio/charset/CharsetEncoder;
Ljava/nio/charset/CoderMalfunctionError;
-Ljava/nio/charset/CoderResult$1;
-Ljava/nio/charset/CoderResult$2;
Ljava/nio/charset/CoderResult$Cache;
Ljava/nio/charset/CoderResult;
Ljava/nio/charset/CodingErrorAction;
@@ -11061,6 +11213,8 @@
Ljava/nio/file/FileSystems$DefaultFileSystemHolder$1;
Ljava/nio/file/FileSystems$DefaultFileSystemHolder;
Ljava/nio/file/FileSystems;
+Ljava/nio/file/FileVisitResult;
+Ljava/nio/file/FileVisitor;
Ljava/nio/file/Files$AcceptAllFilter;
Ljava/nio/file/Files;
Ljava/nio/file/InvalidPathException;
@@ -11072,6 +11226,7 @@
Ljava/nio/file/Paths;
Ljava/nio/file/ProviderMismatchException;
Ljava/nio/file/SecureDirectoryStream;
+Ljava/nio/file/SimpleFileVisitor;
Ljava/nio/file/StandardCopyOption;
Ljava/nio/file/StandardOpenOption;
Ljava/nio/file/Watchable;
@@ -11290,6 +11445,7 @@
Ljava/time/Duration;
Ljava/time/Instant$1;
Ljava/time/Instant;
+Ljava/time/InstantSource;
Ljava/time/LocalDate$1;
Ljava/time/LocalDate;
Ljava/time/LocalDateTime;
@@ -11319,7 +11475,6 @@
Ljava/time/format/DateTimeFormatterBuilder$$ExternalSyntheticLambda0;
Ljava/time/format/DateTimeFormatterBuilder$1;
Ljava/time/format/DateTimeFormatterBuilder$2;
-Ljava/time/format/DateTimeFormatterBuilder$3;
Ljava/time/format/DateTimeFormatterBuilder$CharLiteralPrinterParser;
Ljava/time/format/DateTimeFormatterBuilder$CompositePrinterParser;
Ljava/time/format/DateTimeFormatterBuilder$DateTimePrinterParser;
@@ -11407,14 +11562,15 @@
Ljava/util/AbstractQueue;
Ljava/util/AbstractSequentialList;
Ljava/util/AbstractSet;
+Ljava/util/ArrayDeque$$ExternalSyntheticLambda1;
Ljava/util/ArrayDeque$DeqIterator;
Ljava/util/ArrayDeque$DescendingIterator;
Ljava/util/ArrayDeque;
Ljava/util/ArrayList$ArrayListSpliterator;
-Ljava/util/ArrayList$Itr-IA;
Ljava/util/ArrayList$Itr;
Ljava/util/ArrayList$ListItr;
Ljava/util/ArrayList$SubList$1;
+Ljava/util/ArrayList$SubList$2;
Ljava/util/ArrayList$SubList;
Ljava/util/ArrayList;
Ljava/util/ArrayPrefixHelpers$CumulateTask;
@@ -11429,18 +11585,12 @@
Ljava/util/Arrays$ArrayList;
Ljava/util/Arrays$NaturalOrder;
Ljava/util/Arrays;
-Ljava/util/ArraysParallelSortHelpers$FJByte$Sorter;
-Ljava/util/ArraysParallelSortHelpers$FJChar$Sorter;
-Ljava/util/ArraysParallelSortHelpers$FJDouble$Sorter;
-Ljava/util/ArraysParallelSortHelpers$FJFloat$Sorter;
-Ljava/util/ArraysParallelSortHelpers$FJInt$Sorter;
-Ljava/util/ArraysParallelSortHelpers$FJLong$Sorter;
Ljava/util/ArraysParallelSortHelpers$FJObject$Sorter;
-Ljava/util/ArraysParallelSortHelpers$FJShort$Sorter;
Ljava/util/Base64$Decoder;
Ljava/util/Base64$Encoder;
Ljava/util/Base64;
Ljava/util/BitSet;
+Ljava/util/Calendar$$ExternalSyntheticLambda0;
Ljava/util/Calendar$Builder;
Ljava/util/Calendar;
Ljava/util/Collection;
@@ -11515,6 +11665,7 @@
Ljava/util/Date;
Ljava/util/Deque;
Ljava/util/Dictionary;
+Ljava/util/DualPivotQuicksort$Sorter;
Ljava/util/DualPivotQuicksort;
Ljava/util/DuplicateFormatFlagsException;
Ljava/util/EmptyStackException;
@@ -11587,14 +11738,15 @@
Ljava/util/ImmutableCollections$AbstractImmutableMap;
Ljava/util/ImmutableCollections$AbstractImmutableSet;
Ljava/util/ImmutableCollections$List12;
+Ljava/util/ImmutableCollections$ListItr;
Ljava/util/ImmutableCollections$ListN;
-Ljava/util/ImmutableCollections$Map0;
Ljava/util/ImmutableCollections$Map1;
+Ljava/util/ImmutableCollections$MapN$1;
+Ljava/util/ImmutableCollections$MapN$MapNIterator;
Ljava/util/ImmutableCollections$MapN;
-Ljava/util/ImmutableCollections$Set0;
-Ljava/util/ImmutableCollections$Set1;
-Ljava/util/ImmutableCollections$Set2;
+Ljava/util/ImmutableCollections$Set12;
Ljava/util/ImmutableCollections$SetN;
+Ljava/util/ImmutableCollections$SubList;
Ljava/util/ImmutableCollections;
Ljava/util/InputMismatchException;
Ljava/util/Iterator;
@@ -11611,6 +11763,7 @@
Ljava/util/LinkedHashMap$LinkedValues;
Ljava/util/LinkedHashMap;
Ljava/util/LinkedHashSet;
+Ljava/util/LinkedList$DescendingIterator;
Ljava/util/LinkedList$ListItr;
Ljava/util/LinkedList$Node;
Ljava/util/LinkedList;
@@ -11622,6 +11775,10 @@
Ljava/util/Locale$Cache;
Ljava/util/Locale$Category;
Ljava/util/Locale$FilteringMode;
+Ljava/util/Locale$IsoCountryCode$1;
+Ljava/util/Locale$IsoCountryCode$2;
+Ljava/util/Locale$IsoCountryCode$3;
+Ljava/util/Locale$IsoCountryCode;
Ljava/util/Locale$LanguageRange;
Ljava/util/Locale$LocaleKey;
Ljava/util/Locale$NoImagePreloadHolder;
@@ -11658,13 +11815,15 @@
Ljava/util/ResourceBundle$BundleReference;
Ljava/util/ResourceBundle$CacheKey;
Ljava/util/ResourceBundle$CacheKeyReference;
+Ljava/util/ResourceBundle$Control$$ExternalSyntheticLambda0;
Ljava/util/ResourceBundle$Control$1;
Ljava/util/ResourceBundle$Control$CandidateListCache;
Ljava/util/ResourceBundle$Control;
-Ljava/util/ResourceBundle$LoaderReference;
+Ljava/util/ResourceBundle$KeyElementReference;
+Ljava/util/ResourceBundle$RBClassLoader;
Ljava/util/ResourceBundle$SingleFormatControl;
Ljava/util/ResourceBundle;
-Ljava/util/Scanner$1;
+Ljava/util/Scanner$PatternLRUCache;
Ljava/util/Scanner;
Ljava/util/ServiceConfigurationError;
Ljava/util/ServiceLoader$1;
@@ -11719,6 +11878,7 @@
Ljava/util/TreeMap$Values;
Ljava/util/TreeMap;
Ljava/util/TreeSet;
+Ljava/util/Tripwire$$ExternalSyntheticLambda0;
Ljava/util/Tripwire;
Ljava/util/UUID$Holder;
Ljava/util/UUID;
@@ -11747,6 +11907,8 @@
Ljava/util/concurrent/Callable;
Ljava/util/concurrent/CancellationException;
Ljava/util/concurrent/CompletableFuture$AltResult;
+Ljava/util/concurrent/CompletableFuture$AsyncRun;
+Ljava/util/concurrent/CompletableFuture$AsyncSupply;
Ljava/util/concurrent/CompletableFuture$AsynchronousCompletionTask;
Ljava/util/concurrent/CompletableFuture$Completion;
Ljava/util/concurrent/CompletableFuture$Signaller;
@@ -11843,12 +12005,13 @@
Ljava/util/concurrent/Executors$RunnableAdapter;
Ljava/util/concurrent/Executors;
Ljava/util/concurrent/ForkJoinPool$1;
+Ljava/util/concurrent/ForkJoinPool$DefaultCommonPoolForkJoinWorkerThreadFactory;
Ljava/util/concurrent/ForkJoinPool$DefaultForkJoinWorkerThreadFactory;
Ljava/util/concurrent/ForkJoinPool$ForkJoinWorkerThreadFactory;
Ljava/util/concurrent/ForkJoinPool$ManagedBlocker;
Ljava/util/concurrent/ForkJoinPool$WorkQueue;
Ljava/util/concurrent/ForkJoinPool;
-Ljava/util/concurrent/ForkJoinTask$ExceptionNode;
+Ljava/util/concurrent/ForkJoinTask$Aux;
Ljava/util/concurrent/ForkJoinTask;
Ljava/util/concurrent/ForkJoinWorkerThread;
Ljava/util/concurrent/Future;
@@ -11910,8 +12073,11 @@
Ljava/util/concurrent/atomic/Striped64$Cell;
Ljava/util/concurrent/atomic/Striped64;
Ljava/util/concurrent/locks/AbstractOwnableSynchronizer;
+Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionNode;
Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;
+Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ExclusiveNode;
Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$Node;
+Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$SharedNode;
Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;
Ljava/util/concurrent/locks/Condition;
Ljava/util/concurrent/locks/Lock;
@@ -11958,9 +12124,13 @@
Ljava/util/function/IntUnaryOperator;
Ljava/util/function/LongBinaryOperator;
Ljava/util/function/LongConsumer;
+Ljava/util/function/LongFunction;
Ljava/util/function/LongPredicate;
Ljava/util/function/LongSupplier;
Ljava/util/function/LongUnaryOperator;
+Ljava/util/function/ObjDoubleConsumer;
+Ljava/util/function/ObjIntConsumer;
+Ljava/util/function/ObjLongConsumer;
Ljava/util/function/Predicate;
Ljava/util/function/Supplier;
Ljava/util/function/ToDoubleBiFunction;
@@ -12037,6 +12207,7 @@
Ljava/util/stream/Collector$Characteristics;
Ljava/util/stream/Collector;
Ljava/util/stream/Collectors$$ExternalSyntheticLambda0;
+Ljava/util/stream/Collectors$$ExternalSyntheticLambda13;
Ljava/util/stream/Collectors$$ExternalSyntheticLambda15;
Ljava/util/stream/Collectors$$ExternalSyntheticLambda1;
Ljava/util/stream/Collectors$$ExternalSyntheticLambda20;
@@ -12102,6 +12273,8 @@
Ljava/util/stream/IntPipeline$$ExternalSyntheticLambda8;
Ljava/util/stream/IntPipeline$4$1;
Ljava/util/stream/IntPipeline$4;
+Ljava/util/stream/IntPipeline$9$1;
+Ljava/util/stream/IntPipeline$9;
Ljava/util/stream/IntPipeline$Head;
Ljava/util/stream/IntPipeline$StatelessOp;
Ljava/util/stream/IntPipeline;
@@ -12174,6 +12347,7 @@
Ljava/util/stream/ReferencePipeline$5;
Ljava/util/stream/ReferencePipeline$6$1;
Ljava/util/stream/ReferencePipeline$6;
+Ljava/util/stream/ReferencePipeline$7$1;
Ljava/util/stream/ReferencePipeline$7;
Ljava/util/stream/ReferencePipeline$Head;
Ljava/util/stream/ReferencePipeline$StatefulOp;
@@ -12218,10 +12392,12 @@
Ljava/util/stream/Streams;
Ljava/util/stream/TerminalOp;
Ljava/util/stream/TerminalSink;
+Ljava/util/stream/Tripwire$$ExternalSyntheticLambda0;
Ljava/util/stream/Tripwire;
Ljava/util/zip/Adler32;
Ljava/util/zip/CRC32;
Ljava/util/zip/CheckedInputStream;
+Ljava/util/zip/Checksum$1;
Ljava/util/zip/Checksum;
Ljava/util/zip/DataFormatException;
Ljava/util/zip/Deflater;
@@ -12375,7 +12551,9 @@
Ljdk/internal/math/FormattedFloatingDecimal;
Ljdk/internal/misc/JavaObjectInputStreamAccess;
Ljdk/internal/misc/SharedSecrets;
+Ljdk/internal/misc/TerminatingThreadLocal;
Ljdk/internal/misc/Unsafe;
+Ljdk/internal/misc/UnsafeConstants;
Ljdk/internal/misc/VM;
Ljdk/internal/reflect/Reflection;
Ljdk/internal/util/ArraysSupport;
@@ -12387,7 +12565,6 @@
Llibcore/content/type/MimeMap;
Llibcore/icu/CollationKeyICU;
Llibcore/icu/DateIntervalFormat;
-Llibcore/icu/DateUtilsBridge;
Llibcore/icu/DecimalFormatData;
Llibcore/icu/ICU;
Llibcore/icu/LocaleData;
@@ -12460,17 +12637,24 @@
Lorg/apache/harmony/xml/ExpatException;
Lorg/apache/harmony/xml/ExpatParser$CurrentAttributes;
Lorg/apache/harmony/xml/ExpatParser$ExpatLocator;
+Lorg/apache/harmony/xml/ExpatParser$ParseException;
Lorg/apache/harmony/xml/ExpatParser;
Lorg/apache/harmony/xml/ExpatReader;
+Lorg/apache/harmony/xml/dom/AttrImpl;
+Lorg/apache/harmony/xml/dom/CDATASectionImpl;
Lorg/apache/harmony/xml/dom/CharacterDataImpl;
+Lorg/apache/harmony/xml/dom/CommentImpl;
Lorg/apache/harmony/xml/dom/DOMImplementationImpl;
Lorg/apache/harmony/xml/dom/DocumentImpl;
+Lorg/apache/harmony/xml/dom/DocumentTypeImpl;
Lorg/apache/harmony/xml/dom/ElementImpl;
+Lorg/apache/harmony/xml/dom/EntityReferenceImpl;
Lorg/apache/harmony/xml/dom/InnerNodeImpl;
Lorg/apache/harmony/xml/dom/LeafNodeImpl;
Lorg/apache/harmony/xml/dom/NodeImpl$1;
Lorg/apache/harmony/xml/dom/NodeImpl;
Lorg/apache/harmony/xml/dom/NodeListImpl;
+Lorg/apache/harmony/xml/dom/ProcessingInstructionImpl;
Lorg/apache/harmony/xml/dom/TextImpl;
Lorg/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl;
Lorg/apache/harmony/xml/parsers/DocumentBuilderImpl;
@@ -12484,15 +12668,20 @@
Lorg/json/JSONStringer$Scope;
Lorg/json/JSONStringer;
Lorg/json/JSONTokener;
+Lorg/w3c/dom/Attr;
+Lorg/w3c/dom/CDATASection;
Lorg/w3c/dom/CharacterData;
+Lorg/w3c/dom/Comment;
Lorg/w3c/dom/DOMException;
Lorg/w3c/dom/DOMImplementation;
Lorg/w3c/dom/Document;
Lorg/w3c/dom/DocumentFragment;
Lorg/w3c/dom/DocumentType;
Lorg/w3c/dom/Element;
+Lorg/w3c/dom/EntityReference;
Lorg/w3c/dom/Node;
Lorg/w3c/dom/NodeList;
+Lorg/w3c/dom/ProcessingInstruction;
Lorg/w3c/dom/Text;
Lorg/w3c/dom/TypeInfo;
Lorg/xml/sax/AttributeList;
@@ -12508,6 +12697,7 @@
Lorg/xml/sax/SAXException;
Lorg/xml/sax/SAXNotRecognizedException;
Lorg/xml/sax/SAXNotSupportedException;
+Lorg/xml/sax/SAXParseException;
Lorg/xml/sax/XMLFilter;
Lorg/xml/sax/XMLReader;
Lorg/xml/sax/ext/DeclHandler;
@@ -12516,6 +12706,7 @@
Lorg/xml/sax/ext/LexicalHandler;
Lorg/xml/sax/helpers/AttributesImpl;
Lorg/xml/sax/helpers/DefaultHandler;
+Lorg/xml/sax/helpers/LocatorImpl;
Lorg/xml/sax/helpers/NamespaceSupport;
Lorg/xml/sax/helpers/XMLFilterImpl;
Lorg/xmlpull/v1/XmlPullParser;
@@ -12536,7 +12727,6 @@
Lsun/misc/JavaIOFileDescriptorAccess;
Lsun/misc/LRUCache;
Lsun/misc/SharedSecrets;
-Lsun/misc/Unsafe$$ExternalSyntheticBackportWithForwarding0;
Lsun/misc/Unsafe;
Lsun/misc/VM;
Lsun/misc/Version;
@@ -12705,6 +12895,7 @@
Lsun/security/util/AbstractAlgorithmConstraints$1;
Lsun/security/util/AbstractAlgorithmConstraints;
Lsun/security/util/AlgorithmDecomposer;
+Lsun/security/util/AnchorCertificates$1;
Lsun/security/util/AnchorCertificates;
Lsun/security/util/BitArray;
Lsun/security/util/ByteArrayLexOrder;
@@ -12737,6 +12928,8 @@
Lsun/security/util/MemoryCache$SoftCacheEntry;
Lsun/security/util/MemoryCache;
Lsun/security/util/ObjectIdentifier;
+Lsun/security/util/PropertyExpander;
+Lsun/security/util/Resources;
Lsun/security/util/ResourcesMgr$1;
Lsun/security/util/ResourcesMgr;
Lsun/security/util/SecurityConstants;
@@ -12813,6 +13006,7 @@
Lsun/util/calendar/BaseCalendar$Date;
Lsun/util/calendar/BaseCalendar;
Lsun/util/calendar/CalendarDate;
+Lsun/util/calendar/CalendarSystem$GregorianHolder;
Lsun/util/calendar/CalendarSystem;
Lsun/util/calendar/CalendarUtils;
Lsun/util/calendar/Era;
@@ -12838,6 +13032,7 @@
Lsun/util/locale/ParseStatus;
Lsun/util/locale/StringTokenIterator;
Lsun/util/locale/UnicodeLocaleExtension;
+Lsun/util/locale/provider/CalendarDataUtility;
Lsun/util/logging/LoggingProxy;
Lsun/util/logging/LoggingSupport$1;
Lsun/util/logging/LoggingSupport$2;
@@ -12862,10 +13057,12 @@
[Lcom/android/okhttp/HttpUrl$Builder$ParseResult;
[Lcom/android/okhttp/Protocol;
[Lcom/android/okhttp/TlsVersion;
+[Lcom/android/okhttp/okio/ByteString;
[Lcom/android/org/bouncycastle/asn1/ASN1Encodable;
[Lcom/android/org/bouncycastle/asn1/ASN1Enumerated;
[Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;
[Lcom/android/org/bouncycastle/asn1/ASN1OctetString;
+[Lcom/android/org/bouncycastle/asn1/ASN1Primitive;
[Lcom/android/org/bouncycastle/crypto/params/DHParameters;
[Lcom/android/org/bouncycastle/crypto/params/DSAParameters;
[Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/x509/PEMUtil$Boundaries;
@@ -12896,6 +13093,7 @@
[Ljava/lang/Float;
[Ljava/lang/Integer;
[Ljava/lang/Long;
+[Ljava/lang/Number;
[Ljava/lang/Object;
[Ljava/lang/Package;
[Ljava/lang/Runnable;
@@ -12934,6 +13132,7 @@
[Ljava/net/StandardProtocolFamily;
[Ljava/nio/ByteBuffer;
[Ljava/nio/channels/SelectionKey;
+[Ljava/nio/charset/CoderResult;
[Ljava/nio/file/AccessMode;
[Ljava/nio/file/CopyOption;
[Ljava/nio/file/LinkOption;
@@ -12982,13 +13181,13 @@
[Ljava/util/Comparators$NaturalOrderComparator;
[Ljava/util/Enumeration;
[Ljava/util/Formatter$Flags;
-[Ljava/util/Formatter$FormatString;
[Ljava/util/HashMap$Node;
[Ljava/util/HashMap;
[Ljava/util/Hashtable$HashtableEntry;
[Ljava/util/List;
[Ljava/util/Locale$Category;
[Ljava/util/Locale$FilteringMode;
+[Ljava/util/Locale$IsoCountryCode;
[Ljava/util/Locale;
[Ljava/util/Map$Entry;
[Ljava/util/TimerTask;
@@ -12998,7 +13197,6 @@
[Ljava/util/concurrent/ConcurrentHashMap$Node;
[Ljava/util/concurrent/ConcurrentHashMap$Segment;
[Ljava/util/concurrent/ForkJoinPool$WorkQueue;
-[Ljava/util/concurrent/ForkJoinTask$ExceptionNode;
[Ljava/util/concurrent/ForkJoinTask;
[Ljava/util/concurrent/RunnableScheduledFuture;
[Ljava/util/concurrent/TimeUnit;
@@ -13050,6 +13248,7 @@
[Z
[[B
[[C
+[[D
[[F
[[I
[[J
diff --git a/build/boot/preloaded-classes b/build/boot/preloaded-classes
index 791c7e2..8e47bd5 100644
--- a/build/boot/preloaded-classes
+++ b/build/boot/preloaded-classes
@@ -88,6 +88,7 @@
com.android.okhttp.HttpUrl
com.android.okhttp.HttpsHandler
com.android.okhttp.Interceptor$Chain
+com.android.okhttp.MediaType
com.android.okhttp.OkCacheContainer
com.android.okhttp.OkHttpClient$1
com.android.okhttp.OkHttpClient
@@ -119,8 +120,15 @@
com.android.okhttp.internal.Util$1
com.android.okhttp.internal.Util
com.android.okhttp.internal.Version
+com.android.okhttp.internal.framed.FrameWriter
com.android.okhttp.internal.framed.FramedConnection$Builder
+com.android.okhttp.internal.framed.FramedConnection$Listener$1
+com.android.okhttp.internal.framed.FramedConnection$Listener
com.android.okhttp.internal.framed.FramedConnection
+com.android.okhttp.internal.framed.Header
+com.android.okhttp.internal.framed.PushObserver$1
+com.android.okhttp.internal.framed.PushObserver
+com.android.okhttp.internal.framed.Settings
com.android.okhttp.internal.http.AuthenticatorAdapter
com.android.okhttp.internal.http.CacheRequest
com.android.okhttp.internal.http.CacheStrategy$Factory
@@ -227,13 +235,17 @@
com.android.org.bouncycastle.asn1.ASN1TaggedObject
com.android.org.bouncycastle.asn1.ASN1TaggedObjectParser
com.android.org.bouncycastle.asn1.ASN1UTCTime
+com.android.org.bouncycastle.asn1.BERApplicationSpecific
com.android.org.bouncycastle.asn1.BERApplicationSpecificParser
com.android.org.bouncycastle.asn1.BEROctetString
com.android.org.bouncycastle.asn1.BEROctetStringParser
+com.android.org.bouncycastle.asn1.BERSequence
com.android.org.bouncycastle.asn1.BERSequenceParser
+com.android.org.bouncycastle.asn1.BERSet
com.android.org.bouncycastle.asn1.BERSetParser
com.android.org.bouncycastle.asn1.BERTaggedObjectParser
com.android.org.bouncycastle.asn1.BERTags
+com.android.org.bouncycastle.asn1.ConstructedOctetStream
com.android.org.bouncycastle.asn1.DERBMPString
com.android.org.bouncycastle.asn1.DERBitString
com.android.org.bouncycastle.asn1.DERExternalParser
@@ -260,6 +272,7 @@
com.android.org.bouncycastle.asn1.DLExternal
com.android.org.bouncycastle.asn1.DLFactory
com.android.org.bouncycastle.asn1.DLSequence
+com.android.org.bouncycastle.asn1.DLSet
com.android.org.bouncycastle.asn1.DefiniteLengthInputStream
com.android.org.bouncycastle.asn1.InMemoryRepresentable
com.android.org.bouncycastle.asn1.IndefiniteLengthInputStream
@@ -466,6 +479,7 @@
dalvik.annotation.optimization.CriticalNative
dalvik.annotation.optimization.FastNative
dalvik.annotation.optimization.NeverCompile
+dalvik.annotation.optimization.NeverInline
dalvik.system.AppSpecializationHooks
dalvik.system.BaseDexClassLoader$Reporter
dalvik.system.BaseDexClassLoader
@@ -500,13 +514,16 @@
dalvik.system.SocketTagger
dalvik.system.VMDebug
dalvik.system.VMRuntime$HiddenApiUsageLogger
+dalvik.system.VMRuntime$SdkVersionContainer
dalvik.system.VMRuntime
dalvik.system.VMStack
+dalvik.system.ZipPathValidator$1
+dalvik.system.ZipPathValidator$Callback
+dalvik.system.ZipPathValidator
dalvik.system.ZygoteHooks
java.awt.font.NumericShaper
java.awt.font.TextAttribute
java.io.Bits
-java.io.BufferedInputStream$$ExternalSyntheticBackportWithForwarding0
java.io.BufferedInputStream
java.io.BufferedOutputStream
java.io.BufferedReader
@@ -515,6 +532,7 @@
java.io.ByteArrayOutputStream
java.io.CharArrayReader
java.io.CharArrayWriter
+java.io.CharConversionException
java.io.Closeable
java.io.Console
java.io.DataInput
@@ -600,6 +618,8 @@
java.io.OptionalDataException
java.io.OutputStream
java.io.OutputStreamWriter
+java.io.PipedInputStream
+java.io.PipedOutputStream
java.io.PrintStream
java.io.PrintWriter
java.io.PushbackInputStream
@@ -621,6 +641,8 @@
java.io.WriteAbortedException
java.io.Writer
java.lang.AbstractMethodError
+java.lang.AbstractStringBuilder$$ExternalSyntheticLambda0
+java.lang.AbstractStringBuilder$$ExternalSyntheticLambda1
java.lang.AbstractStringBuilder
java.lang.AndroidHardcodedSystemProperties
java.lang.Appendable
@@ -681,8 +703,6 @@
java.lang.InheritableThreadLocal
java.lang.InstantiationError
java.lang.InstantiationException
-java.lang.Integer$$ExternalSyntheticBackport0
-java.lang.Integer$$ExternalSyntheticBackport1
java.lang.Integer$IntegerCache
java.lang.Integer
java.lang.InternalError
@@ -708,6 +728,8 @@
java.lang.Process
java.lang.ProcessBuilder$NullInputStream
java.lang.ProcessBuilder$NullOutputStream
+java.lang.ProcessBuilder$Redirect$1
+java.lang.ProcessBuilder$Redirect$2
java.lang.ProcessBuilder$Redirect
java.lang.ProcessBuilder
java.lang.ProcessEnvironment$ExternalData
@@ -726,9 +748,18 @@
java.lang.SecurityManager
java.lang.Short$ShortCache
java.lang.Short
+java.lang.StackFrameInfo
java.lang.StackOverflowError
+java.lang.StackStreamFactory$AbstractStackWalker
+java.lang.StackStreamFactory
java.lang.StackTraceElement
+java.lang.StackWalker$StackFrame
+java.lang.StackWalker
java.lang.StrictMath
+java.lang.String$$ExternalSyntheticLambda0
+java.lang.String$$ExternalSyntheticLambda1
+java.lang.String$$ExternalSyntheticLambda2
+java.lang.String$$ExternalSyntheticLambda3
java.lang.String$CaseInsensitiveComparator-IA
java.lang.String$CaseInsensitiveComparator
java.lang.String
@@ -736,8 +767,13 @@
java.lang.StringBuilder
java.lang.StringFactory
java.lang.StringIndexOutOfBoundsException
+java.lang.StringLatin1$CharsSpliterator
+java.lang.StringLatin1$LinesSpliterator
+java.lang.StringLatin1
java.lang.StringUTF16$CharsSpliterator
+java.lang.StringUTF16$CharsSpliteratorForString
java.lang.StringUTF16$CodePointsSpliterator
+java.lang.StringUTF16$CodePointsSpliteratorForString
java.lang.StringUTF16
java.lang.System$PropertiesWithNonOverrideableDefaults
java.lang.System
@@ -845,6 +881,7 @@
java.lang.invoke.Transformers$ReferenceArrayElementSetter
java.lang.invoke.Transformers$ReferenceIdentity
java.lang.invoke.Transformers$Spreader
+java.lang.invoke.Transformers$TableSwitch
java.lang.invoke.Transformers$Transformer
java.lang.invoke.Transformers$TryFinally
java.lang.invoke.Transformers$VarargsCollector
@@ -905,6 +942,7 @@
java.lang.reflect.WildcardType
java.math.BigDecimal$1
java.math.BigDecimal$LongOverflow
+java.math.BigDecimal$StringBuilderHelper
java.math.BigDecimal
java.math.BigInteger$UnsafeHolder
java.math.BigInteger
@@ -1094,14 +1132,13 @@
java.nio.charset.CharsetDecoder
java.nio.charset.CharsetEncoder
java.nio.charset.CoderMalfunctionError
-java.nio.charset.CoderResult$1
-java.nio.charset.CoderResult$2
java.nio.charset.CoderResult$Cache
java.nio.charset.CoderResult
java.nio.charset.CodingErrorAction
java.nio.charset.IllegalCharsetNameException
java.nio.charset.StandardCharsets
java.nio.charset.UnsupportedCharsetException
+java.nio.charset.spi.CharsetProvider
java.nio.file.AccessDeniedException
java.nio.file.AccessMode
java.nio.file.CopyMoveHelper
@@ -1115,6 +1152,8 @@
java.nio.file.FileSystems$DefaultFileSystemHolder$1
java.nio.file.FileSystems$DefaultFileSystemHolder
java.nio.file.FileSystems
+java.nio.file.FileVisitResult
+java.nio.file.FileVisitor
java.nio.file.Files$AcceptAllFilter
java.nio.file.Files
java.nio.file.InvalidPathException
@@ -1126,6 +1165,7 @@
java.nio.file.Paths
java.nio.file.ProviderMismatchException
java.nio.file.SecureDirectoryStream
+java.nio.file.SimpleFileVisitor
java.nio.file.StandardCopyOption
java.nio.file.StandardOpenOption
java.nio.file.Watchable
@@ -1167,12 +1207,14 @@
java.security.KeyPairGenerator
java.security.KeyPairGeneratorSpi
java.security.KeyStore$1
+java.security.KeyStore$CallbackHandlerProtection
java.security.KeyStore$Entry
java.security.KeyStore$LoadStoreParameter
java.security.KeyStore$PasswordProtection
java.security.KeyStore$PrivateKeyEntry
java.security.KeyStore$ProtectionParameter
java.security.KeyStore$SecretKeyEntry
+java.security.KeyStore$SimpleLoadStoreParameter
java.security.KeyStore$TrustedCertificateEntry
java.security.KeyStore
java.security.KeyStoreException
@@ -1371,7 +1413,6 @@
java.time.format.DateTimeFormatterBuilder$$ExternalSyntheticLambda0
java.time.format.DateTimeFormatterBuilder$1
java.time.format.DateTimeFormatterBuilder$2
-java.time.format.DateTimeFormatterBuilder$3
java.time.format.DateTimeFormatterBuilder$CharLiteralPrinterParser
java.time.format.DateTimeFormatterBuilder$CompositePrinterParser
java.time.format.DateTimeFormatterBuilder$DateTimePrinterParser
@@ -1380,6 +1421,7 @@
java.time.format.DateTimeFormatterBuilder$NumberPrinterParser
java.time.format.DateTimeFormatterBuilder$OffsetIdPrinterParser
java.time.format.DateTimeFormatterBuilder$PadPrinterParserDecorator
+java.time.format.DateTimeFormatterBuilder$PrefixTree$CI
java.time.format.DateTimeFormatterBuilder$PrefixTree
java.time.format.DateTimeFormatterBuilder$SettingsParser
java.time.format.DateTimeFormatterBuilder$StringLiteralPrinterParser
@@ -1462,10 +1504,10 @@
java.util.ArrayDeque$DescendingIterator
java.util.ArrayDeque
java.util.ArrayList$ArrayListSpliterator
-java.util.ArrayList$Itr-IA
java.util.ArrayList$Itr
java.util.ArrayList$ListItr
java.util.ArrayList$SubList$1
+java.util.ArrayList$SubList$2
java.util.ArrayList$SubList
java.util.ArrayList
java.util.ArrayPrefixHelpers$CumulateTask
@@ -1480,14 +1522,7 @@
java.util.Arrays$ArrayList
java.util.Arrays$NaturalOrder
java.util.Arrays
-java.util.ArraysParallelSortHelpers$FJByte$Sorter
-java.util.ArraysParallelSortHelpers$FJChar$Sorter
-java.util.ArraysParallelSortHelpers$FJDouble$Sorter
-java.util.ArraysParallelSortHelpers$FJFloat$Sorter
-java.util.ArraysParallelSortHelpers$FJInt$Sorter
-java.util.ArraysParallelSortHelpers$FJLong$Sorter
java.util.ArraysParallelSortHelpers$FJObject$Sorter
-java.util.ArraysParallelSortHelpers$FJShort$Sorter
java.util.Base64$Decoder
java.util.Base64$Encoder
java.util.Base64
@@ -1638,13 +1673,13 @@
java.util.ImmutableCollections$AbstractImmutableMap
java.util.ImmutableCollections$AbstractImmutableSet
java.util.ImmutableCollections$List12
+java.util.ImmutableCollections$ListItr
java.util.ImmutableCollections$ListN
-java.util.ImmutableCollections$Map0
java.util.ImmutableCollections$Map1
+java.util.ImmutableCollections$MapN$1
+java.util.ImmutableCollections$MapN$MapNIterator
java.util.ImmutableCollections$MapN
-java.util.ImmutableCollections$Set0
-java.util.ImmutableCollections$Set1
-java.util.ImmutableCollections$Set2
+java.util.ImmutableCollections$Set12
java.util.ImmutableCollections$SetN
java.util.ImmutableCollections
java.util.InputMismatchException
@@ -1662,6 +1697,7 @@
java.util.LinkedHashMap$LinkedValues
java.util.LinkedHashMap
java.util.LinkedHashSet
+java.util.LinkedList$DescendingIterator
java.util.LinkedList$ListItr
java.util.LinkedList$Node
java.util.LinkedList
@@ -1673,6 +1709,10 @@
java.util.Locale$Cache
java.util.Locale$Category
java.util.Locale$FilteringMode
+java.util.Locale$IsoCountryCode$1
+java.util.Locale$IsoCountryCode$2
+java.util.Locale$IsoCountryCode$3
+java.util.Locale$IsoCountryCode
java.util.Locale$LanguageRange
java.util.Locale$LocaleKey
java.util.Locale$NoImagePreloadHolder
@@ -1712,10 +1752,9 @@
java.util.ResourceBundle$Control$1
java.util.ResourceBundle$Control$CandidateListCache
java.util.ResourceBundle$Control
-java.util.ResourceBundle$LoaderReference
+java.util.ResourceBundle$RBClassLoader
java.util.ResourceBundle$SingleFormatControl
java.util.ResourceBundle
-java.util.Scanner$1
java.util.Scanner
java.util.ServiceConfigurationError
java.util.ServiceLoader$1
@@ -1770,6 +1809,7 @@
java.util.TreeMap$Values
java.util.TreeMap
java.util.TreeSet
+java.util.Tripwire$$ExternalSyntheticLambda0
java.util.Tripwire
java.util.UUID$Holder
java.util.UUID
@@ -1798,6 +1838,8 @@
java.util.concurrent.Callable
java.util.concurrent.CancellationException
java.util.concurrent.CompletableFuture$AltResult
+java.util.concurrent.CompletableFuture$AsyncRun
+java.util.concurrent.CompletableFuture$AsyncSupply
java.util.concurrent.CompletableFuture$AsynchronousCompletionTask
java.util.concurrent.CompletableFuture$Completion
java.util.concurrent.CompletableFuture$Signaller
@@ -1860,6 +1902,7 @@
java.util.concurrent.ConcurrentHashMap
java.util.concurrent.ConcurrentLinkedDeque$Node
java.util.concurrent.ConcurrentLinkedDeque
+java.util.concurrent.ConcurrentLinkedQueue$$ExternalSyntheticLambda0
java.util.concurrent.ConcurrentLinkedQueue$Itr
java.util.concurrent.ConcurrentLinkedQueue$Node
java.util.concurrent.ConcurrentLinkedQueue
@@ -1898,7 +1941,6 @@
java.util.concurrent.ForkJoinPool$ManagedBlocker
java.util.concurrent.ForkJoinPool$WorkQueue
java.util.concurrent.ForkJoinPool
-java.util.concurrent.ForkJoinTask$ExceptionNode
java.util.concurrent.ForkJoinTask
java.util.concurrent.ForkJoinWorkerThread
java.util.concurrent.Future
@@ -1926,13 +1968,13 @@
java.util.concurrent.Semaphore$NonfairSync
java.util.concurrent.Semaphore$Sync
java.util.concurrent.Semaphore
+java.util.concurrent.SynchronousQueue$TransferQueue$QNode
java.util.concurrent.SynchronousQueue$TransferQueue
java.util.concurrent.SynchronousQueue$TransferStack$SNode
java.util.concurrent.SynchronousQueue$TransferStack
java.util.concurrent.SynchronousQueue$Transferer
java.util.concurrent.SynchronousQueue
java.util.concurrent.ThreadFactory
-java.util.concurrent.ThreadLocalRandom
java.util.concurrent.ThreadPoolExecutor$AbortPolicy
java.util.concurrent.ThreadPoolExecutor$DiscardPolicy
java.util.concurrent.ThreadPoolExecutor$Worker
@@ -2007,6 +2049,7 @@
java.util.function.IntUnaryOperator
java.util.function.LongBinaryOperator
java.util.function.LongConsumer
+java.util.function.LongPredicate
java.util.function.LongSupplier
java.util.function.LongUnaryOperator
java.util.function.Predicate
@@ -2030,6 +2073,7 @@
java.util.jar.JarVerifier
java.util.jar.Manifest$FastInputStream
java.util.jar.Manifest
+java.util.logging.ConsoleHandler
java.util.logging.ErrorManager
java.util.logging.FileHandler$1
java.util.logging.FileHandler$InitializationErrorManager
@@ -2149,6 +2193,8 @@
java.util.stream.IntPipeline$$ExternalSyntheticLambda8
java.util.stream.IntPipeline$4$1
java.util.stream.IntPipeline$4
+java.util.stream.IntPipeline$9$1
+java.util.stream.IntPipeline$9
java.util.stream.IntPipeline$Head
java.util.stream.IntPipeline$StatelessOp
java.util.stream.IntPipeline
@@ -2221,6 +2267,7 @@
java.util.stream.ReferencePipeline$5
java.util.stream.ReferencePipeline$6$1
java.util.stream.ReferencePipeline$6
+java.util.stream.ReferencePipeline$7$1
java.util.stream.ReferencePipeline$7
java.util.stream.ReferencePipeline$Head
java.util.stream.ReferencePipeline$StatefulOp
@@ -2265,10 +2312,12 @@
java.util.stream.Streams
java.util.stream.TerminalOp
java.util.stream.TerminalSink
+java.util.stream.Tripwire$$ExternalSyntheticLambda0
java.util.stream.Tripwire
java.util.zip.Adler32
java.util.zip.CRC32
java.util.zip.CheckedInputStream
+java.util.zip.Checksum$1
java.util.zip.Checksum
java.util.zip.DataFormatException
java.util.zip.Deflater
@@ -2301,6 +2350,7 @@
javax.crypto.Cipher$SpiAndProviderUpdater
javax.crypto.Cipher$Transform
javax.crypto.Cipher
+javax.crypto.CipherInputStream
javax.crypto.CipherOutputStream
javax.crypto.CipherSpi
javax.crypto.CryptoPermissions
@@ -2386,6 +2436,9 @@
javax.net.ssl.X509KeyManager
javax.net.ssl.X509TrustManager
javax.security.auth.Destroyable
+javax.security.auth.callback.Callback
+javax.security.auth.callback.CallbackHandler
+javax.security.auth.callback.PasswordCallback
javax.security.auth.callback.UnsupportedCallbackException
javax.security.auth.x500.X500Principal
javax.security.cert.Certificate
@@ -2396,6 +2449,7 @@
javax.xml.datatype.DatatypeConstants$Field
javax.xml.datatype.DatatypeConstants
javax.xml.datatype.Duration
+javax.xml.namespace.QName
javax.xml.parsers.DocumentBuilder
javax.xml.parsers.DocumentBuilderFactory
javax.xml.parsers.ParserConfigurationException
@@ -2410,10 +2464,13 @@
jdk.internal.math.FloatingDecimal$ExceptionalBinaryToASCIIBuffer
jdk.internal.math.FloatingDecimal$PreparedASCIIToBinaryBuffer
jdk.internal.math.FloatingDecimal
+jdk.internal.math.FormattedFloatingDecimal$1
+jdk.internal.math.FormattedFloatingDecimal$2
jdk.internal.math.FormattedFloatingDecimal$Form
jdk.internal.math.FormattedFloatingDecimal
jdk.internal.misc.JavaObjectInputStreamAccess
jdk.internal.misc.SharedSecrets
+jdk.internal.misc.TerminatingThreadLocal
jdk.internal.misc.Unsafe
jdk.internal.misc.VM
jdk.internal.reflect.Reflection
@@ -2426,7 +2483,6 @@
libcore.content.type.MimeMap
libcore.icu.CollationKeyICU
libcore.icu.DateIntervalFormat
-libcore.icu.DateUtilsBridge
libcore.icu.DecimalFormatData
libcore.icu.ICU
libcore.icu.LocaleData
@@ -2499,17 +2555,24 @@
org.apache.harmony.xml.ExpatException
org.apache.harmony.xml.ExpatParser$CurrentAttributes
org.apache.harmony.xml.ExpatParser$ExpatLocator
+org.apache.harmony.xml.ExpatParser$ParseException
org.apache.harmony.xml.ExpatParser
org.apache.harmony.xml.ExpatReader
+org.apache.harmony.xml.dom.AttrImpl
+org.apache.harmony.xml.dom.CDATASectionImpl
org.apache.harmony.xml.dom.CharacterDataImpl
+org.apache.harmony.xml.dom.CommentImpl
org.apache.harmony.xml.dom.DOMImplementationImpl
org.apache.harmony.xml.dom.DocumentImpl
+org.apache.harmony.xml.dom.DocumentTypeImpl
org.apache.harmony.xml.dom.ElementImpl
+org.apache.harmony.xml.dom.EntityReferenceImpl
org.apache.harmony.xml.dom.InnerNodeImpl
org.apache.harmony.xml.dom.LeafNodeImpl
org.apache.harmony.xml.dom.NodeImpl$1
org.apache.harmony.xml.dom.NodeImpl
org.apache.harmony.xml.dom.NodeListImpl
+org.apache.harmony.xml.dom.ProcessingInstructionImpl
org.apache.harmony.xml.dom.TextImpl
org.apache.harmony.xml.parsers.DocumentBuilderFactoryImpl
org.apache.harmony.xml.parsers.DocumentBuilderImpl
@@ -2523,15 +2586,20 @@
org.json.JSONStringer$Scope
org.json.JSONStringer
org.json.JSONTokener
+org.w3c.dom.Attr
+org.w3c.dom.CDATASection
org.w3c.dom.CharacterData
+org.w3c.dom.Comment
org.w3c.dom.DOMException
org.w3c.dom.DOMImplementation
org.w3c.dom.Document
org.w3c.dom.DocumentFragment
org.w3c.dom.DocumentType
org.w3c.dom.Element
+org.w3c.dom.EntityReference
org.w3c.dom.Node
org.w3c.dom.NodeList
+org.w3c.dom.ProcessingInstruction
org.w3c.dom.Text
org.w3c.dom.TypeInfo
org.xml.sax.AttributeList
@@ -2547,6 +2615,7 @@
org.xml.sax.SAXException
org.xml.sax.SAXNotRecognizedException
org.xml.sax.SAXNotSupportedException
+org.xml.sax.SAXParseException
org.xml.sax.XMLFilter
org.xml.sax.XMLReader
org.xml.sax.ext.DeclHandler
@@ -2555,6 +2624,7 @@
org.xml.sax.ext.LexicalHandler
org.xml.sax.helpers.AttributesImpl
org.xml.sax.helpers.DefaultHandler
+org.xml.sax.helpers.LocatorImpl
org.xml.sax.helpers.NamespaceSupport
org.xml.sax.helpers.XMLFilterImpl
org.xmlpull.v1.XmlPullParser
@@ -2575,7 +2645,6 @@
sun.misc.JavaIOFileDescriptorAccess
sun.misc.LRUCache
sun.misc.SharedSecrets
-sun.misc.Unsafe$$ExternalSyntheticBackportWithForwarding0
sun.misc.Unsafe
sun.misc.VM
sun.misc.Version
@@ -2743,6 +2812,7 @@
sun.security.util.AbstractAlgorithmConstraints$1
sun.security.util.AbstractAlgorithmConstraints
sun.security.util.AlgorithmDecomposer
+sun.security.util.AnchorCertificates$1
sun.security.util.AnchorCertificates
sun.security.util.BitArray
sun.security.util.ByteArrayLexOrder
@@ -2775,6 +2845,8 @@
sun.security.util.MemoryCache$SoftCacheEntry
sun.security.util.MemoryCache
sun.security.util.ObjectIdentifier
+sun.security.util.Resources
+sun.security.util.ResourcesMgr$1
sun.security.util.ResourcesMgr
sun.security.util.SecurityConstants
sun.security.util.SignatureFileVerifier
@@ -2850,6 +2922,7 @@
sun.util.calendar.BaseCalendar$Date
sun.util.calendar.BaseCalendar
sun.util.calendar.CalendarDate
+sun.util.calendar.CalendarSystem$GregorianHolder
sun.util.calendar.CalendarSystem
sun.util.calendar.CalendarUtils
sun.util.calendar.Era
@@ -2875,6 +2948,7 @@
sun.util.locale.ParseStatus
sun.util.locale.StringTokenIterator
sun.util.locale.UnicodeLocaleExtension
+sun.util.locale.provider.CalendarDataUtility
sun.util.logging.LoggingProxy
sun.util.logging.LoggingSupport$1
sun.util.logging.LoggingSupport$2
@@ -2892,21 +2966,26 @@
[I
[J
[Landroid.system.StructCapUserData;
+[Landroid.system.StructIfaddrs;
[Landroid.system.StructPollfd;
[Lcom.android.okhttp.CipherSuite;
[Lcom.android.okhttp.ConnectionSpec;
[Lcom.android.okhttp.HttpUrl$Builder$ParseResult;
[Lcom.android.okhttp.Protocol;
[Lcom.android.okhttp.TlsVersion;
+[Lcom.android.okhttp.okio.ByteString;
[Lcom.android.org.bouncycastle.asn1.ASN1Encodable;
+[Lcom.android.org.bouncycastle.asn1.ASN1Enumerated;
[Lcom.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
[Lcom.android.org.bouncycastle.asn1.ASN1OctetString;
+[Lcom.android.org.bouncycastle.asn1.ASN1Primitive;
[Lcom.android.org.bouncycastle.crypto.params.DHParameters;
[Lcom.android.org.bouncycastle.crypto.params.DSAParameters;
[Lcom.android.org.bouncycastle.jcajce.provider.asymmetric.x509.PEMUtil$Boundaries;
[Lcom.android.org.kxml2.io.KXmlParser$ValueContext;
[Ldalvik.system.DexPathList$Element;
[Ldalvik.system.DexPathList$NativeLibraryElement;
+[Ljava.io.Closeable;
[Ljava.io.File$PathStatus;
[Ljava.io.File;
[Ljava.io.FileDescriptor;
@@ -2916,6 +2995,7 @@
[Ljava.io.ObjectStreamClass$MemberSignature;
[Ljava.io.ObjectStreamField;
[Ljava.io.Serializable;
+[Ljava.lang.Boolean;
[Ljava.lang.Byte;
[Ljava.lang.CharSequence;
[Ljava.lang.Character$UnicodeBlock;
@@ -2924,10 +3004,12 @@
[Ljava.lang.ClassLoader;
[Ljava.lang.Comparable;
[Ljava.lang.Daemons$Daemon;
+[Ljava.lang.Double;
[Ljava.lang.Enum;
[Ljava.lang.Float;
[Ljava.lang.Integer;
[Ljava.lang.Long;
+[Ljava.lang.Number;
[Ljava.lang.Object;
[Ljava.lang.Package;
[Ljava.lang.Runnable;
@@ -2980,7 +3062,9 @@
[Ljava.security.ProtectionDomain;
[Ljava.security.Provider;
[Ljava.security.cert.CRLReason;
+[Ljava.security.cert.CertPathValidatorException$BasicReason;
[Ljava.security.cert.Certificate;
+[Ljava.security.cert.PKIXReason;
[Ljava.security.cert.PKIXRevocationChecker$Option;
[Ljava.security.cert.X509CRL;
[Ljava.security.cert.X509Certificate;
@@ -3012,13 +3096,13 @@
[Ljava.util.Comparators$NaturalOrderComparator;
[Ljava.util.Enumeration;
[Ljava.util.Formatter$Flags;
-[Ljava.util.Formatter$FormatString;
[Ljava.util.HashMap$Node;
[Ljava.util.HashMap;
[Ljava.util.Hashtable$HashtableEntry;
[Ljava.util.List;
[Ljava.util.Locale$Category;
[Ljava.util.Locale$FilteringMode;
+[Ljava.util.Locale$IsoCountryCode;
[Ljava.util.Locale;
[Ljava.util.Map$Entry;
[Ljava.util.TimerTask;
@@ -3028,7 +3112,6 @@
[Ljava.util.concurrent.ConcurrentHashMap$Node;
[Ljava.util.concurrent.ConcurrentHashMap$Segment;
[Ljava.util.concurrent.ForkJoinPool$WorkQueue;
-[Ljava.util.concurrent.ForkJoinTask$ExceptionNode;
[Ljava.util.concurrent.ForkJoinTask;
[Ljava.util.concurrent.RunnableScheduledFuture;
[Ljava.util.concurrent.TimeUnit;
@@ -3047,9 +3130,11 @@
[Ljavax.net.ssl.SSLEngineResult$HandshakeStatus;
[Ljavax.net.ssl.SSLEngineResult$Status;
[Ljavax.net.ssl.TrustManager;
+[Ljavax.security.auth.callback.Callback;
[Ljavax.security.auth.x500.X500Principal;
[Ljavax.security.cert.X509Certificate;
[Ljdk.internal.math.FDBigInteger;
+[Ljdk.internal.math.FormattedFloatingDecimal$Form;
[Llibcore.io.ClassPathURLStreamHandler;
[Llibcore.io.IoTracker$Mode;
[Llibcore.reflect.AnnotationMember$DefaultValues;
@@ -3078,6 +3163,7 @@
[Z
[[B
[[C
+[[D
[[F
[[I
[[J
diff --git a/build/build-art-module.sh b/build/build-art-module.sh
deleted file mode 100755
index c6726f1..0000000
--- a/build/build-art-module.sh
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/bin/bash -e
-
-# This script builds the APEX modules, SDKs and module exports that the ART
-# Module provides.
-
-if [ ! -e build/make/core/Makefile ]; then
- echo "$0 must be run from the top of the tree"
- exit 1
-fi
-
-skip_apex=
-skip_module_sdk=
-build_args=()
-for arg; do
- case "$arg" in
- --skip-apex) skip_apex=true ;;
- --skip-module-sdk) skip_module_sdk=true ;;
- *) build_args+=("$arg") ;;
- esac
- shift
-done
-
-if [ -z "$skip_apex" ]; then
- # Take the list of modules from MAINLINE_MODULES.
- if [ -n "${MAINLINE_MODULES}" ]; then
- read -r -a MAINLINE_MODULES <<< "${MAINLINE_MODULES}"
- else
- MAINLINE_MODULES=(
- com.android.art
- com.android.art.debug
- )
- fi
-else
- MAINLINE_MODULES=()
-fi
-
-# Take the list of products to build the modules for from
-# MAINLINE_MODULE_PRODUCTS.
-if [ -n "${MAINLINE_MODULE_PRODUCTS}" ]; then
- read -r -a MAINLINE_MODULE_PRODUCTS <<< "${MAINLINE_MODULE_PRODUCTS}"
-else
- # The default products are the same as in
- # build/soong/scripts/build-mainline-modules.sh.
- MAINLINE_MODULE_PRODUCTS=(
- art_module_arm
- art_module_arm64
- art_module_x86
- art_module_x86_64
- )
-fi
-
-MODULE_SDKS_AND_EXPORTS=()
-if [ -z "$skip_module_sdk" ]; then
- MODULE_SDKS_AND_EXPORTS=(
- art-module-sdk
- art-module-host-exports
- art-module-test-exports
- )
-fi
-
-echo_and_run() {
- echo "$*"
- "$@"
-}
-
-export OUT_DIR=${OUT_DIR:-out}
-export DIST_DIR=${DIST_DIR:-${OUT_DIR}/dist}
-
-# Use same build settings as build_unbundled_mainline_module.sh, for build
-# consistency.
-# TODO(mast): Call out to a common script for building APEXes.
-export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true
-export TARGET_BUILD_VARIANT=${TARGET_BUILD_VARIANT:-"user"}
-export TARGET_BUILD_DENSITY=alldpi
-export TARGET_BUILD_TYPE=release
-
-if [ ! -d frameworks/base ]; then
- # Configure the build system for the reduced manifest branch.
- export SOONG_ALLOW_MISSING_DEPENDENCIES=true
-fi
-
-if [ ${#MAINLINE_MODULES[*]} -gt 0 ]; then
- (
- export TARGET_BUILD_APPS="${MAINLINE_MODULES[*]}"
-
- # We require .apex files here, so ensure we get them regardless of product
- # settings.
- export OVERRIDE_TARGET_FLATTEN_APEX=false
-
- for product in ${MAINLINE_MODULE_PRODUCTS[*]}; do
- echo_and_run build/soong/soong_ui.bash --make-mode \
- TARGET_PRODUCT=${product} "${build_args[@]}" ${MAINLINE_MODULES[*]}
-
- vars="$(TARGET_PRODUCT=${product} build/soong/soong_ui.bash \
- --dumpvars-mode --vars="PRODUCT_OUT TARGET_ARCH")"
- # Assign to a variable and eval that, since bash ignores any error status
- # from the command substitution if it's directly on the eval line.
- eval $vars
-
- mkdir -p ${DIST_DIR}/${TARGET_ARCH}
- for module in ${MAINLINE_MODULES[*]}; do
- echo_and_run cp ${PRODUCT_OUT}/system/apex/${module}.apex \
- ${DIST_DIR}/${TARGET_ARCH}/
- done
- done
- )
-fi
-
-if [ ${#MODULE_SDKS_AND_EXPORTS[*]} -gt 0 ]; then
- # Create multi-arch SDKs in a different out directory. The multi-arch script
- # uses Soong in --soong-only mode which cannot use the same directory as
- # normal mode with make.
- export OUT_DIR=${OUT_DIR}/aml
-
- # Put the build system in apps building mode so we don't trip on platform
- # dependencies, but there are no actual apps to build here.
- export TARGET_BUILD_APPS=none
-
- # We use force building LLVM components flag (even though we actually don't
- # compile them) because we don't have bionic host prebuilts
- # for them.
- export FORCE_BUILD_LLVM_COMPONENTS=true
-
- echo_and_run build/soong/scripts/build-aml-prebuilts.sh \
- TARGET_PRODUCT=mainline_sdk "${build_args[@]}" ${MODULE_SDKS_AND_EXPORTS[*]}
-
- rm -rf ${DIST_DIR}/mainline-sdks
- mkdir -p ${DIST_DIR}
- echo_and_run cp -r ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}/
-fi
diff --git a/build/codegen.go b/build/codegen.go
index 96dd223..66569be 100644
--- a/build/codegen.go
+++ b/build/codegen.go
@@ -62,6 +62,8 @@
arch = &c.Codegen.Arm
case "arm64":
arch = &c.Codegen.Arm64
+ case "riscv64":
+ arch = &c.Codegen.Riscv64
case "x86":
arch = &c.Codegen.X86
case "x86_64":
@@ -205,7 +207,7 @@
type codegenProperties struct {
Codegen struct {
- Arm, Arm64, X86, X86_64 codegenArchProperties
+ Arm, Arm64, Riscv64, X86, X86_64 codegenArchProperties
}
}
diff --git a/build/makevars.go b/build/makevars.go
index 1965b5d..0606bab 100644
--- a/build/makevars.go
+++ b/build/makevars.go
@@ -34,6 +34,7 @@
"bin/llvm-addr2line",
"bin/llvm-dwarfdump",
"bin/llvm-objdump",
+ "lib/libc++.so.1",
"lib64/libc++.so.1",
}
)
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 38c37ec..effbee9 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -528,10 +528,12 @@
TEST_F(CmdlineParserTest, TestIgnoreUnrecognized) {
RuntimeParser::Builder parserBuilder;
+ // clang-format off
parserBuilder
.Define("-help")
.IntoKey(M::Help)
.IgnoreUnrecognized(true);
+ // clang-format on
parser_.reset(new RuntimeParser(parserBuilder.Build()));
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
index de0a588..c47efe1 100644
--- a/cmdline/detail/cmdline_parse_argument_detail.h
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -150,7 +150,7 @@
for (auto cname : names_) {
std::string_view name = cname;
if (using_blanks_) {
- name = name.substr(0, name.find("_"));
+ name = name.substr(0, name.find('_'));
}
auto& os = vios.Stream();
auto print_once = [&]() {
@@ -485,6 +485,7 @@
// Error case: Fail, telling the user what the allowed values were.
std::vector<std::string> allowed_values;
+ allowed_values.reserve(argument_info_.names_.size());
for (auto&& arg_name : argument_info_.names_) {
allowed_values.push_back(arg_name);
}
diff --git a/cmdline/token_range.h b/cmdline/token_range.h
index e28ead9..e917e1d 100644
--- a/cmdline/token_range.h
+++ b/cmdline/token_range.h
@@ -81,7 +81,7 @@
{}
// Non-copying constructor. Retain reference to existing list of tokens.
- TokenRange(std::shared_ptr<TokenList> token_list,
+ TokenRange(const std::shared_ptr<TokenList>& token_list,
TokenList::const_iterator it_begin,
TokenList::const_iterator it_end)
: token_list_(token_list),
@@ -98,7 +98,7 @@
TokenRange(TokenRange&&) = default;
// Non-copying constructor. Retains reference to an existing list of tokens, with offset.
- explicit TokenRange(std::shared_ptr<TokenList> token_list)
+ explicit TokenRange(const std::shared_ptr<TokenList>& token_list)
: token_list_(token_list),
begin_(token_list_->begin()),
end_(token_list_->end())
diff --git a/cmdline/unit.h b/cmdline/unit.h
index f73981f..0b5c344 100644
--- a/cmdline/unit.h
+++ b/cmdline/unit.h
@@ -21,13 +21,7 @@
// Used for arguments that simply indicate presence (e.g. "-help") without any values.
struct Unit {
- // Historical note: We specified a user-defined constructor to avoid
- // 'Conditional jump or move depends on uninitialised value(s)' errors
- // when running Valgrind.
- Unit() {}
- Unit(const Unit&) = default;
- ~Unit() {}
- bool operator==(Unit) const {
+ bool operator==(const Unit&) const {
return true;
}
};
diff --git a/compiler/Android.bp b/compiler/Android.bp
index de98fdb..cab7fbe 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -33,11 +33,8 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: [
- "compiled_method.cc",
"debug/elf_debug_writer.cc",
"dex/inline_method_analyser.cc",
- "dex/verification_results.cc",
- "driver/compiled_method_storage.cc",
"driver/compiler_options.cc",
"driver/dex_compilation_unit.cc",
"jit/jit_compiler.cc",
@@ -94,10 +91,10 @@
"optimizing/ssa_phi_elimination.cc",
"optimizing/stack_map_stream.cc",
"optimizing/superblock_cloner.cc",
+ "optimizing/write_barrier_elimination.cc",
"trampolines/trampoline_compiler.cc",
"utils/assembler.cc",
"utils/jni_macro_assembler.cc",
- "utils/swap_space.cc",
"compiler.cc",
],
@@ -176,6 +173,8 @@
],
export_include_dirs: ["."],
+ // Not using .map.txt because this is an internal API
+ version_script: "libart-compiler.map",
}
cc_defaults {
@@ -228,7 +227,7 @@
"libprofile",
"libdexfile",
],
- whole_static_libs: ["libelffile"],
+ static_libs: ["libelffile"],
runtime_libs: [
// `art::HGraphVisualizerDisassembler::HGraphVisualizerDisassembler` may dynamically load
// `libart-disassembler.so`.
@@ -296,7 +295,7 @@
"libprofiled",
"libdexfiled",
],
- whole_static_libs: ["libelffiled"],
+ static_libs: ["libelffiled"],
runtime_libs: [
// `art::HGraphVisualizerDisassembler::HGraphVisualizerDisassembler` may dynamically load
// `libartd-disassembler.so`.
@@ -369,6 +368,7 @@
data: [
":art-gtest-jars-ExceptionHandle",
":art-gtest-jars-Interfaces",
+ ":art-gtest-jars-Main",
":art-gtest-jars-MyClassNatives",
],
tidy_timeout_srcs: [
@@ -381,9 +381,9 @@
"optimizing/ssa_test.cc",
],
srcs: [
+ "compiler_reflection_test.cc",
"debug/dwarf/dwarf_test.cc",
"debug/src_map_elem_test.cc",
- "driver/compiled_method_storage_test.cc",
"exception_test.cc",
"jni/jni_compiler_test.cc",
"linker/linker_patch_test.cc",
@@ -419,7 +419,6 @@
"optimizing/suspend_check_test.cc",
"utils/atomic_dex_ref_map_test.cc",
"utils/dedupe_set_test.cc",
- "utils/swap_space_test.cc",
"jni/jni_cfi_test.cc",
"optimizing/codegen_test.cc",
@@ -465,8 +464,8 @@
],
shared_libs: [
- "libbacktrace",
"libnativeloader",
+ "libunwindstack",
],
target: {
@@ -488,10 +487,12 @@
],
shared_libs: [
"libprofiled",
- "libartd-compiler",
"libartd-simulator-container",
+ "liblzma",
],
static_libs: [
+ "libartd-compiler",
+ "libelffiled",
"libvixld",
],
}
@@ -506,7 +507,8 @@
data: [":generate-boot-image"],
shared_libs: [
"libprofile",
- "libart-compiler",
+ "liblzma",
+ "libartpalette",
],
static_libs: [
// For now, link `libart-simulator-container` statically for simplicity,
@@ -515,6 +517,8 @@
// TODO(b/192070541): Consider linking `libart-simulator-container`
// dynamically.
"libart-simulator-container",
+ "libart-compiler",
+ "libelffile",
"libvixl",
],
test_config: "art_standalone_compiler_tests.xml",
@@ -548,9 +552,11 @@
},
},
shared_libs: [
- "libartd-compiler",
+ "liblzma",
],
static_libs: [
+ "libartd-compiler",
+ "libelffiled",
"libvixld",
],
}
diff --git a/compiler/art_standalone_compiler_tests.xml b/compiler/art_standalone_compiler_tests.xml
index f723971..394ac8d 100644
--- a/compiler/art_standalone_compiler_tests.xml
+++ b/compiler/art_standalone_compiler_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_compiler_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_compiler_tests->/data/local/tmp/art_standalone_compiler_tests/art_standalone_compiler_tests" />
@@ -24,6 +26,7 @@
<option name="cleanup" value="true" />
<option name="push" value="art-gtest-jars-ExceptionHandle.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-ExceptionHandle.jar" />
<option name="push" value="art-gtest-jars-Interfaces.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-Interfaces.jar" />
+ <option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-Main.jar" />
<option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-MyClassNatives.jar" />
</target_preparer>
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index 9755ef1..e65bee8 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -23,6 +23,7 @@
#include "arch/instruction_set.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "debug/dwarf/dwarf_test.h"
#include "disassembler.h"
#include "dwarf/dwarf_constants.h"
@@ -30,7 +31,7 @@
#include "gtest/gtest.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
class CFITest : public dwarf::DwarfTest {
public:
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index bbb2016..442b96e 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -28,10 +28,8 @@
#include "base/memfd.h"
#include "base/utils.h"
#include "class_linker.h"
-#include "compiled_method-inl.h"
#include "dex/descriptors_names.h"
-#include "dex/verification_results.h"
-#include "driver/compiled_method_storage.h"
+#include "driver/compiled_code_storage.h"
#include "driver/compiler_options.h"
#include "jni/java_vm_ext.h"
#include "interpreter/interpreter.h"
@@ -44,7 +42,7 @@
#include "thread-current-inl.h"
#include "utils/atomic_dex_ref_map-inl.h"
-namespace art {
+namespace art HIDDEN {
class CommonCompilerTestImpl::CodeAndMetadata {
public:
@@ -58,10 +56,10 @@
const uint32_t vmap_table_offset = vmap_table.empty() ? 0u
: sizeof(OatQuickMethodHeader) + vmap_table.size();
OatQuickMethodHeader method_header(vmap_table_offset);
- const size_t code_alignment = GetInstructionSetAlignment(instruction_set);
+ const size_t code_alignment = GetInstructionSetCodeAlignment(instruction_set);
DCHECK_ALIGNED_PARAM(kPageSize, code_alignment);
- code_offset_ = RoundUp(vmap_table.size() + sizeof(method_header), code_alignment);
- const uint32_t capacity = RoundUp(code_offset_ + code_size, kPageSize);
+ const uint32_t code_offset = RoundUp(vmap_table.size() + sizeof(method_header), code_alignment);
+ const uint32_t capacity = RoundUp(code_offset + code_size, kPageSize);
// Create a memfd handle with sufficient capacity.
android::base::unique_fd mem_fd(art::memfd_create_compat("test code", /*flags=*/ 0));
@@ -82,12 +80,12 @@
CHECK(rw_map_.IsValid()) << error_msg;
// Store data.
- uint8_t* code_addr = rw_map_.Begin() + code_offset_;
+ uint8_t* code_addr = rw_map_.Begin() + code_offset;
CHECK_ALIGNED_PARAM(code_addr, code_alignment);
- CHECK_LE(vmap_table_offset, code_offset_);
+ CHECK_LE(vmap_table_offset, code_offset);
memcpy(code_addr - vmap_table_offset, vmap_table.data(), vmap_table.size());
static_assert(std::is_trivially_copyable<OatQuickMethodHeader>::value, "Cannot use memcpy");
- CHECK_LE(sizeof(method_header), code_offset_);
+ CHECK_LE(sizeof(method_header), code_offset);
memcpy(code_addr - sizeof(method_header), &method_header, sizeof(method_header));
CHECK_LE(code_size, static_cast<size_t>(rw_map_.End() - code_addr));
memcpy(code_addr, code.data(), code_size);
@@ -108,22 +106,84 @@
/*filename=*/ "test code",
&error_msg);
CHECK(rx_map_.IsValid()) << error_msg;
+
+ DCHECK_LT(code_offset, rx_map_.Size());
+ size_t adjustment = GetInstructionSetEntryPointAdjustment(instruction_set);
+ entry_point_ = rx_map_.Begin() + code_offset + adjustment;
}
- const void* GetCodePointer() const {
+ const void* GetEntryPoint() const {
DCHECK(rx_map_.IsValid());
- DCHECK_LE(code_offset_, rx_map_.Size());
- return rx_map_.Begin() + code_offset_;
+ return entry_point_;
}
private:
MemMap rw_map_;
MemMap rx_map_;
- uint32_t code_offset_;
+ const void* entry_point_;
DISALLOW_COPY_AND_ASSIGN(CodeAndMetadata);
};
+class CommonCompilerTestImpl::OneCompiledMethodStorage final : public CompiledCodeStorage {
+ public:
+ OneCompiledMethodStorage() {}
+ ~OneCompiledMethodStorage() {}
+
+ CompiledMethod* CreateCompiledMethod(InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi ATTRIBUTE_UNUSED,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic ATTRIBUTE_UNUSED) override {
+ // Supports only one method at a time.
+ CHECK_EQ(instruction_set_, InstructionSet::kNone);
+ CHECK_NE(instruction_set, InstructionSet::kNone);
+ instruction_set_ = instruction_set;
+ CHECK(code_.empty());
+ CHECK(!code.empty());
+ code_.assign(code.begin(), code.end());
+ CHECK(stack_map_.empty());
+ CHECK(!stack_map.empty());
+ stack_map_.assign(stack_map.begin(), stack_map.end());
+ CHECK(patches.empty()) << "Linker patches are unsupported for compiler gtests.";
+ return reinterpret_cast<CompiledMethod*>(this);
+ }
+
+ ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED,
+ /*out*/ std::string* debug_name ATTRIBUTE_UNUSED) override {
+ LOG(FATAL) << "Unsupported.";
+ UNREACHABLE();
+ }
+
+ void SetThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED,
+ ArrayRef<const uint8_t> code ATTRIBUTE_UNUSED,
+ const std::string& debug_name ATTRIBUTE_UNUSED) override {
+ LOG(FATAL) << "Unsupported.";
+ UNREACHABLE();
+ }
+
+ InstructionSet GetInstructionSet() const {
+ CHECK_NE(instruction_set_, InstructionSet::kNone);
+ return instruction_set_;
+ }
+
+ ArrayRef<const uint8_t> GetCode() const {
+ CHECK(!code_.empty());
+ return ArrayRef<const uint8_t>(code_);
+ }
+
+ ArrayRef<const uint8_t> GetStackMap() const {
+ CHECK(!stack_map_.empty());
+ return ArrayRef<const uint8_t>(stack_map_);
+ }
+
+ private:
+ InstructionSet instruction_set_ = InstructionSet::kNone;
+ std::vector<uint8_t> code_;
+ std::vector<uint8_t> stack_map_;
+};
+
std::unique_ptr<CompilerOptions> CommonCompilerTestImpl::CreateCompilerOptions(
InstructionSet instruction_set, const std::string& variant) {
std::unique_ptr<CompilerOptions> compiler_options = std::make_unique<CompilerOptions>();
@@ -143,24 +203,7 @@
InstructionSet instruction_set) {
CHECK_NE(code.size(), 0u);
code_and_metadata_.emplace_back(code, vmap_table, instruction_set);
- return code_and_metadata_.back().GetCodePointer();
-}
-
-void CommonCompilerTestImpl::MakeExecutable(ArtMethod* method,
- const CompiledMethod* compiled_method) {
- CHECK(method != nullptr);
- const void* method_code = nullptr;
- // If the code size is 0 it means the method was skipped due to profile guided compilation.
- if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) {
- const void* code_ptr = MakeExecutable(compiled_method->GetQuickCode(),
- compiled_method->GetVmapTable(),
- compiled_method->GetInstructionSet());
- method_code =
- CompiledMethod::CodePointer(code_ptr, compiled_method->GetInstructionSet());
- LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
- }
- Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(
- method, /*aot_code=*/ method_code);
+ return code_and_metadata_.back().GetEntryPoint();
}
void CommonCompilerTestImpl::SetUp() {
@@ -207,7 +250,6 @@
void CommonCompilerTestImpl::SetUpRuntimeOptionsImpl() {
compiler_options_.reset(new CompilerOptions);
- verification_results_.reset(new VerificationResults());
ApplyInstructionSet();
}
@@ -221,7 +263,6 @@
void CommonCompilerTestImpl::TearDown() {
code_and_metadata_.clear();
- verification_results_.reset();
compiler_options_.reset();
}
@@ -229,7 +270,7 @@
CHECK(method != nullptr);
TimingLogger timings("CommonCompilerTestImpl::CompileMethod", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
- CompiledMethodStorage storage(/*swap_fd=*/ -1);
+ OneCompiledMethodStorage storage;
CompiledMethod* compiled_method = nullptr;
{
DCHECK(!Runtime::Current()->IsStarted());
@@ -241,7 +282,6 @@
Handle<mirror::DexCache> dex_cache =
hs.NewHandle(GetClassLinker()->FindDexCache(self, dex_file));
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(method->GetClassLoader());
- compiler_options_->verification_results_ = verification_results_.get();
if (method->IsNative()) {
compiled_method = compiler->JniCompile(method->GetAccessFlags(),
method->GetDexMethodIndex(),
@@ -257,48 +297,17 @@
dex_file,
dex_cache);
}
- compiler_options_->verification_results_ = nullptr;
+ CHECK(compiled_method != nullptr) << "Failed to compile " << method->PrettyMethod();
+ CHECK_EQ(reinterpret_cast<OneCompiledMethodStorage*>(compiled_method), &storage);
}
- CHECK(method != nullptr);
{
TimingLogger::ScopedTiming t2("MakeExecutable", &timings);
- MakeExecutable(method, compiled_method);
+ const void* method_code = MakeExecutable(storage.GetCode(),
+ storage.GetStackMap(),
+ storage.GetInstructionSet());
+ LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
+ GetRuntime()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ method_code);
}
- CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&storage, compiled_method);
-}
-
-void CommonCompilerTestImpl::CompileDirectMethod(Handle<mirror::ClassLoader> class_loader,
- const char* class_name,
- const char* method_name,
- const char* signature) {
- std::string class_descriptor(DotToDescriptor(class_name));
- Thread* self = Thread::Current();
- ClassLinker* class_linker = GetClassLinker();
- ObjPtr<mirror::Class> klass =
- class_linker->FindClass(self, class_descriptor.c_str(), class_loader);
- CHECK(klass != nullptr) << "Class not found " << class_name;
- auto pointer_size = class_linker->GetImagePointerSize();
- ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
- CHECK(method != nullptr && method->IsDirect()) << "Direct method not found: "
- << class_name << "." << method_name << signature;
- CompileMethod(method);
-}
-
-void CommonCompilerTestImpl::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader,
- const char* class_name,
- const char* method_name,
- const char* signature) {
- std::string class_descriptor(DotToDescriptor(class_name));
- Thread* self = Thread::Current();
- ClassLinker* class_linker = GetClassLinker();
- ObjPtr<mirror::Class> klass =
- class_linker->FindClass(self, class_descriptor.c_str(), class_loader);
- CHECK(klass != nullptr) << "Class not found " << class_name;
- auto pointer_size = class_linker->GetImagePointerSize();
- ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
- CHECK(method != nullptr && !method->IsDirect()) << "Virtual method not found: "
- << class_name << "." << method_name << signature;
- CompileMethod(method);
}
void CommonCompilerTestImpl::ClearBootImageOption() {
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 89cc1fa..f3cd132 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -24,25 +24,25 @@
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
+#include "base/macros.h"
#include "common_runtime_test.h"
#include "compiler.h"
#include "oat_file.h"
-namespace art {
+namespace art HIDDEN {
namespace mirror {
class ClassLoader;
} // namespace mirror
-class CompiledMethod;
class CompilerOptions;
class CumulativeLogger;
class DexFile;
class TimingLogger;
-class VerificationResults;
template<class T> class Handle;
-class CommonCompilerTestImpl {
+// Export all symbols in `CommonCompilerTestImpl` for dex2oat tests.
+class EXPORT CommonCompilerTestImpl {
public:
static std::unique_ptr<CompilerOptions> CreateCompilerOptions(InstructionSet instruction_set,
const std::string& variant);
@@ -55,9 +55,6 @@
ArrayRef<const uint8_t> vmap_table,
InstructionSet instruction_set);
- void MakeExecutable(ArtMethod* method, const CompiledMethod* compiled_method)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
protected:
void SetUp();
@@ -74,14 +71,6 @@
void CompileMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
- void CompileDirectMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
- const char* method_name, const char* signature)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- void CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
- const char* method_name, const char* signature)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
void ApplyInstructionSet();
void OverrideInstructionSetFeatures(InstructionSet instruction_set, const std::string& variant);
@@ -96,7 +85,6 @@
= InstructionSetFeatures::FromCppDefines();
std::unique_ptr<CompilerOptions> compiler_options_;
- std::unique_ptr<VerificationResults> verification_results_;
protected:
virtual ClassLinker* GetClassLinker() = 0;
@@ -104,6 +92,8 @@
private:
class CodeAndMetadata;
+ class OneCompiledMethodStorage;
+
std::vector<CodeAndMetadata> code_and_metadata_;
};
diff --git a/compiler/compiled_method-inl.h b/compiler/compiled_method-inl.h
deleted file mode 100644
index e60b30f..0000000
--- a/compiler/compiled_method-inl.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_COMPILED_METHOD_INL_H_
-#define ART_COMPILER_COMPILED_METHOD_INL_H_
-
-#include "compiled_method.h"
-
-#include "base/array_ref.h"
-#include "base/length_prefixed_array.h"
-#include "linker/linker_patch.h"
-
-namespace art {
-
-inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const {
- return GetArray(quick_code_);
-}
-
-template <typename T>
-inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) {
- if (array == nullptr) {
- return ArrayRef<const T>();
- }
- DCHECK_NE(array->size(), 0u);
- return ArrayRef<const T>(&array->At(0), array->size());
-}
-
-inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const {
- return GetArray(vmap_table_);
-}
-
-inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const {
- return GetArray(cfi_info_);
-}
-
-inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const {
- return GetArray(patches_);
-}
-
-} // namespace art
-
-#endif // ART_COMPILER_COMPILED_METHOD_INL_H_
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
deleted file mode 100644
index 03b87ef..0000000
--- a/compiler/compiled_method.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "compiled_method.h"
-
-#include "driver/compiled_method_storage.h"
-#include "utils/swap_space.h"
-
-namespace art {
-
-CompiledCode::CompiledCode(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code)
- : storage_(storage),
- quick_code_(storage->DeduplicateCode(quick_code)),
- packed_fields_(InstructionSetField::Encode(instruction_set)) {
-}
-
-CompiledCode::~CompiledCode() {
- GetStorage()->ReleaseCode(quick_code_);
-}
-
-bool CompiledCode::operator==(const CompiledCode& rhs) const {
- if (quick_code_ != nullptr) {
- if (rhs.quick_code_ == nullptr) {
- return false;
- } else if (quick_code_->size() != rhs.quick_code_->size()) {
- return false;
- } else {
- return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin());
- }
- }
- return (rhs.quick_code_ == nullptr);
-}
-
-size_t CompiledCode::AlignCode(size_t offset) const {
- return AlignCode(offset, GetInstructionSet());
-}
-
-size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) {
- return RoundUp(offset, GetInstructionSetAlignment(instruction_set));
-}
-
-size_t CompiledCode::CodeDelta() const {
- return CodeDelta(GetInstructionSet());
-}
-
-size_t CompiledCode::CodeDelta(InstructionSet instruction_set) {
- switch (instruction_set) {
- case InstructionSet::kArm:
- case InstructionSet::kArm64:
- case InstructionSet::kX86:
- case InstructionSet::kX86_64:
- return 0;
- case InstructionSet::kThumb2: {
- // +1 to set the low-order bit so a BLX will switch to Thumb mode
- return 1;
- }
- default:
- LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
- UNREACHABLE();
- }
-}
-
-const void* CompiledCode::CodePointer(const void* code_pointer, InstructionSet instruction_set) {
- switch (instruction_set) {
- case InstructionSet::kArm:
- case InstructionSet::kArm64:
- case InstructionSet::kX86:
- case InstructionSet::kX86_64:
- return code_pointer;
- case InstructionSet::kThumb2: {
- uintptr_t address = reinterpret_cast<uintptr_t>(code_pointer);
- // Set the low-order bit so a BLX will switch to Thumb mode
- address |= 0x1;
- return reinterpret_cast<const void*>(address);
- }
- default:
- LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
- UNREACHABLE();
- }
-}
-
-CompiledMethod::CompiledMethod(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches)
- : CompiledCode(storage, instruction_set, quick_code),
- vmap_table_(storage->DeduplicateVMapTable(vmap_table)),
- cfi_info_(storage->DeduplicateCFIInfo(cfi_info)),
- patches_(storage->DeduplicateLinkerPatches(patches)) {
-}
-
-CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
- CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches) {
- SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
- CompiledMethod* ret = alloc.allocate(1);
- alloc.construct(ret,
- storage,
- instruction_set,
- quick_code,
- vmap_table,
- cfi_info, patches);
- return ret;
-}
-
-void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage,
- CompiledMethod* m) {
- SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
- alloc.destroy(m);
- alloc.deallocate(m, 1);
-}
-
-CompiledMethod::~CompiledMethod() {
- CompiledMethodStorage* storage = GetStorage();
- storage->ReleaseLinkerPatches(patches_);
- storage->ReleaseCFIInfo(cfi_info_);
- storage->ReleaseVMapTable(vmap_table_);
-}
-
-} // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
deleted file mode 100644
index e92777f..0000000
--- a/compiler/compiled_method.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_COMPILED_METHOD_H_
-#define ART_COMPILER_COMPILED_METHOD_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "arch/instruction_set.h"
-#include "base/bit_field.h"
-#include "base/bit_utils.h"
-
-namespace art {
-
-template <typename T> class ArrayRef;
-class CompiledMethodStorage;
-template<typename T> class LengthPrefixedArray;
-
-namespace linker {
-class LinkerPatch;
-} // namespace linker
-
-class CompiledCode {
- public:
- // For Quick to supply an code blob
- CompiledCode(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code);
-
- virtual ~CompiledCode();
-
- InstructionSet GetInstructionSet() const {
- return GetPackedField<InstructionSetField>();
- }
-
- ArrayRef<const uint8_t> GetQuickCode() const;
-
- bool operator==(const CompiledCode& rhs) const;
-
- // To align an offset from a page-aligned value to make it suitable
- // for code storage. For example on ARM, to ensure that PC relative
- // valu computations work out as expected.
- size_t AlignCode(size_t offset) const;
- static size_t AlignCode(size_t offset, InstructionSet instruction_set);
-
- // returns the difference between the code address and a usable PC.
- // mainly to cope with kThumb2 where the lower bit must be set.
- size_t CodeDelta() const;
- static size_t CodeDelta(InstructionSet instruction_set);
-
- // Returns a pointer suitable for invoking the code at the argument
- // code_pointer address. Mainly to cope with kThumb2 where the
- // lower bit must be set to indicate Thumb mode.
- static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set);
-
- protected:
- static constexpr size_t kInstructionSetFieldSize =
- MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast));
- static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize;
- static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
-
- template <typename T>
- static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array);
-
- CompiledMethodStorage* GetStorage() {
- return storage_;
- }
-
- template <typename BitFieldType>
- typename BitFieldType::value_type GetPackedField() const {
- return BitFieldType::Decode(packed_fields_);
- }
-
- template <typename BitFieldType>
- void SetPackedField(typename BitFieldType::value_type value) {
- DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
- packed_fields_ = BitFieldType::Update(value, packed_fields_);
- }
-
- private:
- using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>;
-
- CompiledMethodStorage* const storage_;
-
- // Used to store the compiled code.
- const LengthPrefixedArray<uint8_t>* const quick_code_;
-
- uint32_t packed_fields_;
-};
-
-class CompiledMethod final : public CompiledCode {
- public:
- // Constructs a CompiledMethod.
- // Note: Consider using the static allocation methods below that will allocate the CompiledMethod
- // in the swap space.
- CompiledMethod(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches);
-
- virtual ~CompiledMethod();
-
- static CompiledMethod* SwapAllocCompiledMethod(
- CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches);
-
- static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m);
-
- bool IsIntrinsic() const {
- return GetPackedField<IsIntrinsicField>();
- }
-
- // Marks the compiled method as being generated using an intrinsic codegen.
- // Such methods have no relationships to their code items.
- // This affects debug information generated at link time.
- void MarkAsIntrinsic() {
- DCHECK(!IsIntrinsic());
- SetPackedField<IsIntrinsicField>(/* value= */ true);
- }
-
- ArrayRef<const uint8_t> GetVmapTable() const;
-
- ArrayRef<const uint8_t> GetCFIInfo() const;
-
- ArrayRef<const linker::LinkerPatch> GetPatches() const;
-
- private:
- static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits;
- static constexpr size_t kIsIntrinsicSize = 1u;
- static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize;
- static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits,
- "Too many packed fields.");
-
- using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>;
-
- // For quick code, holds code infos which contain stack maps, inline information, and etc.
- const LengthPrefixedArray<uint8_t>* const vmap_table_;
- // For quick code, a FDE entry for the debug_frame section.
- const LengthPrefixedArray<uint8_t>* const cfi_info_;
- // For quick code, linker patches needed by the method.
- const LengthPrefixedArray<linker::LinkerPatch>* const patches_;
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_COMPILED_METHOD_H_
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index 98d7339..e2587c1 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -25,10 +25,10 @@
#include "oat.h"
#include "optimizing/optimizing_compiler.h"
-namespace art {
+namespace art HIDDEN {
Compiler* Compiler::Create(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage,
+ CompiledCodeStorage* storage,
Compiler::Kind kind) {
// Check that oat version when runtime was compiled matches the oat version of the compiler.
constexpr std::array<uint8_t, 4> compiler_oat_version = OatHeader::kOatVersion;
diff --git a/compiler/compiler.h b/compiler/compiler.h
index afa0dba..ce785bb 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_COMPILER_H_
#define ART_COMPILER_COMPILER_H_
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/os.h"
#include "compilation_kind.h"
#include "dex/invoke_type.h"
-namespace art {
+namespace art HIDDEN {
namespace dex {
struct CodeItem;
@@ -38,8 +39,8 @@
} // namespace mirror
class ArtMethod;
+class CompiledCodeStorage;
class CompiledMethod;
-class CompiledMethodStorage;
class CompilerOptions;
class DexFile;
template<class T> class Handle;
@@ -52,9 +53,9 @@
kOptimizing
};
- static Compiler* Create(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage,
- Kind kind);
+ EXPORT static Compiler* Create(const CompilerOptions& compiler_options,
+ CompiledCodeStorage* storage,
+ Kind kind);
virtual bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file) const = 0;
@@ -99,7 +100,7 @@
protected:
Compiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage,
+ CompiledCodeStorage* storage,
uint64_t warning) :
compiler_options_(compiler_options),
storage_(storage),
@@ -110,13 +111,13 @@
return compiler_options_;
}
- CompiledMethodStorage* GetCompiledMethodStorage() const {
+ CompiledCodeStorage* GetCompiledCodeStorage() const {
return storage_;
}
private:
const CompilerOptions& compiler_options_;
- CompiledMethodStorage* const storage_;
+ CompiledCodeStorage* const storage_;
const uint64_t maximum_compilation_time_before_warning_;
DISALLOW_COPY_AND_ASSIGN(Compiler);
diff --git a/compiler/compiler_reflection_test.cc b/compiler/compiler_reflection_test.cc
new file mode 100644
index 0000000..f3c07db
--- /dev/null
+++ b/compiler/compiler_reflection_test.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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 "reflection.h"
+
+#include "base/macros.h"
+#include "class_linker.h"
+#include "common_compiler_test.h"
+#include "handle_scope-inl.h"
+#include "jni/jni_internal.h"
+#include "mirror/class.h"
+#include "mirror/class_loader.h"
+
+namespace art HIDDEN {
+
+class CompilerReflectionTest : public CommonCompilerTest {};
+
+TEST_F(CompilerReflectionTest, StaticMainMethod) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadDex("Main");
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+
+ ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
+ ASSERT_TRUE(klass != nullptr);
+
+ ArtMethod* method = klass->FindClassMethod("main",
+ "([Ljava/lang/String;)V",
+ kRuntimePointerSize);
+ ASSERT_TRUE(method != nullptr);
+ ASSERT_TRUE(method->IsStatic());
+
+ CompileMethod(method);
+
+ // Start runtime.
+ bool started = runtime_->Start();
+ CHECK(started);
+ soa.Self()->TransitionFromSuspendedToRunnable();
+
+ jvalue args[1];
+ args[0].l = nullptr;
+ InvokeWithJValues(soa, nullptr, jni::EncodeArtMethod(method), args);
+}
+
+} // namespace art
diff --git a/compiler/debug/debug_info.h b/compiler/debug/debug_info.h
index 04c6991..4027f11 100644
--- a/compiler/debug/debug_info.h
+++ b/compiler/debug/debug_info.h
@@ -20,9 +20,10 @@
#include <map>
#include "base/array_ref.h"
+#include "base/macros.h"
#include "method_debug_info.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
namespace debug {
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index 8897e45..14c92b2 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -23,7 +23,7 @@
#include "dwarf/headers.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace dwarf {
// Run the tests only on host since we need objdump.
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index bad986a..1a0a798 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -26,6 +26,7 @@
#include <set>
#include <string>
+#include "base/macros.h"
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "common_compiler_test.h"
@@ -33,7 +34,7 @@
#include "gtest/gtest.h"
#include "stream/file_output_stream.h"
-namespace art {
+namespace art HIDDEN {
namespace dwarf {
#define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__)
diff --git a/compiler/debug/elf_compilation_unit.h b/compiler/debug/elf_compilation_unit.h
index b1d89eb..1d7523c 100644
--- a/compiler/debug/elf_compilation_unit.h
+++ b/compiler/debug/elf_compilation_unit.h
@@ -19,9 +19,10 @@
#include <vector>
+#include "base/macros.h"
#include "debug/method_debug_info.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
struct ElfCompilationUnit {
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
index 094e887..6b72262 100644
--- a/compiler/debug/elf_debug_frame_writer.h
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -20,13 +20,14 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/macros.h"
#include "debug/method_debug_info.h"
#include "dwarf/debug_frame_opcode_writer.h"
#include "dwarf/dwarf_constants.h"
#include "dwarf/headers.h"
#include "elf/elf_builder.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
static constexpr bool kWriteDebugFrameHdr = false;
@@ -88,6 +89,10 @@
WriteCIE(is64bit, return_reg, opcodes, buffer);
return;
}
+ case InstructionSet::kRiscv64: {
+ UNIMPLEMENTED(FATAL);
+ return;
+ }
case InstructionSet::kX86: {
// FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296
constexpr bool generate_opcodes_for_x86_fp = false;
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 04981aa..9915a24 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -22,6 +22,7 @@
#include <vector>
#include "art_field-inl.h"
+#include "base/macros.h"
#include "debug/elf_compilation_unit.h"
#include "debug/elf_debug_loc_writer.h"
#include "debug/method_debug_info.h"
@@ -39,7 +40,7 @@
#include "oat_file.h"
#include "obj_ptr-inl.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 8d62747..4896bc1 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -20,6 +20,7 @@
#include <unordered_set>
#include <vector>
+#include "base/macros.h"
#include "debug/elf_compilation_unit.h"
#include "debug/src_map_elem.h"
#include "dex/dex_file-inl.h"
@@ -29,7 +30,7 @@
#include "oat_file.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
using PositionInfos = std::vector<DexFile::PositionInfo>;
@@ -73,6 +74,7 @@
code_factor_bits_ = 2; // 32-bit instructions
break;
case InstructionSet::kNone:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
break;
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index 37ab948..8cf476e 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -21,13 +21,13 @@
#include <map>
#include "arch/instruction_set.h"
-#include "compiled_method.h"
+#include "base/macros.h"
#include "debug/method_debug_info.h"
#include "dwarf/debug_info_entry_writer.h"
#include "dwarf/register.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
using Reg = dwarf::Reg;
@@ -38,6 +38,8 @@
return Reg::ArmCore(machine_reg);
case InstructionSet::kArm64:
return Reg::Arm64Core(machine_reg);
+ case InstructionSet::kRiscv64:
+ return Reg::Riscv64Core(machine_reg);
case InstructionSet::kX86:
return Reg::X86Core(machine_reg);
case InstructionSet::kX86_64:
@@ -55,6 +57,8 @@
return Reg::ArmFp(machine_reg);
case InstructionSet::kArm64:
return Reg::Arm64Fp(machine_reg);
+ case InstructionSet::kRiscv64:
+ return Reg::Riscv64Fp(machine_reg);
case InstructionSet::kX86:
return Reg::X86Fp(machine_reg);
case InstructionSet::kX86_64:
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 765a81d..8f64d73 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -38,7 +38,7 @@
#include "oat.h"
#include "stream/vector_output_stream.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
using ElfRuntimeTypes = std::conditional<sizeof(void*) == 4, ElfTypes32, ElfTypes64>::type;
@@ -208,7 +208,8 @@
using Reader = ElfDebugReader<ElfTypes>;
Reader reader(buffer);
reader.VisitFunctionSymbols([&](Elf_Sym sym, const char*) {
- DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
+ DCHECK_EQ(sym.st_value,
+ method_info.code_address + GetInstructionSetEntryPointAdjustment(isa));
DCHECK_EQ(sym.st_size, method_info.code_size);
num_syms++;
});
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 1ce3c6f..72b028c 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -27,7 +27,7 @@
#include "dwarf/dwarf_constants.h"
#include "elf/elf_builder.h"
-namespace art {
+namespace art HIDDEN {
class OatHeader;
struct JITCodeEntry;
namespace mirror {
@@ -37,11 +37,11 @@
struct MethodDebugInfo;
template <typename ElfTypes>
-void WriteDebugInfo(
+EXPORT void WriteDebugInfo(
ElfBuilder<ElfTypes>* builder,
const DebugInfo& debug_info);
-std::vector<uint8_t> MakeMiniDebugInfo(
+EXPORT std::vector<uint8_t> MakeMiniDebugInfo(
InstructionSet isa,
const InstructionSetFeatures* features,
uint64_t text_section_address,
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index 410f704..fcd6696 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -21,6 +21,7 @@
#include <unordered_set>
#include <unordered_map>
+#include "base/macros.h"
#include "base/utils.h"
#include "debug/debug_info.h"
#include "debug/method_debug_info.h"
@@ -29,7 +30,7 @@
#include "dex/dex_file-inl.h"
#include "elf/elf_builder.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
// The ARM specification defines three special mapping symbols
@@ -153,7 +154,7 @@
uint64_t address = info.code_address;
address += info.is_code_address_text_relative ? text->GetAddress() : 0;
// Add in code delta, e.g., thumb bit 0 for Thumb2 code.
- address += CompiledMethod::CodeDelta(info.isa);
+ address += GetInstructionSetEntryPointAdjustment(info.isa);
symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC);
}
// Add symbols for dex files.
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index 152db6e..b83c6e2 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -21,9 +21,10 @@
#include "arch/instruction_set.h"
#include "base/array_ref.h"
+#include "base/macros.h"
#include "dex/dex_file.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
struct MethodDebugInfo {
diff --git a/compiler/debug/src_map_elem.h b/compiler/debug/src_map_elem.h
index 5286b8c..646a1f0 100644
--- a/compiler/debug/src_map_elem.h
+++ b/compiler/debug/src_map_elem.h
@@ -19,7 +19,9 @@
#include <stdint.h>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class SrcMapElem {
public:
diff --git a/compiler/debug/src_map_elem_test.cc b/compiler/debug/src_map_elem_test.cc
index ceaa53f..bdbafd5 100644
--- a/compiler/debug/src_map_elem_test.cc
+++ b/compiler/debug/src_map_elem_test.cc
@@ -20,7 +20,7 @@
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
TEST(SrcMapElem, Operators) {
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index 3201965..381db3d 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -33,7 +33,7 @@
* only to allow the debugger to check whether a method has been inlined.
*/
-namespace art {
+namespace art HIDDEN {
namespace { // anonymous namespace
diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h
index e1d652a..99d07c6 100644
--- a/compiler/dex/inline_method_analyser.h
+++ b/compiler/dex/inline_method_analyser.h
@@ -28,7 +28,7 @@
* only to allow the debugger to check whether a method has been inlined.
*/
-namespace art {
+namespace art HIDDEN {
class CodeItemDataAccessor;
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
deleted file mode 100644
index 7c5595e..0000000
--- a/compiler/dex/verification_results.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
-#define ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
-
-#include <set>
-
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "dex/class_reference.h"
-#include "dex/method_reference.h"
-
-namespace art {
-
-namespace verifier {
-class VerifierDepsTest;
-} // namespace verifier
-
-// Used by CompilerCallbacks to track verification information from the Runtime.
-class VerificationResults {
- public:
- VerificationResults();
- ~VerificationResults();
-
- void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
- bool IsClassRejected(ClassReference ref) const REQUIRES(!rejected_classes_lock_);
-
- void AddUncompilableClass(ClassReference ref) REQUIRES(!uncompilable_methods_lock_);
- void AddUncompilableMethod(MethodReference ref) REQUIRES(!uncompilable_methods_lock_);
- bool IsUncompilableMethod(MethodReference ref) const REQUIRES(!uncompilable_methods_lock_);
-
- private:
- // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
- mutable ReaderWriterMutex uncompilable_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::set<MethodReference> uncompilable_methods_ GUARDED_BY(uncompilable_methods_lock_);
-
- // Rejected classes.
- // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
- mutable ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
-
- friend class verifier::VerifierDepsTest;
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
diff --git a/compiler/driver/compiled_code_storage.h b/compiler/driver/compiled_code_storage.h
new file mode 100644
index 0000000..cef7398
--- /dev/null
+++ b/compiler/driver/compiled_code_storage.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILED_CODE_STORAGE_H_
+#define ART_COMPILER_DRIVER_COMPILED_CODE_STORAGE_H_
+
+#include <string>
+
+#include "base/array_ref.h"
+#include "base/macros.h"
+
+namespace art HIDDEN {
+
+namespace linker {
+class LinkerPatch;
+} // namespace linker
+
+class CompiledMethod;
+enum class InstructionSet;
+
+// Interface for storing AOT-compiled artifacts.
+// These artifacts include compiled method code and related stack maps and
+// linker patches as well as the compiled thunk code required for some kinds
+// of linker patches.
+//
+// This interface is used for passing AOT-compiled code and metadata produced
+// by the `libart-compiler` to `dex2oat`. The `CompiledMethod` created by
+// `dex2oat` is completely opaque to the `libart-compiler`.
+class CompiledCodeStorage {
+ public:
+ virtual CompiledMethod* CreateCompiledMethod(InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic) = 0;
+
+ // TODO: Rewrite the interface for passing thunks to the `dex2oat` to reduce
+ // locking. The `OptimizingCompiler` is currently calling `GetThunkCode()`
+ // and locking a mutex there for every `LinkerPatch` that needs a thunk to
+ // check whether we need to compile it. Using a thunk compiler interface,
+ // we could drive this from the `dex2oat` side and lock the mutex at most
+ // once per `CreateCompiledMethod()` for any number of patches.
+ virtual ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& patch,
+ /*out*/ std::string* debug_name = nullptr) = 0;
+ virtual void SetThunkCode(const linker::LinkerPatch& patch,
+ ArrayRef<const uint8_t> code,
+ const std::string& debug_name) = 0;
+
+ protected:
+ CompiledCodeStorage() {}
+ ~CompiledCodeStorage() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledCodeStorage);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILED_CODE_STORAGE_H_
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc
deleted file mode 100644
index 4857ec0..0000000
--- a/compiler/driver/compiled_method_storage.cc
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <ostream>
-
-#include "compiled_method_storage.h"
-
-#include <android-base/logging.h>
-
-#include "base/data_hash.h"
-#include "base/utils.h"
-#include "compiled_method.h"
-#include "linker/linker_patch.h"
-#include "thread-current-inl.h"
-#include "utils/dedupe_set-inl.h"
-#include "utils/swap_space.h"
-
-namespace art {
-
-namespace { // anonymous namespace
-
-template <typename T>
-const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) {
- DCHECK(!array.empty());
- SwapAllocator<uint8_t> allocator(swap_space);
- void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size()));
- LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size());
- std::copy(array.begin(), array.end(), array_copy->begin());
- return array_copy;
-}
-
-template <typename T>
-void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) {
- SwapAllocator<uint8_t> allocator(swap_space);
- size_t size = LengthPrefixedArray<T>::ComputeSize(array->size());
- array->~LengthPrefixedArray<T>();
- allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size);
-}
-
-} // anonymous namespace
-
-template <typename T, typename DedupeSetType>
-inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray(
- const ArrayRef<const T>& data,
- DedupeSetType* dedupe_set) {
- if (data.empty()) {
- return nullptr;
- } else if (!DedupeEnabled()) {
- return CopyArray(swap_space_.get(), data);
- } else {
- return dedupe_set->Add(Thread::Current(), data);
- }
-}
-
-template <typename T>
-inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
- const LengthPrefixedArray<T>* array) {
- if (array != nullptr && !DedupeEnabled()) {
- ReleaseArray(swap_space_.get(), array);
- }
-}
-
-template <typename ContentType>
-class CompiledMethodStorage::DedupeHashFunc {
- private:
- static constexpr bool kUseMurmur3Hash = true;
-
- public:
- size_t operator()(const ArrayRef<ContentType>& array) const {
- return DataHash()(array);
- }
-};
-
-template <typename T>
-class CompiledMethodStorage::LengthPrefixedArrayAlloc {
- public:
- explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
- : swap_space_(swap_space) {
- }
-
- const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
- return CopyArray(swap_space_, array);
- }
-
- void Destroy(const LengthPrefixedArray<T>* array) {
- ReleaseArray(swap_space_, array);
- }
-
- private:
- SwapSpace* const swap_space_;
-};
-
-class CompiledMethodStorage::ThunkMapKey {
- public:
- ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2)
- : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {}
-
- bool operator<(const ThunkMapKey& other) const {
- if (custom_value1_ != other.custom_value1_) {
- return custom_value1_ < other.custom_value1_;
- }
- if (custom_value2_ != other.custom_value2_) {
- return custom_value2_ < other.custom_value2_;
- }
- return type_ < other.type_;
- }
-
- private:
- linker::LinkerPatch::Type type_;
- uint32_t custom_value1_;
- uint32_t custom_value2_;
-};
-
-class CompiledMethodStorage::ThunkMapValue {
- public:
- ThunkMapValue(std::vector<uint8_t, SwapAllocator<uint8_t>>&& code,
- const std::string& debug_name)
- : code_(std::move(code)), debug_name_(debug_name) {}
-
- ArrayRef<const uint8_t> GetCode() const {
- return ArrayRef<const uint8_t>(code_);
- }
-
- const std::string& GetDebugName() const {
- return debug_name_;
- }
-
- private:
- std::vector<uint8_t, SwapAllocator<uint8_t>> code_;
- std::string debug_name_;
-};
-
-CompiledMethodStorage::CompiledMethodStorage(int swap_fd)
- : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
- dedupe_enabled_(true),
- dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
- dedupe_vmap_table_("dedupe vmap table",
- LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
- dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
- dedupe_linker_patches_("dedupe cfi info",
- LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())),
- thunk_map_lock_("thunk_map_lock"),
- thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) {
-}
-
-CompiledMethodStorage::~CompiledMethodStorage() {
- // All done by member destructors.
-}
-
-void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
- if (swap_space_.get() != nullptr) {
- const size_t swap_size = swap_space_->GetSize();
- os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
- }
- if (extended) {
- Thread* self = Thread::Current();
- os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
- os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
- os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
- }
-}
-
-const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
- const ArrayRef<const uint8_t>& code) {
- return AllocateOrDeduplicateArray(code, &dedupe_code_);
-}
-
-void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
- ReleaseArrayIfNotDeduplicated(code);
-}
-
-size_t CompiledMethodStorage::UniqueCodeEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_code_.Size(Thread::Current());
-}
-
-const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
- const ArrayRef<const uint8_t>& table) {
- return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
-}
-
-void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
- ReleaseArrayIfNotDeduplicated(table);
-}
-
-size_t CompiledMethodStorage::UniqueVMapTableEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_vmap_table_.Size(Thread::Current());
-}
-
-const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
- const ArrayRef<const uint8_t>& cfi_info) {
- return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
-}
-
-void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
- ReleaseArrayIfNotDeduplicated(cfi_info);
-}
-
-size_t CompiledMethodStorage::UniqueCFIInfoEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_cfi_info_.Size(Thread::Current());
-}
-
-const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
- const ArrayRef<const linker::LinkerPatch>& linker_patches) {
- return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
-}
-
-void CompiledMethodStorage::ReleaseLinkerPatches(
- const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
- ReleaseArrayIfNotDeduplicated(linker_patches);
-}
-
-size_t CompiledMethodStorage::UniqueLinkerPatchesEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_linker_patches_.Size(Thread::Current());
-}
-
-CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
- const linker::LinkerPatch& linker_patch) {
- uint32_t custom_value1 = 0u;
- uint32_t custom_value2 = 0u;
- switch (linker_patch.GetType()) {
- case linker::LinkerPatch::Type::kCallEntrypoint:
- custom_value1 = linker_patch.EntrypointOffset();
- break;
- case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
- custom_value1 = linker_patch.GetBakerCustomValue1();
- custom_value2 = linker_patch.GetBakerCustomValue2();
- break;
- case linker::LinkerPatch::Type::kCallRelative:
- // No custom values.
- break;
- default:
- LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
- UNREACHABLE();
- }
- return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
-}
-
-ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
- /*out*/ std::string* debug_name) {
- ThunkMapKey key = GetThunkMapKey(linker_patch);
- MutexLock lock(Thread::Current(), thunk_map_lock_);
- auto it = thunk_map_.find(key);
- if (it != thunk_map_.end()) {
- const ThunkMapValue& value = it->second;
- if (debug_name != nullptr) {
- *debug_name = value.GetDebugName();
- }
- return value.GetCode();
- } else {
- if (debug_name != nullptr) {
- *debug_name = std::string();
- }
- return ArrayRef<const uint8_t>();
- }
-}
-
-void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
- ArrayRef<const uint8_t> code,
- const std::string& debug_name) {
- DCHECK(!code.empty());
- ThunkMapKey key = GetThunkMapKey(linker_patch);
- std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
- code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
- ThunkMapValue value(std::move(code_copy), debug_name);
- MutexLock lock(Thread::Current(), thunk_map_lock_);
- // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
- thunk_map_.emplace(key, std::move(value));
-}
-
-} // namespace art
diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h
deleted file mode 100644
index f9f3401..0000000
--- a/compiler/driver/compiled_method_storage.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
-#define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
-
-#include <iosfwd>
-#include <map>
-#include <memory>
-
-#include "base/array_ref.h"
-#include "base/length_prefixed_array.h"
-#include "base/macros.h"
-#include "utils/dedupe_set.h"
-#include "utils/swap_space.h"
-
-namespace art {
-
-namespace linker {
-class LinkerPatch;
-} // namespace linker
-
-class CompiledMethodStorage {
- public:
- explicit CompiledMethodStorage(int swap_fd);
- ~CompiledMethodStorage();
-
- void DumpMemoryUsage(std::ostream& os, bool extended) const;
-
- void SetDedupeEnabled(bool dedupe_enabled) {
- dedupe_enabled_ = dedupe_enabled;
- }
- bool DedupeEnabled() const {
- return dedupe_enabled_;
- }
-
- SwapAllocator<void> GetSwapSpaceAllocator() {
- return SwapAllocator<void>(swap_space_.get());
- }
-
- const LengthPrefixedArray<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
- void ReleaseCode(const LengthPrefixedArray<uint8_t>* code);
- size_t UniqueCodeEntries() const;
-
- const LengthPrefixedArray<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& table);
- void ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table);
- size_t UniqueVMapTableEntries() const;
-
- const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
- void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info);
- size_t UniqueCFIInfoEntries() const;
-
- const LengthPrefixedArray<linker::LinkerPatch>* DeduplicateLinkerPatches(
- const ArrayRef<const linker::LinkerPatch>& linker_patches);
- void ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch>* linker_patches);
- size_t UniqueLinkerPatchesEntries() const;
-
- // Returns the code associated with the given patch.
- // If the code has not been set, returns empty data.
- // If `debug_name` is not null, stores the associated debug name in `*debug_name`.
- ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& linker_patch,
- /*out*/ std::string* debug_name = nullptr);
-
- // Sets the code and debug name associated with the given patch.
- void SetThunkCode(const linker::LinkerPatch& linker_patch,
- ArrayRef<const uint8_t> code,
- const std::string& debug_name);
-
- private:
- class ThunkMapKey;
- class ThunkMapValue;
- using ThunkMapValueType = std::pair<const ThunkMapKey, ThunkMapValue>;
- using ThunkMap = std::map<ThunkMapKey,
- ThunkMapValue,
- std::less<ThunkMapKey>,
- SwapAllocator<ThunkMapValueType>>;
- static_assert(std::is_same<ThunkMapValueType, ThunkMap::value_type>::value, "Value type check.");
-
- static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch);
-
- template <typename T, typename DedupeSetType>
- const LengthPrefixedArray<T>* AllocateOrDeduplicateArray(const ArrayRef<const T>& data,
- DedupeSetType* dedupe_set);
-
- template <typename T>
- void ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T>* array);
-
- // DeDuplication data structures.
- template <typename ContentType>
- class DedupeHashFunc;
-
- template <typename T>
- class LengthPrefixedArrayAlloc;
-
- template <typename T>
- using ArrayDedupeSet = DedupeSet<ArrayRef<const T>,
- LengthPrefixedArray<T>,
- LengthPrefixedArrayAlloc<T>,
- size_t,
- DedupeHashFunc<const T>,
- 4>;
-
- // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
- // as other fields rely on this.
- std::unique_ptr<SwapSpace> swap_space_;
-
- bool dedupe_enabled_;
-
- ArrayDedupeSet<uint8_t> dedupe_code_;
- ArrayDedupeSet<uint8_t> dedupe_vmap_table_;
- ArrayDedupeSet<uint8_t> dedupe_cfi_info_;
- ArrayDedupeSet<linker::LinkerPatch> dedupe_linker_patches_;
-
- Mutex thunk_map_lock_;
- ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_);
-
- DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 51cd999..603596f 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -23,6 +23,7 @@
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
+#include "art_method-inl.h"
#include "base/runtime_debug.h"
#include "base/string_view_cpp20.h"
#include "base/variant_map.h"
@@ -30,12 +31,11 @@
#include "cmdline_parser.h"
#include "compiler_options_map-inl.h"
#include "dex/dex_file-inl.h"
-#include "dex/verification_results.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "simple_compiler_options_map.h"
-namespace art {
+namespace art HIDDEN {
CompilerOptions::CompilerOptions()
: compiler_filter_(CompilerFilter::kDefaultCompilerFilter),
@@ -48,7 +48,6 @@
no_inline_from_(),
dex_files_for_oat_file_(),
image_classes_(),
- verification_results_(nullptr),
compiler_type_(CompilerType::kAotCompiler),
image_type_(ImageType::kNone),
multi_image_(false),
@@ -146,14 +145,34 @@
bool CompilerOptions::IsImageClass(const char* descriptor) const {
// Historical note: We used to hold the set indirectly and there was a distinction between an
- // empty set and a null, null meaning to include all classes. However, the distiction has been
+ // empty set and a null, null meaning to include all classes. However, the distinction has been
// removed; if we don't have a profile, we treat it as an empty set of classes. b/77340429
return image_classes_.find(std::string_view(descriptor)) != image_classes_.end();
}
-const VerificationResults* CompilerOptions::GetVerificationResults() const {
- DCHECK(Runtime::Current()->IsAotCompiler());
- return verification_results_;
+bool CompilerOptions::IsPreloadedClass(const char* pretty_descriptor) const {
+ return preloaded_classes_.find(std::string_view(pretty_descriptor)) != preloaded_classes_.end();
+}
+
+bool CompilerOptions::ShouldCompileWithClinitCheck(ArtMethod* method) const {
+ if (method != nullptr &&
+ Runtime::Current()->IsAotCompiler() &&
+ method->IsStatic() &&
+ !method->IsConstructor() &&
+ // Compiled code for native methods never do a clinit check, so we may put the resolution
+ // trampoline for native methods. This means that it's possible post zygote fork for the
+ // entry to be dirtied. We could resolve this by either:
+ // - Make these methods use the generic JNI entrypoint, but that's not
+ // desirable for a method that is in the profile.
+ // - Ensure the declaring class of such native methods are always in the
+ // preloaded-classes list.
+ // - Emit the clinit check in the compiled code of native methods.
+ !method->IsNative()) {
+ ScopedObjectAccess soa(Thread::Current());
+ ObjPtr<mirror::Class> cls = method->GetDeclaringClass<kWithoutReadBarrier>();
+ return cls->IsInBootImageAndNotInPreloadedClasses();
+ }
+ return false;
}
} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 1bffdb1..1cc9f2d 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -30,7 +30,7 @@
#include "base/utils.h"
#include "optimizing/register_allocator.h"
-namespace art {
+namespace art HIDDEN {
namespace jit {
class JitCompiler;
@@ -44,11 +44,11 @@
class Arm64RelativePatcherTest;
} // namespace linker
+class ArtMethod;
class DexFile;
enum class InstructionSet;
class InstructionSetFeatures;
class ProfileCompilationInfo;
-class VerificationResults;
// Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared.
enum class ProfileMethodsCheck : uint8_t {
@@ -83,8 +83,8 @@
kAppImage, // Creating app image.
};
- CompilerOptions();
- ~CompilerOptions();
+ EXPORT CompilerOptions();
+ EXPORT ~CompilerOptions();
CompilerFilter::Filter GetCompilerFilter() const {
return compiler_filter_;
@@ -298,9 +298,11 @@
return image_classes_;
}
- bool IsImageClass(const char* descriptor) const;
+ EXPORT bool IsImageClass(const char* descriptor) const;
- const VerificationResults* GetVerificationResults() const;
+ // Returns whether the given `pretty_descriptor` is in the list of preloaded
+ // classes. `pretty_descriptor` should be the result of calling `PrettyDescriptor`.
+ EXPORT bool IsPreloadedClass(const char* pretty_descriptor) const;
bool ParseCompilerOptions(const std::vector<std::string>& options,
bool ignore_unrecognized,
@@ -383,9 +385,15 @@
return ContainsElement(GetDexFilesForOatFile(), dex_file);
}
+ // If this is a static non-constructor method in the boot classpath, and its class isn't
+ // initialized at compile-time, or won't be initialized by the zygote, add
+ // initialization checks at entry. This will avoid the need of trampolines
+ // which at runtime we will need to dirty after initialization.
+ EXPORT bool ShouldCompileWithClinitCheck(ArtMethod* method) const;
+
private:
- bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
- bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
+ EXPORT bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
+ EXPORT bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
CompilerFilter::Filter compiler_filter_;
size_t huge_method_threshold_;
@@ -408,8 +416,9 @@
// Must not be empty for real boot image, only for tests pretending to compile boot image.
HashSet<std::string> image_classes_;
- // Results of AOT verification.
- const VerificationResults* verification_results_;
+ // Classes listed in the preloaded-classes file, used for boot image and
+ // boot image extension compilation.
+ HashSet<std::string> preloaded_classes_;
CompilerType compiler_type_;
ImageType image_type_;
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index fcbc0f2..79a5962 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -29,7 +29,7 @@
#include "cmdline_parser.h"
#include "compiler_options.h"
-namespace art {
+namespace art HIDDEN {
template <>
struct CmdlineType<CompilerFilter::Filter> : CmdlineTypeParser<CompilerFilter::Filter> {
@@ -118,6 +118,7 @@
template <typename Map, typename Builder>
inline void AddCompilerOptionsArgumentParserOptions(Builder& b) {
+ // clang-format off
b.
Define("--compiler-filter=_")
.template WithType<CompilerFilter::Filter>()
@@ -256,6 +257,7 @@
.template WithType<unsigned int>()
.WithHelp("Maximum solid block size for compressed images.")
.IntoKey(Map::MaxImageBlockSize);
+ // clang-format on
}
#pragma GCC diagnostic pop
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
index 7e2f846..b2dd57d 100644
--- a/compiler/driver/compiler_options_map.h
+++ b/compiler/driver/compiler_options_map.h
@@ -21,10 +21,11 @@
#include <vector>
#include "base/compiler_filter.h"
+#include "base/macros.h"
#include "base/variant_map.h"
#include "cmdline_types.h"
-namespace art {
+namespace art HIDDEN {
enum class ProfileMethodsCheck : uint8_t;
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 0d0f074..ccebfa9 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -25,7 +25,7 @@
#include "mirror/dex_cache.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
ClassLinker* class_linker,
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index def90fa..d595c0a 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -20,11 +20,12 @@
#include <stdint.h>
#include "base/arena_object.h"
+#include "base/macros.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file.h"
#include "handle.h"
-namespace art {
+namespace art HIDDEN {
namespace mirror {
class Class;
class ClassLoader;
diff --git a/compiler/driver/simple_compiler_options_map.h b/compiler/driver/simple_compiler_options_map.h
index e7a51a4..6663c0c 100644
--- a/compiler/driver/simple_compiler_options_map.h
+++ b/compiler/driver/simple_compiler_options_map.h
@@ -23,9 +23,10 @@
#include <memory>
#include "compiler_options_map-inl.h"
+#include "base/macros.h"
#include "base/variant_map.h"
-namespace art {
+namespace art HIDDEN {
template <typename TValue>
struct SimpleParseArgumentMapKey : VariantMapKey<TValue> {
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index 495398b..82c4998 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <android-base/test_utils.h>
+
#include <memory>
#include <type_traits>
@@ -22,6 +24,7 @@
#include "base/callee_save_type.h"
#include "base/enums.h"
#include "base/leb128.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "class_linker.h"
#include "common_runtime_test.h"
@@ -42,7 +45,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
class ExceptionTest : public CommonRuntimeTest {
protected:
@@ -78,7 +81,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stack_maps(&allocator, kRuntimeISA);
- stack_maps.BeginMethod(4 * sizeof(void*), 0u, 0u, 0u);
+ stack_maps.BeginMethod(/* frame_size_in_bytes= */ 4 * sizeof(void*),
+ /* core_spill_mask= */ 0u,
+ /* fp_spill_mask= */ 0u,
+ /* num_dex_registers= */ 0u,
+ /* baseline= */ false,
+ /* debuggable= */ false);
stack_maps.BeginStackMapEntry(kDexPc, native_pc_offset);
stack_maps.EndStackMapEntry();
stack_maps.EndMethod(code_size);
@@ -86,7 +94,7 @@
const size_t stack_maps_size = stack_map.size();
const size_t header_size = sizeof(OatQuickMethodHeader);
- const size_t code_alignment = GetInstructionSetAlignment(kRuntimeISA);
+ const size_t code_alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
fake_header_code_and_maps_.resize(stack_maps_size + header_size + code_size + code_alignment);
// NB: The start of the vector might not have been allocated the desired alignment.
@@ -187,15 +195,24 @@
fake_stack.push_back(0);
}
- fake_stack.push_back(method_g_->GetOatQuickMethodHeader(0)->ToNativeQuickPc(
- method_g_, kDexPc, /* is_for_catch_handler= */ false)); // return pc
+ OatQuickMethodHeader* header = OatQuickMethodHeader::FromEntryPoint(
+ method_g_->GetEntryPointFromQuickCompiledCode());
+ // Untag native pc when running with hwasan since the pcs on the stack aren't tagged and we use
+ // this to create a fake stack. See OatQuickMethodHeader::Contains where we untag code pointers
+ // before comparing it with the PC from the stack.
+ uintptr_t native_pc = header->ToNativeQuickPc(method_g_, kDexPc);
+ if (running_with_hwasan()) {
+ // TODO(228989263): Use HWASanUntag once we have a hwasan target for tests too. HWASanUntag
+ // uses static checks which won't work if we don't have a dedicated target.
+ native_pc = (native_pc & ((1ULL << 56) - 1));
+ }
+ fake_stack.push_back(native_pc); // return pc
// Create/push fake 16byte stack frame for method g
fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
fake_stack.push_back(0);
fake_stack.push_back(0);
- fake_stack.push_back(method_g_->GetOatQuickMethodHeader(0)->ToNativeQuickPc(
- method_g_, kDexPc, /* is_for_catch_handler= */ false)); // return pc
+ fake_stack.push_back(native_pc); // return pc.
// Create/push fake 16byte stack frame for method f
fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 8462f75..e672367 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -34,13 +34,17 @@
#include "jit/jit_code_cache.h"
#include "jit/jit_logger.h"
-namespace art {
+namespace art HIDDEN {
namespace jit {
JitCompiler* JitCompiler::Create() {
return new JitCompiler();
}
+void JitCompiler::SetDebuggableCompilerOption(bool value) {
+ compiler_options_->SetDebuggable(value);
+}
+
void JitCompiler::ParseCompilerOptions() {
// Special case max code units for inlining, whose default is "unset" (implictly
// meaning no limit). Do this before parsing the actual passed options.
@@ -121,7 +125,7 @@
}
}
-extern "C" JitCompilerInterface* jit_load() {
+EXPORT extern "C" JitCompilerInterface* jit_load() {
VLOG(jit) << "Create jit compiler";
auto* const jit_compiler = JitCompiler::Create();
CHECK(jit_compiler != nullptr);
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index 8e9966d..5a919fb 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_JIT_JIT_COMPILER_H_
#define ART_COMPILER_JIT_JIT_COMPILER_H_
+#include "base/macros.h"
#include "base/mutex.h"
#include "compilation_kind.h"
#include "jit/jit.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
class Compiler;
@@ -50,6 +51,8 @@
bool IsBaselineCompiler() const override;
+ void SetDebuggableCompilerOption(bool val) override;
+
bool GenerateDebugInfo() override;
void ParseCompilerOptions() override;
diff --git a/compiler/jit/jit_logger.cc b/compiler/jit/jit_logger.cc
index 6b9453f..3284526 100644
--- a/compiler/jit/jit_logger.cc
+++ b/compiler/jit/jit_logger.cc
@@ -24,7 +24,7 @@
#include "jit/jit_code_cache.h"
#include "oat_file-inl.h"
-namespace art {
+namespace art HIDDEN {
namespace jit {
#ifdef ART_TARGET_ANDROID
diff --git a/compiler/jit/jit_logger.h b/compiler/jit/jit_logger.h
index f4ef75a..9d1f307 100644
--- a/compiler/jit/jit_logger.h
+++ b/compiler/jit/jit_logger.h
@@ -19,11 +19,11 @@
#include <memory>
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/os.h"
-#include "compiled_method.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 368b87c..70cf2d4 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -20,6 +20,7 @@
#include "arch/instruction_set.h"
#include "base/arena_allocator.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "cfi_test.h"
#include "gtest/gtest.h"
@@ -30,7 +31,7 @@
#include "jni/jni_cfi_test_expected.inc"
-namespace art {
+namespace art HIDDEN {
// Run the tests only on host.
#ifndef ART_TARGET_ANDROID
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 0a1f017..d1d3190 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -22,6 +22,7 @@
#include "art_method-inl.h"
#include "base/bit_utils.h"
#include "base/casts.h"
+#include "base/macros.h"
#include "base/mem_map.h"
#include "class_linker.h"
#include "common_compiler_test.h"
@@ -43,7 +44,7 @@
#include "oat_quick_method_header.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-#include "thread.h"
+#include "thread-inl.h"
extern "C" JNIEXPORT jint JNICALL Java_MyClassNatives_bar(JNIEnv*, jobject, jint count) {
return count + 1;
@@ -71,7 +72,7 @@
// TODO: In the Baker read barrier configuration, add checks to ensure
// the Marking Register's value is correct.
-namespace art {
+namespace art HIDDEN {
enum class JniKind {
kNormal, // Regular kind of un-annotated natives.
@@ -236,13 +237,14 @@
bool direct,
const char* method_name,
const char* method_sig) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<2> hs(self);
Handle<mirror::ClassLoader> loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
// Compile the native method before starting the runtime
Handle<mirror::Class> c =
- hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader));
+ hs.NewHandle(class_linker_->FindClass(self, "LMyClassNatives;", loader));
const auto pointer_size = class_linker_->GetImagePointerSize();
ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size);
ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
@@ -251,8 +253,11 @@
// Class initialization could replace the entrypoint, so force
// the initialization before we set up the entrypoint below.
class_linker_->EnsureInitialized(
- soa.Self(), c, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
- class_linker_->MakeInitializedClassesVisiblyInitialized(soa.Self(), /*wait=*/ true);
+ self, c, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ {
+ ScopedThreadSuspension sts(self, ThreadState::kNative);
+ class_linker_->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
+ }
}
if (check_generic_jni_) {
method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
@@ -402,7 +407,7 @@
jobject JniCompilerTest::class_loader_;
void JniCompilerTest::AssertCallerObjectLocked(JNIEnv* env) {
- Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+ Thread* self = Thread::ForEnv(env);
CHECK_EQ(self, Thread::Current());
ScopedObjectAccess soa(self);
ArtMethod** caller_frame = self->GetManagedStack()->GetTopQuickFrame();
@@ -414,7 +419,7 @@
CHECK(!caller->IsCriticalNative());
CHECK(caller->IsSynchronized());
ObjPtr<mirror::Object> lock;
- if (self->GetManagedStack()->GetTopQuickFrameTag()) {
+ if (self->GetManagedStack()->GetTopQuickFrameGenericJniTag()) {
// Generic JNI.
lock = GetGenericJniSynchronizationObject(self, caller);
} else if (caller->IsStatic()) {
@@ -845,6 +850,7 @@
return x | y;
}
+EXPORT // Defined in `libart.so`.
void InitEntryPoints(JniEntryPoints* jpoints,
QuickEntryPoints* qpoints,
bool monitor_jni_entry_exit);
@@ -1307,7 +1313,7 @@
CompileForTestWithCurrentJni(class_loader_, false, "synchronizedThrowException", "()V");
}
}
- // Start runtime to avoid re-initialization in SetupForTest.
+ // Start runtime to avoid re-initialization in SetUpForTest.
Thread::Current()->TransitionFromSuspendedToRunnable();
bool started = runtime_->Start();
CHECK(started);
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index c1afdb8..d81ca77 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -23,7 +23,7 @@
#include "base/macros.h"
#include "utils/arm/managed_register_arm.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
//
@@ -199,6 +199,10 @@
return ArmManagedRegister::FromCoreRegister(R0);
}
+ManagedRegister ArmManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return ArmManagedRegister::FromCoreRegister(R2);
+}
+
void ArmManagedRuntimeCallingConvention::ResetIterator(FrameOffset displacement) {
ManagedRuntimeCallingConvention::ResetIterator(displacement);
gpr_index_ = 1u; // Skip r0 for ArtMethod*
diff --git a/compiler/jni/quick/arm/calling_convention_arm.h b/compiler/jni/quick/arm/calling_convention_arm.h
index 4526d9e..3a09d4e 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.h
+++ b/compiler/jni/quick/arm/calling_convention_arm.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_ARM_CALLING_CONVENTION_ARM_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class ArmManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -39,6 +40,7 @@
void ResetIterator(FrameOffset displacement) override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
void Next() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index ec77db3..e716502 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -22,7 +22,7 @@
#include "arch/instruction_set.h"
#include "utils/arm64/managed_register_arm64.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
static constexpr ManagedRegister kXArgumentRegisters[] = {
@@ -174,6 +174,10 @@
return Arm64ManagedRegister::FromXRegister(X0);
}
+ManagedRegister Arm64ManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return Arm64ManagedRegister::FromXRegister(X4);
+}
+
bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
if (IsCurrentParamAFloatOrDouble()) {
return itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments;
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.h b/compiler/jni/quick/arm64/calling_convention_arm64.h
index 176271e..f29eb15 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.h
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_ARM64_CALLING_CONVENTION_ARM64_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
class Arm64ManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -35,6 +36,7 @@
ManagedRegister ReturnRegister() const override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
ManagedRegister CurrentParamRegister() override;
diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc
index eb4d372..ce9bee6 100644
--- a/compiler/jni/quick/calling_convention.cc
+++ b/compiler/jni/quick/calling_convention.cc
@@ -37,7 +37,7 @@
#include "jni/quick/x86_64/calling_convention_x86_64.h"
#endif
-namespace art {
+namespace art HIDDEN {
// Managed runtime calling convention
diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h
index e2f3bfb..0187b14 100644
--- a/compiler/jni/quick/calling_convention.h
+++ b/compiler/jni/quick/calling_convention.h
@@ -20,11 +20,12 @@
#include "base/arena_object.h"
#include "base/array_ref.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "dex/primitive.h"
#include "thread.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
enum class InstructionSet;
@@ -244,6 +245,11 @@
// Register that holds the incoming method argument
virtual ManagedRegister MethodRegister() = 0;
+ // Register that is used to pass frame size for method exit hook call. This
+ // shouldn't be the same as the return register since method exit hook also expects
+ // return values in the return register.
+ virtual ManagedRegister ArgumentRegisterForMethodExitHook() = 0;
+
// Iterator interface
bool HasNext();
virtual void Next();
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 7435699..58d11ae 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -36,7 +36,9 @@
#include "dex/dex_file-inl.h"
#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "instrumentation.h"
#include "jni/jni_env_ext.h"
+#include "runtime.h"
#include "thread.h"
#include "utils/arm/managed_register_arm.h"
#include "utils/arm64/managed_register_arm64.h"
@@ -47,7 +49,7 @@
#define __ jni_asm->
-namespace art {
+namespace art HIDDEN {
constexpr size_t kIRTCookieSize = JniCallingConvention::SavedLocalReferenceCookieSize();
@@ -101,6 +103,20 @@
// i.e. if the method was annotated with @CriticalNative
const bool is_critical_native = (access_flags & kAccCriticalNative) != 0u;
+ bool needs_entry_exit_hooks =
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler();
+ // We don't support JITing stubs for critical native methods in debuggable runtimes yet.
+ // TODO(mythria): Add support required for calling method entry / exit hooks from critical native
+ // methods.
+ DCHECK_IMPLIES(needs_entry_exit_hooks, !is_critical_native);
+
+ // When walking the stack the top frame doesn't have a pc associated with it. We then depend on
+ // the invariant that we don't have JITed code when AOT code is available. In debuggable runtimes
+ // this invariant doesn't hold. So we tag the SP for JITed code to indentify if we are executing
+ // JITed code or AOT code. Since tagging involves additional instructions we tag only in
+ // debuggable runtimes.
+ bool should_tag_sp = needs_entry_exit_hooks;
+
VLOG(jni) << "JniCompile: Method :: "
<< dex_file.PrettyMethod(method_idx, /* with signature */ true)
<< " :: access_flags = " << std::hex << access_flags << std::dec;
@@ -219,7 +235,22 @@
// because garbage collections are disabled within the execution of a
// @CriticalNative method.
if (LIKELY(!is_critical_native)) {
- __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+ __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>(), should_tag_sp);
+ }
+
+ // 1.5. Call any method entry hooks if required.
+ // For critical native methods, we don't JIT stubs in debuggable runtimes (see
+ // OptimizingCompiler::JitCompile).
+ // TODO(mythria): Add support to call method entry / exit hooks for critical native methods too.
+ std::unique_ptr<JNIMacroLabel> method_entry_hook_slow_path;
+ std::unique_ptr<JNIMacroLabel> method_entry_hook_return;
+ if (UNLIKELY(needs_entry_exit_hooks)) {
+ uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
+ int offset = instrumentation::Instrumentation::HaveMethodEntryListenersOffset().Int32Value();
+ method_entry_hook_slow_path = __ CreateLabel();
+ method_entry_hook_return = __ CreateLabel();
+ __ TestByteAndJumpIfNotZero(address + offset, method_entry_hook_slow_path.get());
+ __ Bind(method_entry_hook_return.get());
}
// 2. Lock the object (if synchronized) and transition out of Runnable (if normal native).
@@ -532,7 +563,21 @@
__ Bind(suspend_check_resume.get());
}
- // 7.5. Remove activation - need to restore callee save registers since the GC
+ // 7.5. Check if method exit hooks needs to be called
+ // For critical native methods, we don't JIT stubs in debuggable runtimes.
+ // TODO(mythria): Add support to call method entry / exit hooks for critical native methods too.
+ std::unique_ptr<JNIMacroLabel> method_exit_hook_slow_path;
+ std::unique_ptr<JNIMacroLabel> method_exit_hook_return;
+ if (UNLIKELY(needs_entry_exit_hooks)) {
+ uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
+ int offset = instrumentation::Instrumentation::RunExitHooksOffset().Int32Value();
+ method_exit_hook_slow_path = __ CreateLabel();
+ method_exit_hook_return = __ CreateLabel();
+ __ TestByteAndJumpIfNotZero(address + offset, method_exit_hook_slow_path.get());
+ __ Bind(method_exit_hook_return.get());
+ }
+
+ // 7.6. Remove activation - need to restore callee save registers since the GC
// may have changed them.
DCHECK_EQ(jni_asm->cfi().GetCurrentCFAOffset(), static_cast<int>(current_frame_size));
if (LIKELY(!is_critical_native) || !main_jni_conv->UseTailCall()) {
@@ -605,7 +650,7 @@
if (reference_return) {
// Suspend check entry point overwrites top of managed stack and leaves it clobbered.
// We need to restore the top for subsequent runtime call to `JniDecodeReferenceResult()`.
- __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+ __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>(), should_tag_sp);
}
if (reference_return && main_out_arg_size != 0) {
__ IncreaseFrameSize(main_out_arg_size);
@@ -630,6 +675,25 @@
__ DeliverPendingException();
}
+ // 8.6. Method entry / exit hooks slow paths.
+ if (UNLIKELY(needs_entry_exit_hooks)) {
+ __ Bind(method_entry_hook_slow_path.get());
+ // Use Jni specific method entry hook that saves all the arguments. We have only saved the
+ // callee save registers at this point. So go through Jni specific stub that saves the rest
+ // of the live registers.
+ __ CallFromThread(QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEntryHook));
+ __ ExceptionPoll(exception_slow_path.get());
+ __ Jump(method_entry_hook_return.get());
+
+ __ Bind(method_exit_hook_slow_path.get());
+ // Method exit hooks is called just before tearing down the frame. So there are no live
+ // registers and we can directly call the method exit hook and don't need a Jni specific
+ // entrypoint.
+ __ Move(mr_conv->ArgumentRegisterForMethodExitHook(), managed_frame_size);
+ __ CallFromThread(QUICK_ENTRYPOINT_OFFSET(kPointerSize, pMethodExitHook));
+ __ Jump(method_exit_hook_return.get());
+ }
+
// 9. Finalize code generation.
__ FinalizeCode();
size_t cs = __ CodeSize();
diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h
index 52a6f3c..d43b2a9 100644
--- a/compiler/jni/quick/jni_compiler.h
+++ b/compiler/jni/quick/jni_compiler.h
@@ -21,8 +21,9 @@
#include "arch/instruction_set.h"
#include "base/array_ref.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class ArtMethod;
diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc
index 65be92c..598e8e7 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.cc
+++ b/compiler/jni/quick/x86/calling_convention_x86.cc
@@ -22,7 +22,7 @@
#include "arch/x86/jni_frame_x86.h"
#include "utils/x86/managed_register_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
static constexpr ManagedRegister kManagedCoreArgumentRegisters[] = {
@@ -143,6 +143,10 @@
return X86ManagedRegister::FromCpuRegister(EAX);
}
+ManagedRegister X86ManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return X86ManagedRegister::FromCpuRegister(EBX);
+}
+
void X86ManagedRuntimeCallingConvention::ResetIterator(FrameOffset displacement) {
ManagedRuntimeCallingConvention::ResetIterator(displacement);
gpr_arg_count_ = 1u; // Skip EAX for ArtMethod*
diff --git a/compiler/jni/quick/x86/calling_convention_x86.h b/compiler/jni/quick/x86/calling_convention_x86.h
index cd7ef5b..f0d663d 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.h
+++ b/compiler/jni/quick/x86/calling_convention_x86.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_X86_CALLING_CONVENTION_X86_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
class X86ManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -37,6 +38,7 @@
void ResetIterator(FrameOffset displacement) override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
void Next() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
index 862ee5e..9d0761d 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
@@ -23,7 +23,7 @@
#include "base/bit_utils.h"
#include "utils/x86_64/managed_register_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
static constexpr ManagedRegister kCoreArgumentRegisters[] = {
@@ -147,6 +147,10 @@
return X86_64ManagedRegister::FromCpuRegister(RDI);
}
+ManagedRegister X86_64ManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return X86_64ManagedRegister::FromCpuRegister(R8);
+}
+
bool X86_64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
if (IsCurrentParamAFloatOrDouble()) {
return itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments;
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.h b/compiler/jni/quick/x86_64/calling_convention_x86_64.h
index 483f1f5..859a277 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.h
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_X86_64_CALLING_CONVENTION_X86_64_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
class X86_64ManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -35,6 +36,7 @@
ManagedRegister ReturnRegister() const override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
ManagedRegister CurrentParamRegister() override;
diff --git a/compiler/libart-compiler.map b/compiler/libart-compiler.map
new file mode 100644
index 0000000..f66052a
--- /dev/null
+++ b/compiler/libart-compiler.map
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2022 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.
+#
+
+ART_COMPILER {
+ global:
+ extern "C++" {
+ art::debug::MakeMiniDebugInfo*;
+ *art::debug::WriteDebugInfo*;
+ art::Compiler::Create*;
+ art::CompilerOptions::*;
+ art::CreateTrampoline*;
+ art::IntrinsicObjects::*;
+ art::linker::operator*art::linker::LinkerPatch::Type*;
+ art::operator*art::Whence*;
+ };
+
+ jit_load;
+
+ local:
+ *;
+};
diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h
index 7da1e82..8ed7fce 100644
--- a/compiler/linker/linker_patch.h
+++ b/compiler/linker/linker_patch.h
@@ -23,9 +23,10 @@
#include <android-base/logging.h>
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "dex/method_reference.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
@@ -328,7 +329,7 @@
friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
};
-std::ostream& operator<<(std::ostream& os, LinkerPatch::Type type);
+EXPORT std::ostream& operator<<(std::ostream& os, LinkerPatch::Type type);
inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
return lhs.literal_offset_ == rhs.literal_offset_ &&
diff --git a/compiler/linker/linker_patch_test.cc b/compiler/linker/linker_patch_test.cc
index 997418c..1c46da1 100644
--- a/compiler/linker/linker_patch_test.cc
+++ b/compiler/linker/linker_patch_test.cc
@@ -16,9 +16,10 @@
#include <gtest/gtest.h>
+#include "base/macros.h"
#include "linker_patch.h"
-namespace art {
+namespace art HIDDEN {
namespace linker {
TEST(LinkerPatch, LinkerPatchOperators) {
diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc
index f1af4cb..22b174f 100644
--- a/compiler/linker/output_stream_test.cc
+++ b/compiler/linker/output_stream_test.cc
@@ -16,17 +16,17 @@
#include <android-base/logging.h>
+#include "base/common_art_test.h"
#include "base/macros.h"
#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
#include "stream/buffered_output_stream.h"
#include "stream/file_output_stream.h"
#include "stream/vector_output_stream.h"
-namespace art {
+namespace art HIDDEN {
namespace linker {
-class OutputStreamTest : public CommonRuntimeTest {
+class OutputStreamTest : public CommonArtTest {
protected:
void CheckOffset(off_t expected) {
off_t actual = output_stream_->Seek(0, kSeekCurrent);
diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc
index e1f061a..703584c 100644
--- a/compiler/optimizing/block_builder.cc
+++ b/compiler/optimizing/block_builder.cc
@@ -22,7 +22,7 @@
#include "dex/dex_file_exception_helpers.h"
#include "quicken_info.h"
-namespace art {
+namespace art HIDDEN {
HBasicBlockBuilder::HBasicBlockBuilder(HGraph* graph,
const DexFile* const dex_file,
diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h
index 42a3f32..8668ef8 100644
--- a/compiler/optimizing/block_builder.h
+++ b/compiler/optimizing/block_builder.h
@@ -17,13 +17,14 @@
#ifndef ART_COMPILER_OPTIMIZING_BLOCK_BUILDER_H_
#define ART_COMPILER_OPTIMIZING_BLOCK_BUILDER_H_
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class HBasicBlockBuilder : public ValueObject {
public:
diff --git a/compiler/optimizing/block_namer.cc b/compiler/optimizing/block_namer.cc
index d30448c..029e26b 100644
--- a/compiler/optimizing/block_namer.cc
+++ b/compiler/optimizing/block_namer.cc
@@ -18,7 +18,7 @@
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
std::ostream& BlockNamer::PrintName(std::ostream& os, HBasicBlock* blk) const {
os << "B";
diff --git a/compiler/optimizing/block_namer.h b/compiler/optimizing/block_namer.h
index ed396b9..39c5973 100644
--- a/compiler/optimizing/block_namer.h
+++ b/compiler/optimizing/block_namer.h
@@ -19,7 +19,9 @@
#include <ostream>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HBasicBlock;
struct BlockNamer {
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index dad3c81..52f1e9a 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -24,7 +24,7 @@
#include "nodes.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
class MonotonicValueRange;
@@ -564,6 +564,19 @@
early_exit_loop_.clear();
taken_test_loop_.clear();
finite_loop_.clear();
+
+ // We may have eliminated all bounds checks so we should update the flag.
+ // TODO(solanes): Do this without a linear pass of the graph?
+ GetGraph()->SetHasBoundsChecks(false);
+ for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsBoundsCheck()) {
+ GetGraph()->SetHasBoundsChecks(true);
+ return;
+ }
+ }
+ }
}
private:
@@ -1818,6 +1831,7 @@
HInstruction* condition,
bool is_null_check = false) {
HInstruction* suspend = loop->GetSuspendCheck();
+ DCHECK(suspend != nullptr);
block->InsertInstructionBefore(condition, block->GetLastInstruction());
DeoptimizationKind kind =
is_null_check ? DeoptimizationKind::kLoopNullBCE : DeoptimizationKind::kLoopBoundsBCE;
diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h
index ef08877..f210fa9 100644
--- a/compiler/optimizing/bounds_check_elimination.h
+++ b/compiler/optimizing/bounds_check_elimination.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
class HInductionVarAnalysis;
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 5927d68..929a9e7 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -17,6 +17,7 @@
#include "bounds_check_elimination.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "gvn.h"
#include "induction_var_analysis.h"
@@ -27,7 +28,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the BoundsCheckElimination tests.
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index e7826bb..48d1a9d 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -33,7 +33,7 @@
#include "ssa_builder.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
HGraphBuilder::HGraphBuilder(HGraph* graph,
const CodeItemDebugInfoAccessor& accessor,
@@ -103,7 +103,6 @@
graph_->SetNumberOfVRegs(code_item_accessor_.RegistersSize());
graph_->SetNumberOfInVRegs(code_item_accessor_.InsSize());
graph_->SetMaximumNumberOfOutVRegs(code_item_accessor_.OutsSize());
- graph_->SetHasTryCatch(code_item_accessor_.TriesSize() != 0);
// Use ScopedArenaAllocator for all local allocations.
ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
@@ -168,7 +167,6 @@
graph_->SetNumberOfVRegs(return_vregs + num_arg_vregs);
graph_->SetNumberOfInVRegs(num_arg_vregs);
graph_->SetMaximumNumberOfOutVRegs(num_arg_vregs);
- graph_->SetHasTryCatch(false);
// Use ScopedArenaAllocator for all local allocations.
ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 580769e..ef225d9 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -19,12 +19,13 @@
#include "base/arena_object.h"
#include "base/array_ref.h"
+#include "base/macros.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
class CodeGenerator;
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
index c6232ef..20a763c 100644
--- a/compiler/optimizing/cha_guard_optimization.cc
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -16,7 +16,7 @@
#include "cha_guard_optimization.h"
-namespace art {
+namespace art HIDDEN {
// Note we can only do CHA guard elimination/motion in a single pass, since
// if a guard is not removed, another guard might be removed due to
@@ -200,6 +200,7 @@
block->RemoveInstruction(deopt);
HInstruction* suspend = loop_info->GetSuspendCheck();
+ DCHECK(suspend != nullptr);
// Need a new deoptimize instruction that copies the environment
// of the suspend instruction for the loop.
HDeoptimize* deoptimize = new (GetGraph()->GetAllocator()) HDeoptimize(
diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h
index 440d51a..5c1fdd9 100644
--- a/compiler/optimizing/cha_guard_optimization.h
+++ b/compiler/optimizing/cha_guard_optimization.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
#define ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimize CHA guards by removing/moving them.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 6fe346b..4d177c2 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -15,6 +15,7 @@
*/
#include "code_generator.h"
+#include "base/globals.h"
#ifdef ART_ENABLE_CODEGEN_arm
#include "code_generator_arm_vixl.h"
@@ -39,7 +40,6 @@
#include "base/leb128.h"
#include "class_linker.h"
#include "class_root-inl.h"
-#include "compiled_method.h"
#include "dex/bytecode_utils.h"
#include "dex/code_item_accessors-inl.h"
#include "graph_visualizer.h"
@@ -61,7 +61,7 @@
#include "thread-current-inl.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
// Return whether a location is consistent with a type.
static bool CheckType(DataType::Type type, Location location) {
@@ -389,7 +389,8 @@
core_spill_mask_,
fpu_spill_mask_,
GetGraph()->GetNumberOfVRegs(),
- GetGraph()->IsCompilingBaseline());
+ GetGraph()->IsCompilingBaseline(),
+ GetGraph()->IsDebuggable());
size_t frame_start = GetAssembler()->CodeSize();
GenerateFrameEntry();
@@ -412,7 +413,13 @@
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
if (current->HasEnvironment()) {
- // Create stackmap for HNativeDebugInfo or any instruction which calls native code.
+ // Catch StackMaps are dealt with later on in `RecordCatchBlockInfo`.
+ if (block->IsCatchBlock() && block->GetFirstInstruction() == current) {
+ DCHECK(current->IsNop());
+ continue;
+ }
+
+ // Create stackmap for HNop or any instruction which calls native code.
// Note that we need correct mapping for the native PC of the call instruction,
// so the runtime's stackmap is not sufficient since it is at PC after the call.
MaybeRecordNativeDebugInfo(current, block->GetDexPc());
@@ -1123,7 +1130,7 @@
for (HBasicBlock* block : graph.GetReversePostOrder()) {
if (block->IsLoopHeader()) {
HSuspendCheck* suspend_check = block->GetLoopInformation()->GetSuspendCheck();
- if (!suspend_check->GetEnvironment()->IsFromInlinedInvoke()) {
+ if (suspend_check != nullptr && !suspend_check->GetEnvironment()->IsFromInlinedInvoke()) {
loop_headers.push_back(suspend_check);
}
}
@@ -1333,53 +1340,43 @@
continue;
}
- uint32_t dex_pc = block->GetDexPc();
- uint32_t num_vregs = graph_->GetNumberOfVRegs();
- uint32_t native_pc = GetAddressOf(block);
+ // Get the outer dex_pc. We save the full environment list for DCHECK purposes in kIsDebugBuild.
+ std::vector<uint32_t> dex_pc_list_for_verification;
+ if (kIsDebugBuild) {
+ dex_pc_list_for_verification.push_back(block->GetDexPc());
+ }
+ DCHECK(block->GetFirstInstruction()->IsNop());
+ DCHECK(block->GetFirstInstruction()->AsNop()->NeedsEnvironment());
+ HEnvironment* const environment = block->GetFirstInstruction()->GetEnvironment();
+ DCHECK(environment != nullptr);
+ HEnvironment* outer_environment = environment;
+ while (outer_environment->GetParent() != nullptr) {
+ outer_environment = outer_environment->GetParent();
+ if (kIsDebugBuild) {
+ dex_pc_list_for_verification.push_back(outer_environment->GetDexPc());
+ }
+ }
- stack_map_stream->BeginStackMapEntry(dex_pc,
+ if (kIsDebugBuild) {
+ // dex_pc_list_for_verification is set from innnermost to outermost. Let's reverse it
+ // since we are expected to pass from outermost to innermost.
+ std::reverse(dex_pc_list_for_verification.begin(), dex_pc_list_for_verification.end());
+ DCHECK_EQ(dex_pc_list_for_verification.front(), outer_environment->GetDexPc());
+ }
+
+ uint32_t native_pc = GetAddressOf(block);
+ stack_map_stream->BeginStackMapEntry(outer_environment->GetDexPc(),
native_pc,
/* register_mask= */ 0,
/* sp_mask= */ nullptr,
- StackMap::Kind::Catch);
+ StackMap::Kind::Catch,
+ /* needs_vreg_info= */ true,
+ dex_pc_list_for_verification);
- HInstruction* current_phi = block->GetFirstPhi();
- for (size_t vreg = 0; vreg < num_vregs; ++vreg) {
- while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
- HInstruction* next_phi = current_phi->GetNext();
- DCHECK(next_phi == nullptr ||
- current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
- << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
- current_phi = next_phi;
- }
-
- if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) {
- stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
- } else {
- Location location = current_phi->GetLocations()->Out();
- switch (location.GetKind()) {
- case Location::kStackSlot: {
- stack_map_stream->AddDexRegisterEntry(
- DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
- break;
- }
- case Location::kDoubleStackSlot: {
- stack_map_stream->AddDexRegisterEntry(
- DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
- stack_map_stream->AddDexRegisterEntry(
- DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize));
- ++vreg;
- DCHECK_LT(vreg, num_vregs);
- break;
- }
- default: {
- // All catch phis must be allocated to a stack slot.
- LOG(FATAL) << "Unexpected kind " << location.GetKind();
- UNREACHABLE();
- }
- }
- }
- }
+ EmitEnvironment(environment,
+ /* slow_path= */ nullptr,
+ /* needs_vreg_info= */ true,
+ /* is_for_catch_handler= */ true);
stack_map_stream->EndStackMapEntry();
}
@@ -1390,7 +1387,9 @@
code_generation_data_->AddSlowPath(slow_path);
}
-void CodeGenerator::EmitVRegInfo(HEnvironment* environment, SlowPathCode* slow_path) {
+void CodeGenerator::EmitVRegInfo(HEnvironment* environment,
+ SlowPathCode* slow_path,
+ bool is_for_catch_handler) {
StackMapStream* stack_map_stream = GetStackMapStream();
// Walk over the environment, and record the location of dex registers.
for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
@@ -1445,6 +1444,7 @@
}
case Location::kRegister : {
+ DCHECK(!is_for_catch_handler);
int id = location.reg();
if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(id)) {
uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(id);
@@ -1466,6 +1466,7 @@
}
case Location::kFpuRegister : {
+ DCHECK(!is_for_catch_handler);
int id = location.reg();
if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(id)) {
uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(id);
@@ -1487,6 +1488,7 @@
}
case Location::kFpuRegisterPair : {
+ DCHECK(!is_for_catch_handler);
int low = location.low();
int high = location.high();
if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(low)) {
@@ -1508,6 +1510,7 @@
}
case Location::kRegisterPair : {
+ DCHECK(!is_for_catch_handler);
int low = location.low();
int high = location.high();
if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(low)) {
@@ -1538,9 +1541,54 @@
}
}
+void CodeGenerator::EmitVRegInfoOnlyCatchPhis(HEnvironment* environment) {
+ StackMapStream* stack_map_stream = GetStackMapStream();
+ DCHECK(environment->GetHolder()->GetBlock()->IsCatchBlock());
+ DCHECK_EQ(environment->GetHolder()->GetBlock()->GetFirstInstruction(), environment->GetHolder());
+ HInstruction* current_phi = environment->GetHolder()->GetBlock()->GetFirstPhi();
+ for (size_t vreg = 0; vreg < environment->Size(); ++vreg) {
+ while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
+ HInstruction* next_phi = current_phi->GetNext();
+ DCHECK(next_phi == nullptr ||
+ current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
+ << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
+ current_phi = next_phi;
+ }
+
+ if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) {
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
+ } else {
+ Location location = current_phi->GetLocations()->Out();
+ switch (location.GetKind()) {
+ case Location::kStackSlot: {
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack,
+ location.GetStackIndex());
+ break;
+ }
+ case Location::kDoubleStackSlot: {
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack,
+ location.GetStackIndex());
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack,
+ location.GetHighStackIndex(kVRegSize));
+ ++vreg;
+ DCHECK_LT(vreg, environment->Size());
+ break;
+ }
+ default: {
+ LOG(FATAL) << "All catch phis must be allocated to a stack slot. Unexpected kind "
+ << location.GetKind();
+ UNREACHABLE();
+ }
+ }
+ }
+ }
+}
+
void CodeGenerator::EmitEnvironment(HEnvironment* environment,
SlowPathCode* slow_path,
- bool needs_vreg_info) {
+ bool needs_vreg_info,
+ bool is_for_catch_handler,
+ bool innermost_environment) {
if (environment == nullptr) return;
StackMapStream* stack_map_stream = GetStackMapStream();
@@ -1548,7 +1596,11 @@
if (emit_inline_info) {
// We emit the parent environment first.
- EmitEnvironment(environment->GetParent(), slow_path, needs_vreg_info);
+ EmitEnvironment(environment->GetParent(),
+ slow_path,
+ needs_vreg_info,
+ is_for_catch_handler,
+ /* innermost_environment= */ false);
stack_map_stream->BeginInlineInfoEntry(environment->GetMethod(),
environment->GetDexPc(),
needs_vreg_info ? environment->Size() : 0,
@@ -1556,9 +1608,13 @@
this);
}
+ // If a dex register map is not required we just won't emit it.
if (needs_vreg_info) {
- // If a dex register map is not required we just won't emit it.
- EmitVRegInfo(environment, slow_path);
+ if (innermost_environment && is_for_catch_handler) {
+ EmitVRegInfoOnlyCatchPhis(environment);
+ } else {
+ EmitVRegInfo(environment, slow_path, is_for_catch_handler);
+ }
}
if (emit_inline_info) {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 3dcf136..ee80357 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -26,6 +26,7 @@
#include "base/bit_utils.h"
#include "base/enums.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "base/memory_region.h"
#include "class_root.h"
#include "dex/string_reference.h"
@@ -33,13 +34,15 @@
#include "graph_visualizer.h"
#include "locations.h"
#include "nodes.h"
+#include "oat_quick_method_header.h"
#include "optimizing_compiler_stats.h"
#include "read_barrier_option.h"
#include "stack.h"
+#include "subtype_check.h"
#include "utils/assembler.h"
#include "utils/label.h"
-namespace art {
+namespace art HIDDEN {
// Binary encoding of 2^32 for type double.
static int64_t constexpr k2Pow32EncodingForDouble = INT64_C(0x41F0000000000000);
@@ -59,6 +62,16 @@
static const ReadBarrierOption gCompilerReadBarrierOption =
gUseReadBarrier ? kWithReadBarrier : kWithoutReadBarrier;
+constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
+constexpr size_t status_byte_offset =
+ mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
+constexpr uint32_t shifted_visibly_initialized_value =
+ enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
+constexpr uint32_t shifted_initializing_value =
+ enum_cast<uint32_t>(ClassStatus::kInitializing) << (status_lsb_position % kBitsPerByte);
+constexpr uint32_t shifted_initialized_value =
+ enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
+
class Assembler;
class CodeGenerator;
class CompilerOptions;
@@ -836,8 +849,11 @@
void BlockIfInRegister(Location location, bool is_out = false) const;
void EmitEnvironment(HEnvironment* environment,
SlowPathCode* slow_path,
- bool needs_vreg_info = true);
- void EmitVRegInfo(HEnvironment* environment, SlowPathCode* slow_path);
+ bool needs_vreg_info = true,
+ bool is_for_catch_handler = false,
+ bool innermost_environment = true);
+ void EmitVRegInfo(HEnvironment* environment, SlowPathCode* slow_path, bool is_for_catch_handler);
+ void EmitVRegInfoOnlyCatchPhis(HEnvironment* environment);
static void PrepareCriticalNativeArgumentMoves(
HInvokeStaticOrDirect* invoke,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 94eac52..5a5d36d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -27,7 +27,6 @@
#include "class_root-inl.h"
#include "class_table.h"
#include "code_generator_utils.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
@@ -44,6 +43,7 @@
#include "mirror/var_handle.h"
#include "offsets.h"
#include "optimizing/common_arm64.h"
+#include "optimizing/nodes.h"
#include "thread.h"
#include "utils/arm64/assembler_arm64.h"
#include "utils/assembler.h"
@@ -58,7 +58,7 @@
#error "ARM64 Codegen VIXL macro-assembler macro already defined."
#endif
-namespace art {
+namespace art HIDDEN {
template<class MirrorType>
class GcRoot;
@@ -825,6 +825,9 @@
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ __ Mov(vixl::aarch64::x4, arm64_codegen->GetFrameSize());
+ }
arm64_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ B(GetExitLabel());
@@ -1169,9 +1172,21 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathARM64(instruction);
codegen_->AddSlowPath(slow_path);
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ __ Ldr(value, MemOperand(sp, codegen_->GetStackOffsetOfShouldDeoptimizeFlag()));
+ __ Cbnz(value, slow_path->GetEntryLabel());
+ }
+
uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
- __ Mov(temp, address + offset);
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset() :
+ instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
+ __ Mov(temp, address + offset.Int32Value());
__ Ldrb(value, MemOperand(temp, 0));
__ Cbnz(value, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -1233,6 +1248,54 @@
void CodeGeneratorARM64::GenerateFrameEntry() {
MacroAssembler* masm = GetVIXLAssembler();
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ UseScratchRegisterScope temps(masm);
+ vixl::aarch64::Label resolution;
+ vixl::aarch64::Label memory_barrier;
+
+ Register temp1 = temps.AcquireW();
+ Register temp2 = temps.AcquireW();
+
+ // Check if we're visibly initialized.
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ Ldr(temp1, MemOperand(kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ Ldrb(temp2, HeapOperand(temp1, status_byte_offset));
+ __ Cmp(temp2, shifted_visibly_initialized_value);
+ __ B(hs, &frame_entry_label_);
+
+ // Check if we're initialized and jump to code that does a memory barrier if
+ // so.
+ __ Cmp(temp2, shifted_initialized_value);
+ __ B(hs, &memory_barrier);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ Cmp(temp2, shifted_initializing_value);
+ __ B(lo, &resolution);
+
+ __ Ldr(temp1, HeapOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ Ldr(temp2, MemOperand(tr, Thread::TidOffset<kArm64PointerSize>().Int32Value()));
+ __ Cmp(temp1, temp2);
+ __ B(eq, &frame_entry_label_);
+ __ Bind(&resolution);
+
+ // Jump to the resolution stub.
+ ThreadOffset64 entrypoint_offset =
+ GetThreadOffset<kArm64PointerSize>(kQuickQuickResolutionTrampoline);
+ __ Ldr(temp1.X(), MemOperand(tr, entrypoint_offset.Int32Value()));
+ __ Br(temp1.X());
+
+ __ Bind(&memory_barrier);
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
__ Bind(&frame_entry_label_);
bool do_overflow_check =
@@ -1364,12 +1427,12 @@
}
}
-void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) {
+void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool emit_null_check) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register card = temps.AcquireX();
Register temp = temps.AcquireW(); // Index within the CardTable - 32bit.
vixl::aarch64::Label done;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Cbz(value, &done);
}
// Load the address of the card table into `card`.
@@ -1391,7 +1454,7 @@
// of the card to mark; and 2. to load the `kCardDirty` value) saves a load
// (no need to explicitly load `kCardDirty` as an immediate value).
__ Strb(card, MemOperand(card, temp.X()));
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&done);
}
}
@@ -1904,11 +1967,6 @@
Register class_reg) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp = temps.AcquireW();
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- const size_t status_byte_offset =
- mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
// CMP (immediate) is limited to imm12 or imm12<<12, so we would need to materialize
// the constant 0xf0000000 for comparison with the full 32-bit field. To reduce the code
@@ -1974,6 +2032,13 @@
void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction,
HBasicBlock* successor) {
+ if (instruction->IsNoOp()) {
+ if (successor != nullptr) {
+ __ B(codegen_->GetLabelOf(successor));
+ }
+ return;
+ }
+
if (codegen_->CanUseImplicitSuspendCheck()) {
__ Ldr(kImplicitSuspendCheckRegister, MemOperand(kImplicitSuspendCheckRegister));
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2165,7 +2230,8 @@
void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
bool is_predicated =
instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->GetIsPredicatedSet();
@@ -2205,8 +2271,12 @@
}
}
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- codegen_->MarkGCCard(obj, Register(value), value_can_be_null);
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)) &&
+ write_barrier_kind != WriteBarrierKind::kDontEmit) {
+ codegen_->MarkGCCard(
+ obj,
+ Register(value),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_predicated) {
@@ -2871,7 +2941,11 @@
}
}
- codegen_->MarkGCCard(array, value.W(), /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ codegen_->MarkGCCard(array, value.W(), /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsLinked());
@@ -3845,12 +3919,12 @@
}
}
-void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderARM64::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorARM64::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorARM64::IncreaseFrame(size_t adjustment) {
@@ -3893,7 +3967,10 @@
}
void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
// Temp is used for read barrier.
@@ -6156,7 +6233,10 @@
}
void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderARM64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 66e8471..deba88b 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
#include "base/bit_field.h"
+#include "base/macros.h"
#include "class_root.h"
#include "code_generator.h"
#include "common_arm64.h"
@@ -36,7 +37,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace linker {
class Arm64RelativePatcherTest;
@@ -328,7 +329,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void HandleCondition(HCondition* instruction);
@@ -616,7 +618,7 @@
// Emit a write barrier.
void MarkGCCard(vixl::aarch64::Register object,
vixl::aarch64::Register value,
- bool value_can_be_null);
+ bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index ca3147e..51d6a46 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -26,7 +26,6 @@
#include "class_table.h"
#include "code_generator_utils.h"
#include "common_arm.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "gc/space/image_space.h"
@@ -46,7 +45,7 @@
#include "utils/assembler.h"
#include "utils/stack_checks.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
namespace vixl32 = vixl::aarch32;
@@ -974,6 +973,10 @@
(instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ // Load frame size to pass to the exit hooks
+ __ Mov(vixl::aarch32::Register(R2), arm_codegen->GetFrameSize());
+ }
arm_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ B(GetExitLabel());
@@ -2167,9 +2170,24 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathARMVIXL(instruction);
codegen_->AddSlowPath(slow_path);
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ GetAssembler()->LoadFromOffset(kLoadWord,
+ temp,
+ sp,
+ codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
+ __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
+ }
+
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset() :
+ instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
uint32_t address = reinterpret_cast32<uint32_t>(Runtime::Current()->GetInstrumentation());
- __ Mov(temp, address + offset);
+ __ Mov(temp, address + offset.Int32Value());
__ Ldrb(temp, MemOperand(temp, 0));
__ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -2237,6 +2255,61 @@
bool skip_overflow_check =
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Label resolution;
+ vixl32::Label memory_barrier;
+
+ // Check if we're visibly initialized.
+
+ vixl32::Register temp1 = temps.Acquire();
+ // Use r4 as other temporary register.
+ DCHECK(!blocked_core_registers_[R4]);
+ DCHECK(!kCoreCalleeSaves.Includes(r4));
+ vixl32::Register temp2 = r4;
+ for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
+ DCHECK(!reg.Is(r4));
+ }
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ Ldr(temp1, MemOperand(kMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ Ldrb(temp2, MemOperand(temp1, status_byte_offset));
+ __ Cmp(temp2, shifted_visibly_initialized_value);
+ __ B(cs, &frame_entry_label_);
+
+ // Check if we're initialized and jump to code that does a memory barrier if
+ // so.
+ __ Cmp(temp2, shifted_initialized_value);
+ __ B(cs, &memory_barrier);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ Cmp(temp2, shifted_initializing_value);
+ __ B(lo, &resolution);
+
+ __ Ldr(temp1, MemOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ Ldr(temp2, MemOperand(tr, Thread::TidOffset<kArmPointerSize>().Int32Value()));
+ __ Cmp(temp1, temp2);
+ __ B(eq, &frame_entry_label_);
+ __ Bind(&resolution);
+
+ // Jump to the resolution stub.
+ ThreadOffset32 entrypoint_offset =
+ GetThreadOffset<kArmPointerSize>(kQuickQuickResolutionTrampoline);
+ __ Ldr(temp1, MemOperand(tr, entrypoint_offset.Int32Value()));
+ __ Bx(temp1);
+
+ __ Bind(&memory_barrier);
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+
__ Bind(&frame_entry_label_);
if (HasEmptyFrame()) {
@@ -3072,12 +3145,12 @@
}
}
-void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderARMVIXL::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorARMVIXL::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorARMVIXL::IncreaseFrame(size_t adjustment) {
@@ -5730,8 +5803,9 @@
__ CompareAndBranchIfNonZero(temp1, &fail);
}
-void LocationsBuilderARMVIXL::HandleFieldSet(
- HInstruction* instruction, const FieldInfo& field_info) {
+void LocationsBuilderARMVIXL::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations =
@@ -5754,8 +5828,12 @@
// Temporary registers for the write barrier.
// TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
if (needs_write_barrier) {
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
- locations->AddTemp(Location::RequiresRegister());
+ if (write_barrier_kind != WriteBarrierKind::kDontEmit) {
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ } else if (kPoisonHeapReferences) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
} else if (generate_volatile) {
// ARM encoding have some additional constraints for ldrexd/strexd:
// - registers need to be consecutive
@@ -5776,7 +5854,8 @@
void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -5892,10 +5971,16 @@
UNREACHABLE();
}
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)) &&
+ write_barrier_kind != WriteBarrierKind::kDontEmit) {
vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
vixl32::Register card = RegisterFrom(locations->GetTemp(1));
- codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
+ codegen_->MarkGCCard(
+ temp,
+ card,
+ base,
+ RegisterFrom(value),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_volatile) {
@@ -6168,11 +6253,14 @@
}
void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -6205,11 +6293,14 @@
}
void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
@@ -6691,8 +6782,10 @@
locations->SetInAt(2, Location::RequiresRegister());
}
if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ // Temporary registers for the write barrier or register poisoning.
+ // TODO(solanes): We could reduce the temp usage but it requires some non-trivial refactoring of
+ // InstructionCodeGeneratorARMVIXL::VisitArraySet.
+ locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -6844,7 +6937,11 @@
}
}
- codegen_->MarkGCCard(temp1, temp2, array, value, /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ codegen_->MarkGCCard(temp1, temp2, array, value, /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsReferenced());
@@ -7075,9 +7172,9 @@
vixl32::Register card,
vixl32::Register object,
vixl32::Register value,
- bool value_can_be_null) {
+ bool emit_null_check) {
vixl32::Label is_null;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ CompareAndBranchIfZero(value, &is_null, /* is_far_target=*/ false);
}
// Load the address of the card table into `card`.
@@ -7100,7 +7197,7 @@
// of the card to mark; and 2. to load the `kCardDirty` value) saves a load
// (no need to explicitly load `kCardDirty` as an immediate value).
__ Strb(card, MemOperand(card, temp));
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&is_null);
}
}
@@ -7625,12 +7722,7 @@
LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
UseScratchRegisterScope temps(GetVIXLAssembler());
vixl32::Register temp = temps.Acquire();
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << status_lsb_position;
-
- const size_t status_offset = mirror::Class::StatusOffset().SizeValue();
- GetAssembler()->LoadFromOffset(kLoadWord, temp, class_reg, status_offset);
+ __ Ldrb(temp, MemOperand(class_reg, status_byte_offset));
__ Cmp(temp, shifted_visibly_initialized_value);
__ B(lo, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 9caa498..872a17b 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "class_root.h"
#include "code_generator.h"
#include "common_arm.h"
@@ -36,7 +37,7 @@
#include "aarch32/macro-assembler-aarch32.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace linker {
class Thumb2RelativePatcherTest;
@@ -309,7 +310,9 @@
void HandleIntegerRotate(LocationSummary* locations);
void HandleLongRotate(LocationSummary* locations);
void HandleShift(HBinaryOperation* operation);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
Location ArithmeticZeroOrFpuRegister(HInstruction* input);
@@ -378,7 +381,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void GenerateMinMaxInt(LocationSummary* locations, bool is_min);
@@ -542,7 +546,7 @@
vixl::aarch32::Register card,
vixl::aarch32::Register object,
vixl::aarch32::Register value,
- bool value_can_be_null);
+ bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
@@ -602,7 +606,6 @@
struct PcRelativePatchInfo {
PcRelativePatchInfo(const DexFile* dex_file, uint32_t off_or_idx)
: target_dex_file(dex_file), offset_or_index(off_or_idx) { }
- PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
// Target dex file or null for .data.bmig.rel.ro patches.
const DexFile* target_dex_file;
diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc
index abec264..9980592 100644
--- a/compiler/optimizing/code_generator_utils.cc
+++ b/compiler/optimizing/code_generator_utils.cc
@@ -20,7 +20,7 @@
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void CalculateMagicAndShiftForDivRem(int64_t divisor, bool is_long,
int64_t* magic, int* shift) {
diff --git a/compiler/optimizing/code_generator_utils.h b/compiler/optimizing/code_generator_utils.h
index 64665ad..9d9ab2b 100644
--- a/compiler/optimizing/code_generator_utils.h
+++ b/compiler/optimizing/code_generator_utils.h
@@ -21,7 +21,9 @@
#include <cstdlib>
#include <limits>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HInstruction;
diff --git a/compiler/optimizing/code_generator_vector_arm64_neon.cc b/compiler/optimizing/code_generator_vector_arm64_neon.cc
index 0fe9898..c70c8f5 100644
--- a/compiler/optimizing/code_generator_vector_arm64_neon.cc
+++ b/compiler/optimizing/code_generator_vector_arm64_neon.cc
@@ -23,7 +23,7 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
using helpers::DRegisterFrom;
diff --git a/compiler/optimizing/code_generator_vector_arm64_sve.cc b/compiler/optimizing/code_generator_vector_arm64_sve.cc
index 824b6c9..87b6d57 100644
--- a/compiler/optimizing/code_generator_vector_arm64_sve.cc
+++ b/compiler/optimizing/code_generator_vector_arm64_sve.cc
@@ -23,17 +23,14 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
using helpers::DRegisterFrom;
-using helpers::HeapOperand;
using helpers::InputRegisterAt;
using helpers::Int64FromLocation;
using helpers::LocationFrom;
using helpers::OutputRegister;
-using helpers::QRegisterFrom;
-using helpers::StackOperandFrom;
using helpers::SveStackOperandFrom;
using helpers::VRegisterFrom;
using helpers::ZRegisterFrom;
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index c46f9b7..1359d89 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -20,7 +20,7 @@
namespace vixl32 = vixl::aarch32;
using namespace vixl32; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm {
using helpers::DRegisterFrom;
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 9c837dd..0e57604 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -19,7 +19,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 330bf76..10b84d6 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -19,7 +19,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 31aa2a4..58cb56d 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -20,7 +20,6 @@
#include "art_method-inl.h"
#include "class_table.h"
#include "code_generator_utils.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
@@ -36,6 +35,7 @@
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
#include "mirror/var_handle.h"
+#include "optimizing/nodes.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "utils/assembler.h"
@@ -43,7 +43,7 @@
#include "utils/x86/assembler_x86.h"
#include "utils/x86/managed_register_x86.h"
-namespace art {
+namespace art HIDDEN {
template<class MirrorType>
class GcRoot;
@@ -967,6 +967,9 @@
(instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ __ movl(EBX, Immediate(codegen->GetFrameSize()));
+ }
x86_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ jmp(GetExitLabel());
@@ -1197,9 +1200,21 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathX86(instruction);
codegen_->AddSlowPath(slow_path);
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ __ cmpl(Address(ESP, codegen_->GetStackOffsetOfShouldDeoptimizeFlag()), Immediate(0));
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ }
+
uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
- __ cmpb(Address::Absolute(address + offset), Immediate(0));
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset() :
+ instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
+ __ cmpb(Address::Absolute(address + offset.Int32Value()), Immediate(0));
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
@@ -1261,6 +1276,44 @@
void CodeGeneratorX86::GenerateFrameEntry() {
__ cfi().SetCurrentCFAOffset(kX86WordSize); // return address
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ NearLabel continue_execution, resolution;
+ // We'll use EBP as temporary.
+ __ pushl(EBP);
+ // Check if we're visibly initialized.
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ movl(EBP, Address(kMethodRegisterArgument, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_visibly_initialized_value));
+ __ j(kAboveEqual, &continue_execution);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_initializing_value));
+ __ j(kBelow, &resolution);
+
+ __ movl(EBP, Address(EBP, mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ fs()->cmpl(EBP, Address::Absolute(Thread::TidOffset<kX86PointerSize>().Int32Value()));
+ __ j(kEqual, &continue_execution);
+ __ Bind(&resolution);
+
+ __ popl(EBP);
+ // Jump to the resolution stub.
+ ThreadOffset32 entrypoint_offset =
+ GetThreadOffset<kX86PointerSize>(kQuickQuickResolutionTrampoline);
+ __ fs()->jmp(Address::Absolute(entrypoint_offset));
+
+ __ Bind(&continue_execution);
+ __ popl(EBP);
+ }
+
__ Bind(&frame_entry_label_);
bool skip_overflow_check =
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
@@ -2230,12 +2283,12 @@
}
}
-void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderX86::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorX86::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorX86::IncreaseFrame(size_t adjustment) {
@@ -5689,13 +5742,10 @@
DCHECK_EQ(size, linker_patches->size());
}
-void CodeGeneratorX86::MarkGCCard(Register temp,
- Register card,
- Register object,
- Register value,
- bool value_can_be_null) {
+void CodeGeneratorX86::MarkGCCard(
+ Register temp, Register card, Register object, Register value, bool emit_null_check) {
NearLabel is_null;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ testl(value, value);
__ j(kEqual, &is_null);
}
@@ -5720,7 +5770,7 @@
// (no need to explicitly load `kCardDirty` as an immediate value).
__ movb(Address(temp, card, TIMES_1, 0),
X86ManagedRegister::FromCpuRegister(card).AsByteRegister());
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&is_null);
}
}
@@ -5824,7 +5874,9 @@
}
}
-void LocationsBuilderX86::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
+void LocationsBuilderX86::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations =
@@ -5861,10 +5913,13 @@
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // May be used for reference poisoning too.
- // Ensure the card is in a byte register.
- locations->AddTemp(Location::RegisterLocation(ECX));
+ if (write_barrier_kind != WriteBarrierKind::kDontEmit) {
+ locations->AddTemp(Location::RequiresRegister());
+ // Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ } else if (kPoisonHeapReferences) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
}
}
@@ -5875,7 +5930,8 @@
Address field_addr,
Register base,
bool is_volatile,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
LocationSummary* locations = instruction->GetLocations();
Location value = locations->InAt(value_index);
bool needs_write_barrier =
@@ -5988,10 +6044,15 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
- if (needs_write_barrier) {
+ if (needs_write_barrier && write_barrier_kind != WriteBarrierKind::kDontEmit) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>(), value_can_be_null);
+ codegen_->MarkGCCard(
+ temp,
+ card,
+ base,
+ value.AsRegister<Register>(),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_volatile) {
@@ -6001,7 +6062,8 @@
void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -6026,7 +6088,8 @@
field_addr,
base,
is_volatile,
- value_can_be_null);
+ value_can_be_null,
+ write_barrier_kind);
if (is_predicated) {
__ Bind(&pred_is_null);
@@ -6042,19 +6105,25 @@
}
void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86::VisitPredicatedInstanceFieldGet(
@@ -6315,10 +6384,12 @@
locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
}
if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
- // Ensure the card is in a byte register.
- locations->AddTemp(Location::RegisterLocation(ECX));
+ // Used by reference poisoning or emitting write barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ // Only used when emitting a write barrier. Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ }
}
}
@@ -6435,9 +6506,16 @@
}
}
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(
- temp, card, array, value.AsRegister<Register>(), /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ Register card = locations->GetTemp(1).AsRegister<Register>();
+ codegen_->MarkGCCard(temp,
+ card,
+ array,
+ value.AsRegister<Register>(),
+ /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsLinked());
@@ -7233,12 +7311,6 @@
void InstructionCodeGeneratorX86::GenerateClassInitializationCheck(
SlowPathCode* slow_path, Register class_reg) {
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- const size_t status_byte_offset =
- mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
-
__ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value));
__ j(kBelow, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 75c5ceb..9f09e17 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -19,6 +19,7 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "code_generator.h"
#include "dex/dex_file_types.h"
#include "driver/compiler_options.h"
@@ -26,7 +27,7 @@
#include "parallel_move_resolver.h"
#include "utils/x86/assembler_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// Use a local definition to prevent copying mistakes.
@@ -196,7 +197,9 @@
void HandleInvoke(HInvoke* invoke);
void HandleCondition(HCondition* condition);
void HandleShift(HBinaryOperation* instruction);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
bool CpuHasAvxFeatureFlag();
bool CpuHasAvx2FeatureFlag();
@@ -249,7 +252,8 @@
Address field_addr,
Register base,
bool is_volatile,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
private:
// Generate code for the given suspend check. If not null, `successor`
@@ -279,7 +283,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
// Generate a heap reference load using one register `out`:
@@ -519,11 +524,8 @@
void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) override;
// Emit a write barrier.
- void MarkGCCard(Register temp,
- Register card,
- Register object,
- Register value,
- bool value_can_be_null);
+ void MarkGCCard(
+ Register temp, Register card, Register object, Register value, bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index d74cb01..2d7dc44 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -21,7 +21,6 @@
#include "class_root-inl.h"
#include "class_table.h"
#include "code_generator_utils.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "gc/space/image_space.h"
@@ -37,6 +36,7 @@
#include "mirror/class-inl.h"
#include "mirror/object_reference.h"
#include "mirror/var_handle.h"
+#include "optimizing/nodes.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "utils/assembler.h"
@@ -45,7 +45,7 @@
#include "utils/x86_64/constants_x86_64.h"
#include "utils/x86_64/managed_register_x86_64.h"
-namespace art {
+namespace art HIDDEN {
template<class MirrorType>
class GcRoot;
@@ -986,6 +986,10 @@
(instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ // Load FrameSize to pass to the exit hook.
+ __ movq(CpuRegister(R8), Immediate(codegen->GetFrameSize()));
+ }
x86_64_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ jmp(GetExitLabel());
@@ -1561,9 +1565,22 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathX86_64(instruction);
codegen_->AddSlowPath(slow_path);
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ __ cmpl(Address(CpuRegister(RSP), codegen_->GetStackOffsetOfShouldDeoptimizeFlag()),
+ Immediate(0));
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ }
+
uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
- __ movq(CpuRegister(TMP), Immediate(address + offset));
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset()
+ : instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
+ __ movq(CpuRegister(TMP), Immediate(address + offset.Int32Value()));
__ cmpb(Address(CpuRegister(TMP), 0), Immediate(0));
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -1653,6 +1670,44 @@
void CodeGeneratorX86_64::GenerateFrameEntry() {
__ cfi().SetCurrentCFAOffset(kX86_64WordSize); // return address
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ NearLabel resolution;
+ // Check if we're visibly initialized.
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ movl(CpuRegister(TMP),
+ Address(CpuRegister(kMethodRegisterArgument),
+ ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ cmpb(Address(CpuRegister(TMP), status_byte_offset),
+ Immediate(shifted_visibly_initialized_value));
+ __ j(kAboveEqual, &frame_entry_label_);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ cmpb(Address(CpuRegister(TMP), status_byte_offset), Immediate(shifted_initializing_value));
+ __ j(kBelow, &resolution);
+
+ __ movl(CpuRegister(TMP),
+ Address(CpuRegister(TMP), mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ gs()->cmpl(
+ CpuRegister(TMP),
+ Address::Absolute(Thread::TidOffset<kX86_64PointerSize>().Int32Value(), /*no_rip=*/ true));
+ __ j(kEqual, &frame_entry_label_);
+ __ Bind(&resolution);
+
+ // Jump to the resolution stub.
+ ThreadOffset64 entrypoint_offset =
+ GetThreadOffset<kX86_64PointerSize>(kQuickQuickResolutionTrampoline);
+ __ gs()->jmp(Address::Absolute(entrypoint_offset, /*no_rip=*/ true));
+ }
+
__ Bind(&frame_entry_label_);
bool skip_overflow_check = IsLeafMethod()
&& !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
@@ -2274,12 +2329,12 @@
}
}
-void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderX86_64::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorX86_64::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorX86_64::IncreaseFrame(size_t adjustment) {
@@ -5119,6 +5174,9 @@
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
}
}
+
+ // TODO(solanes): We could reduce the temp usage but it requires some non-trivial refactoring of
+ // InstructionCodeGeneratorX86_64::HandleFieldSet.
if (needs_write_barrier) {
// Temporary registers for the write barrier.
locations->AddTemp(Location::RequiresRegister());
@@ -5180,7 +5238,8 @@
bool is_volatile,
bool is_atomic,
bool value_can_be_null,
- bool byte_swap) {
+ bool byte_swap,
+ WriteBarrierKind write_barrier_kind) {
LocationSummary* locations = instruction->GetLocations();
Location value = locations->InAt(value_index);
@@ -5298,10 +5357,16 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(value_index))) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(value_index)) &&
+ write_barrier_kind != WriteBarrierKind::kDontEmit) {
CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
CpuRegister card = locations->GetTemp(extra_temp_index).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>(), value_can_be_null);
+ codegen_->MarkGCCard(
+ temp,
+ card,
+ base,
+ value.AsRegister<CpuRegister>(),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_volatile) {
@@ -5311,7 +5376,8 @@
void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -5336,7 +5402,9 @@
base,
is_volatile,
/*is_atomic=*/ false,
- value_can_be_null);
+ value_can_be_null,
+ /*byte_swap=*/ false,
+ write_barrier_kind);
if (is_predicated) {
__ Bind(&pred_is_null);
@@ -5348,7 +5416,10 @@
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86_64::VisitPredicatedInstanceFieldGet(
@@ -5388,7 +5459,10 @@
}
void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86_64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
@@ -5619,9 +5693,12 @@
}
if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ // Used by reference poisoning or emitting write barrier.
locations->AddTemp(Location::RequiresRegister());
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ // Only used when emitting a write barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
}
@@ -5739,9 +5816,16 @@
}
}
- CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(
- temp, card, array, value.AsRegister<CpuRegister>(), /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
+ codegen_->MarkGCCard(temp,
+ card,
+ array,
+ value.AsRegister<CpuRegister>(),
+ /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsLinked());
@@ -5940,9 +6024,9 @@
CpuRegister card,
CpuRegister object,
CpuRegister value,
- bool value_can_be_null) {
+ bool emit_null_check) {
NearLabel is_null;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ testl(value, value);
__ j(kEqual, &is_null);
}
@@ -5967,7 +6051,7 @@
// of the card to mark; and 2. to load the `kCardDirty` value) saves a load
// (no need to explicitly load `kCardDirty` as an immediate value).
__ movb(Address(temp, card, TIMES_1, 0), card);
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&is_null);
}
}
@@ -6282,12 +6366,6 @@
void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck(
SlowPathCode* slow_path, CpuRegister class_reg) {
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- const size_t status_byte_offset =
- mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
-
__ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value));
__ j(kBelow, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 39a72d8..1fac62f 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -18,13 +18,14 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_X86_64_H_
#include "arch/x86_64/instruction_set_features_x86_64.h"
+#include "base/macros.h"
#include "code_generator.h"
#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/x86_64/assembler_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Use a local definition to prevent copying mistakes.
@@ -250,7 +251,8 @@
bool is_volatile,
bool is_atomic,
bool value_can_be_null,
- bool byte_swap = false);
+ bool byte_swap,
+ WriteBarrierKind write_barrier_kind);
void Bswap(Location value, DataType::Type type, CpuRegister* temp = nullptr);
@@ -273,7 +275,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type);
@@ -435,7 +438,7 @@
CpuRegister card,
CpuRegister object,
CpuRegister value,
- bool value_can_be_null);
+ bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc
index 766bb01..4267933 100644
--- a/compiler/optimizing/code_sinking.cc
+++ b/compiler/optimizing/code_sinking.cc
@@ -25,7 +25,7 @@
#include "common_dominator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
bool CodeSinking::Run() {
HBasicBlock* exit = graph_->GetExitBlock();
@@ -271,10 +271,21 @@
}
}
for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
- HInstruction* user = use.GetUser()->GetHolder();
+ HEnvironment* env = use.GetUser();
+ HInstruction* user = env->GetHolder();
if (user->GetBlock() == target_block &&
(insert_pos == nullptr || user->StrictlyDominates(insert_pos))) {
- insert_pos = user;
+ if (target_block->IsCatchBlock() && target_block->GetFirstInstruction() == user) {
+ // We can sink the instructions past the environment setting Nop. If we do that, we have to
+ // remove said instruction from the environment. Since we know that we will be sinking the
+ // instruction to this block and there are no more instructions to consider, we can safely
+ // remove it from the environment now.
+ DCHECK(target_block->GetFirstInstruction()->IsNop());
+ env->RemoveAsUserOfInput(use.GetIndex());
+ env->SetRawEnvAt(use.GetIndex(), /*instruction=*/ nullptr);
+ } else {
+ insert_pos = user;
+ }
}
}
if (insert_pos == nullptr) {
diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h
index 8eb3a52..14f62f2 100644
--- a/compiler/optimizing/code_sinking.h
+++ b/compiler/optimizing/code_sinking.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_CODE_SINKING_H_
#define ART_COMPILER_OPTIMIZING_CODE_SINKING_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization pass to move instructions into uncommon branches,
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index c0441b0..9f1f62b 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -33,7 +33,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
// Return all combinations of ISA and code generator that are executable on
// hardware, or on simulator, and that we'd like to test.
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 397e601..88551d8 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -20,6 +20,7 @@
#include "arch/arm/registers_arm.h"
#include "arch/instruction_set.h"
#include "arch/x86/registers_x86.h"
+#include "base/macros.h"
#include "code_simulator.h"
#include "code_simulator_container.h"
#include "common_compiler_test.h"
@@ -43,9 +44,9 @@
#include "code_generator_x86_64.h"
#endif
-namespace art {
+namespace art HIDDEN {
-typedef CodeGenerator* (*CreateCodegenFn)(HGraph*, const CompilerOptions&);
+using CreateCodegenFn = CodeGenerator* (*)(HGraph*, const CompilerOptions&);
class CodegenTargetConfig {
public:
@@ -254,15 +255,11 @@
Runtime* GetRuntime() override { return nullptr; }
};
CodeHolder code_holder;
- const void* code_ptr =
+ const void* method_code =
code_holder.MakeExecutable(allocator.GetMemory(), ArrayRef<const uint8_t>(), target_isa);
- typedef Expected (*fptr)();
- fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(code_ptr));
- if (target_isa == InstructionSet::kThumb2) {
- // For thumb we need the bottom bit set.
- f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1);
- }
+ using fptr = Expected (*)();
+ fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(method_code));
VerifyGeneratedCode(target_isa, f, has_result, expected);
}
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index 320915e..5f71cb9 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
#define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
+#include "base/macros.h"
#include "instruction_simplifier_shared.h"
#include "locations.h"
#include "nodes.h"
@@ -28,7 +29,7 @@
#include "aarch32/macro-assembler-aarch32.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 81c6561..18751c4 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
#define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
+#include "base/macros.h"
#include "code_generator.h"
#include "instruction_simplifier_shared.h"
#include "locations.h"
@@ -31,7 +32,7 @@
#include "aarch64/simulator-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/common_dominator.h b/compiler/optimizing/common_dominator.h
index 9f012cf..f01270e 100644
--- a/compiler/optimizing/common_dominator.h
+++ b/compiler/optimizing/common_dominator.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_DOMINATOR_H_
#define ART_COMPILER_OPTIMIZING_COMMON_DOMINATOR_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Helper class for finding common dominators of two or more blocks in a graph.
// The domination information of a graph must not be modified while there is
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 2031707..489b62d 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -16,14 +16,19 @@
#include "constant_folding.h"
-namespace art {
+#include <algorithm>
+
+#include "optimizing/data_type.h"
+#include "optimizing/nodes.h"
+
+namespace art HIDDEN {
// This visitor tries to simplify instructions that can be evaluated
// as constants.
class HConstantFoldingVisitor : public HGraphDelegateVisitor {
public:
- explicit HConstantFoldingVisitor(HGraph* graph)
- : HGraphDelegateVisitor(graph) {}
+ explicit HConstantFoldingVisitor(HGraph* graph, OptimizingCompilerStats* stats)
+ : HGraphDelegateVisitor(graph, stats) {}
private:
void VisitBasicBlock(HBasicBlock* block) override;
@@ -33,6 +38,9 @@
void VisitTypeConversion(HTypeConversion* inst) override;
void VisitDivZeroCheck(HDivZeroCheck* inst) override;
+ void VisitIf(HIf* inst) override;
+
+ void PropagateValue(HBasicBlock* starting_block, HInstruction* variable, HConstant* constant);
DISALLOW_COPY_AND_ASSIGN(HConstantFoldingVisitor);
};
@@ -69,7 +77,7 @@
bool HConstantFolding::Run() {
- HConstantFoldingVisitor visitor(graph_);
+ HConstantFoldingVisitor visitor(graph_, stats_);
// Process basic blocks in reverse post-order in the dominator tree,
// so that an instruction turned into a constant, used as input of
// another instruction, may possibly be used to turn that second
@@ -130,6 +138,137 @@
}
}
+void HConstantFoldingVisitor::PropagateValue(HBasicBlock* starting_block,
+ HInstruction* variable,
+ HConstant* constant) {
+ const bool recording_stats = stats_ != nullptr;
+ size_t uses_before = 0;
+ size_t uses_after = 0;
+ if (recording_stats) {
+ uses_before = variable->GetUses().SizeSlow();
+ }
+
+ variable->ReplaceUsesDominatedBy(
+ starting_block->GetFirstInstruction(), constant, /* strictly_dominated= */ false);
+
+ if (recording_stats) {
+ uses_after = variable->GetUses().SizeSlow();
+ DCHECK_GE(uses_after, 1u) << "we must at least have the use in the if clause.";
+ DCHECK_GE(uses_before, uses_after);
+ MaybeRecordStat(stats_, MethodCompilationStat::kPropagatedIfValue, uses_before - uses_after);
+ }
+}
+
+void HConstantFoldingVisitor::VisitIf(HIf* inst) {
+ // Consistency check: the true and false successors do not dominate each other.
+ DCHECK(!inst->IfTrueSuccessor()->Dominates(inst->IfFalseSuccessor()) &&
+ !inst->IfFalseSuccessor()->Dominates(inst->IfTrueSuccessor()));
+
+ HInstruction* if_input = inst->InputAt(0);
+
+ // Already a constant.
+ if (if_input->IsConstant()) {
+ return;
+ }
+
+ // if (variable) {
+ // SSA `variable` guaranteed to be true
+ // } else {
+ // and here false
+ // }
+ PropagateValue(inst->IfTrueSuccessor(), if_input, GetGraph()->GetIntConstant(1));
+ PropagateValue(inst->IfFalseSuccessor(), if_input, GetGraph()->GetIntConstant(0));
+
+ // If the input is a condition, we can propagate the information of the condition itself.
+ if (!if_input->IsCondition()) {
+ return;
+ }
+ HCondition* condition = if_input->AsCondition();
+
+ // We want either `==` or `!=`, since we cannot make assumptions for other conditions e.g. `>`
+ if (!condition->IsEqual() && !condition->IsNotEqual()) {
+ return;
+ }
+
+ HInstruction* left = condition->GetLeft();
+ HInstruction* right = condition->GetRight();
+
+ // We want one of them to be a constant and not the other.
+ if (left->IsConstant() == right->IsConstant()) {
+ return;
+ }
+
+ // At this point we have something like:
+ // if (variable == constant) {
+ // SSA `variable` guaranteed to be equal to constant here
+ // } else {
+ // No guarantees can be made here (usually, see boolean case below).
+ // }
+ // Similarly with variable != constant, except that we can make guarantees in the else case.
+
+ HConstant* constant = left->IsConstant() ? left->AsConstant() : right->AsConstant();
+ HInstruction* variable = left->IsConstant() ? right : left;
+
+ // Don't deal with floats/doubles since they bring a lot of edge cases e.g.
+ // if (f == 0.0f) {
+ // // f is not really guaranteed to be 0.0f. It could be -0.0f, for example
+ // }
+ if (DataType::IsFloatingPointType(variable->GetType())) {
+ return;
+ }
+ DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
+
+ // Sometimes we have an HCompare flowing into an Equals/NonEquals, which can act as a proxy. For
+ // example: `Equals(Compare(var, constant), 0)`. This is common for long, float, and double.
+ if (variable->IsCompare()) {
+ // We only care about equality comparisons so we skip if it is a less or greater comparison.
+ if (!constant->IsArithmeticZero()) {
+ return;
+ }
+
+ // Update left and right to be the ones from the HCompare.
+ left = variable->AsCompare()->GetLeft();
+ right = variable->AsCompare()->GetRight();
+
+ // Re-check that one of them to be a constant and not the other.
+ if (left->IsConstant() == right->IsConstant()) {
+ return;
+ }
+
+ constant = left->IsConstant() ? left->AsConstant() : right->AsConstant();
+ variable = left->IsConstant() ? right : left;
+
+ // Re-check floating point values.
+ if (DataType::IsFloatingPointType(variable->GetType())) {
+ return;
+ }
+ DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
+ }
+
+ // From this block forward we want to replace the SSA value. We use `starting_block` and not the
+ // `if` block as we want to update one of the branches but not the other.
+ HBasicBlock* starting_block =
+ condition->IsEqual() ? inst->IfTrueSuccessor() : inst->IfFalseSuccessor();
+
+ PropagateValue(starting_block, variable, constant);
+
+ // Special case for booleans since they have only two values so we know what to propagate in the
+ // other branch. However, sometimes our boolean values are not compared to 0 or 1. In those cases
+ // we cannot make an assumption for the `else` branch.
+ if (variable->GetType() == DataType::Type::kBool &&
+ constant->IsIntConstant() &&
+ (constant->AsIntConstant()->IsTrue() || constant->AsIntConstant()->IsFalse())) {
+ HBasicBlock* other_starting_block =
+ condition->IsEqual() ? inst->IfFalseSuccessor() : inst->IfTrueSuccessor();
+ DCHECK_NE(other_starting_block, starting_block);
+
+ HConstant* other_constant = constant->AsIntConstant()->IsTrue() ?
+ GetGraph()->GetIntConstant(0) :
+ GetGraph()->GetIntConstant(1);
+ DCHECK_NE(other_constant, constant);
+ PropagateValue(other_starting_block, variable, other_constant);
+ }
+}
void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instruction) {
DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
index 72bd95b..1a5e904 100644
--- a/compiler/optimizing/constant_folding.h
+++ b/compiler/optimizing/constant_folding.h
@@ -17,10 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_
#define ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
+#include "optimizing/optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization pass performing a simple constant-expression
@@ -39,7 +41,8 @@
*/
class HConstantFolding : public HOptimization {
public:
- HConstantFolding(HGraph* graph, const char* name) : HOptimization(graph, name) {}
+ HConstantFolding(HGraph* graph, OptimizingCompilerStats* stats, const char* name)
+ : HOptimization(graph, name, stats) {}
bool Run() override;
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 74d9d3a..7c3dae2 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -17,6 +17,8 @@
#include <functional>
#include "constant_folding.h"
+
+#include "base/macros.h"
#include "dead_code_elimination.h"
#include "driver/compiler_options.h"
#include "graph_checker.h"
@@ -25,7 +27,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the constant folding and dce tests.
@@ -58,7 +60,7 @@
std::string actual_before = printer_before.str();
EXPECT_EQ(expected_before, actual_before);
- HConstantFolding(graph_, "constant_folding").Run();
+ HConstantFolding(graph_, /* stats= */ nullptr, "constant_folding").Run();
GraphChecker graph_checker_cf(graph_);
graph_checker_cf.Run();
ASSERT_TRUE(graph_checker_cf.IsValid());
diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.cc b/compiler/optimizing/constructor_fence_redundancy_elimination.cc
index 3a1a9e0..66ec377 100644
--- a/compiler/optimizing/constructor_fence_redundancy_elimination.cc
+++ b/compiler/optimizing/constructor_fence_redundancy_elimination.cc
@@ -20,7 +20,7 @@
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
-namespace art {
+namespace art HIDDEN {
static constexpr bool kCfreLogFenceInputCount = false;
@@ -147,16 +147,6 @@
void VisitAlias(HInstruction* aliasing_inst) {
// An object is considered "published" if it becomes aliased by other instructions.
if (HasInterestingPublishTargetAsInput(aliasing_inst)) {
- // Note that constructing a "NullCheck" for new-instance, new-array,
- // or a 'this' (receiver) reference is impossible.
- //
- // If by some reason we actually encounter such a NullCheck(FenceTarget),
- // we LOG(WARNING).
- if (UNLIKELY(aliasing_inst->IsNullCheck())) {
- LOG(kIsDebugBuild ? FATAL : WARNING)
- << "Unexpected instruction: NullCheck; should not be legal in graph";
- // We then do a best-effort to handle this case.
- }
MergeCandidateFences();
}
}
diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h
index 014b342..e04b986 100644
--- a/compiler/optimizing/constructor_fence_redundancy_elimination.h
+++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_CONSTRUCTOR_FENCE_REDUNDANCY_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_CONSTRUCTOR_FENCE_REDUNDANCY_ELIMINATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/*
* Constructor Fence Redundancy Elimination (CFRE).
diff --git a/compiler/optimizing/critical_native_abi_fixup_arm.cc b/compiler/optimizing/critical_native_abi_fixup_arm.cc
index 3c4db4b..03fb26b 100644
--- a/compiler/optimizing/critical_native_abi_fixup_arm.cc
+++ b/compiler/optimizing/critical_native_abi_fixup_arm.cc
@@ -23,7 +23,7 @@
#include "scoped_thread_state_change-inl.h"
#include "well_known_classes.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Fix up FP arguments passed in core registers for call to @CriticalNative by inserting fake calls
@@ -45,9 +45,9 @@
if (DataType::IsFloatingPointType(input_type)) {
bool is_double = (input_type == DataType::Type::kFloat64);
DataType::Type converted_type = is_double ? DataType::Type::kInt64 : DataType::Type::kInt32;
- jmethodID known_method = is_double ? WellKnownClasses::java_lang_Double_doubleToRawLongBits
- : WellKnownClasses::java_lang_Float_floatToRawIntBits;
- ArtMethod* resolved_method = jni::DecodeArtMethod(known_method);
+ ArtMethod* resolved_method = is_double
+ ? WellKnownClasses::java_lang_Double_doubleToRawLongBits
+ : WellKnownClasses::java_lang_Float_floatToRawIntBits;
DCHECK(resolved_method != nullptr);
DCHECK(resolved_method->IsIntrinsic());
MethodReference target_method(nullptr, 0);
diff --git a/compiler/optimizing/critical_native_abi_fixup_arm.h b/compiler/optimizing/critical_native_abi_fixup_arm.h
index faa3c7a..c2068f5 100644
--- a/compiler/optimizing/critical_native_abi_fixup_arm.h
+++ b/compiler/optimizing/critical_native_abi_fixup_arm.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_CRITICAL_NATIVE_ABI_FIXUP_ARM_H_
#define ART_COMPILER_OPTIMIZING_CRITICAL_NATIVE_ABI_FIXUP_ARM_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class CriticalNativeAbiFixupArm : public HOptimization {
diff --git a/compiler/optimizing/data_type-inl.h b/compiler/optimizing/data_type-inl.h
index 1b33b77..bbfe904 100644
--- a/compiler/optimizing/data_type-inl.h
+++ b/compiler/optimizing/data_type-inl.h
@@ -20,7 +20,7 @@
#include "data_type.h"
#include "dex/primitive.h"
-namespace art {
+namespace art HIDDEN {
// Note: Not declared in data_type.h to avoid pulling in "primitive.h".
constexpr DataType::Type DataTypeFromPrimitive(Primitive::Type type) {
diff --git a/compiler/optimizing/data_type.cc b/compiler/optimizing/data_type.cc
index cb354f4..183cf2c 100644
--- a/compiler/optimizing/data_type.cc
+++ b/compiler/optimizing/data_type.cc
@@ -16,7 +16,7 @@
#include "data_type.h"
-namespace art {
+namespace art HIDDEN {
static const char* kTypeNames[] = {
"Reference",
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h
index ec6ca7a..b6d9519 100644
--- a/compiler/optimizing/data_type.h
+++ b/compiler/optimizing/data_type.h
@@ -22,8 +22,9 @@
#include <android-base/logging.h>
#include "base/bit_utils.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class DataType {
public:
diff --git a/compiler/optimizing/data_type_test.cc b/compiler/optimizing/data_type_test.cc
index 8fea22b..f6f614d 100644
--- a/compiler/optimizing/data_type_test.cc
+++ b/compiler/optimizing/data_type_test.cc
@@ -22,7 +22,7 @@
#include "base/macros.h"
#include "dex/primitive.h"
-namespace art {
+namespace art HIDDEN {
template <DataType::Type data_type, Primitive::Type primitive_type>
static void CheckConversion() {
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index d808f2c..5437d9b 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -16,14 +16,17 @@
#include "dead_code_elimination.h"
+#include "android-base/logging.h"
#include "base/array_ref.h"
#include "base/bit_vector-inl.h"
+#include "base/logging.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
+#include "optimizing/nodes.h"
#include "ssa_phi_elimination.h"
-namespace art {
+namespace art HIDDEN {
static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
// Use local allocator for allocating memory.
@@ -213,6 +216,9 @@
// | ...
// | instr_n
// | foo() // always throws
+// | instr_n+2
+// | ...
+// | instr_n+m
// \ goto B2
// \ /
// B2
@@ -230,11 +236,14 @@
// B2 Exit
//
// Rationale:
-// Removal of the never taken edge to B2 may expose
-// other optimization opportunities, such as code sinking.
+// Removal of the never taken edge to B2 may expose other optimization opportunities, such as code
+// sinking.
+//
+// Note: The example above is a simple one that uses a `goto` but we could end the block with an If,
+// for example.
bool HDeadCodeElimination::SimplifyAlwaysThrows() {
HBasicBlock* exit = graph_->GetExitBlock();
- if (exit == nullptr) {
+ if (!graph_->HasAlwaysThrowingInvokes() || exit == nullptr) {
return false;
}
@@ -242,55 +251,56 @@
// Order does not matter, just pick one.
for (HBasicBlock* block : graph_->GetReversePostOrder()) {
- if (block->GetTryCatchInformation() != nullptr) {
+ if (block->IsTryBlock()) {
// We don't want to perform the simplify always throws optimizations for throws inside of
- // tries since those throws might not go to the exit block. We do that by checking the
- // TryCatchInformation of the blocks.
- //
- // As a special case the `catch_block` is the first block of the catch and it has
- // TryCatchInformation. Other blocks in the catch don't have try catch information (as long as
- // they are not part of an outer try). Knowing if a `catch_block` is part of an outer try is
- // possible by checking its successors, but other restrictions of the simplify always throws
- // optimization will block `catch_block` nevertheless (e.g. only one predecessor) so it is not
- // worth the effort.
-
- // TODO(solanes): Maybe we can do a `goto catch` if inside of a try catch instead of going to
- // the exit. If we do so, we have to take into account that we should go to the nearest valid
- // catch i.e. one that would accept our exception type.
+ // tries since those throws might not go to the exit block.
continue;
}
- HInstruction* last = block->GetLastInstruction();
- HInstruction* prev = last->GetPrevious();
- if (prev == nullptr) {
- DCHECK_EQ(block->GetFirstInstruction(), block->GetLastInstruction());
- continue;
- }
-
- if (prev->AlwaysThrows() &&
- last->IsGoto() &&
- block->GetPhis().IsEmpty() &&
- block->GetPredecessors().size() == 1u) {
- HBasicBlock* pred = block->GetSinglePredecessor();
- HBasicBlock* succ = block->GetSingleSuccessor();
- // Ensure no computations are merged through throwing block.
- // This does not prevent the optimization per se, but would
- // require an elaborate clean up of the SSA graph.
- if (succ != exit &&
- !block->Dominates(pred) &&
- pred->Dominates(succ) &&
- succ->GetPredecessors().size() > 1u &&
- succ->GetPhis().IsEmpty()) {
- block->ReplaceSuccessor(succ, exit);
- rerun_dominance_and_loop_analysis = true;
- MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
- // Perform a quick follow up optimization on object != null control dependences
- // that is much cheaper to perform now than in a later phase.
- if (RemoveNonNullControlDependences(pred, block)) {
- MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
- }
+ // We iterate to find the first instruction that always throws. If two instructions always
+ // throw, the first one will throw and the second one will never be reached.
+ HInstruction* throwing_invoke = nullptr;
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ if (it.Current()->IsInvoke() && it.Current()->AsInvoke()->AlwaysThrows()) {
+ throwing_invoke = it.Current();
+ break;
}
}
+
+ if (throwing_invoke == nullptr) {
+ // No always-throwing instruction found. Continue with the rest of the blocks.
+ continue;
+ }
+
+ // If we are already pointing at the exit block we could still remove the instructions
+ // between the always throwing instruction, and the exit block. If we have no other
+ // instructions, just continue since there's nothing to do.
+ if (block->GetSuccessors().size() == 1 &&
+ block->GetSingleSuccessor() == exit &&
+ block->GetLastInstruction()->GetPrevious() == throwing_invoke) {
+ continue;
+ }
+
+ // We split the block at the throwing instruction, and the instructions after the throwing
+ // instructions will be disconnected from the graph after `block` points to the exit.
+ // `RemoveDeadBlocks` will take care of removing this new block and its instructions.
+ // Even though `SplitBefore` doesn't guarantee the graph to remain in SSA form, it is fine
+ // since we do not break it.
+ HBasicBlock* new_block = block->SplitBefore(throwing_invoke->GetNext(),
+ /* require_graph_not_in_ssa_form= */ false);
+ DCHECK_EQ(block->GetSingleSuccessor(), new_block);
+ block->ReplaceSuccessor(new_block, exit);
+
+ rerun_dominance_and_loop_analysis = true;
+ MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
+ // Perform a quick follow up optimization on object != null control dependences
+ // that is much cheaper to perform now than in a later phase.
+ // If there are multiple predecessors, none may end with a HIf as required in
+ // RemoveNonNullControlDependences because we split critical edges.
+ if (block->GetPredecessors().size() == 1u &&
+ RemoveNonNullControlDependences(block->GetSinglePredecessor(), block)) {
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
+ }
}
// We need to re-analyze the graph in order to run DCE afterwards.
@@ -303,54 +313,45 @@
return false;
}
-// Simplify the pattern:
-//
-// B1 B2 ...
-// goto goto goto
-// \ | /
-// \ | /
-// B3
-// i1 = phi(input, input)
-// (i2 = condition on i1)
-// if i1 (or i2)
-// / \
-// / \
-// B4 B5
-//
-// Into:
-//
-// B1 B2 ...
-// | | |
-// B4 B5 B?
-//
-// Note that individual edges can be redirected (for example B2->B3
-// can be redirected as B2->B5) without applying this optimization
-// to other incoming edges.
-//
-// This simplification cannot be applied to catch blocks, because
-// exception handler edges do not represent normal control flow.
-// Though in theory this could still apply to normal control flow
-// going directly to a catch block, we cannot support it at the
-// moment because the catch Phi's inputs do not correspond to the
-// catch block's predecessors, so we cannot identify which
-// predecessor corresponds to a given statically evaluated input.
-//
-// We do not apply this optimization to loop headers as this could
-// create irreducible loops. We rely on the suspend check in the
-// loop header to prevent the pattern match.
-//
-// Note that we rely on the dead code elimination to get rid of B3.
bool HDeadCodeElimination::SimplifyIfs() {
bool simplified_one_or_more_ifs = false;
bool rerun_dominance_and_loop_analysis = false;
- for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ // Iterating in PostOrder it's better for MaybeAddPhi as it can add a Phi for multiple If
+ // instructions in a chain without updating the dominator chain. The branch redirection itself can
+ // work in PostOrder or ReversePostOrder without issues.
+ for (HBasicBlock* block : graph_->GetPostOrder()) {
+ if (block->IsCatchBlock()) {
+ // This simplification cannot be applied to catch blocks, because exception handler edges do
+ // not represent normal control flow. Though in theory this could still apply to normal
+ // control flow going directly to a catch block, we cannot support it at the moment because
+ // the catch Phi's inputs do not correspond to the catch block's predecessors, so we cannot
+ // identify which predecessor corresponds to a given statically evaluated input.
+ continue;
+ }
+
HInstruction* last = block->GetLastInstruction();
- HInstruction* first = block->GetFirstInstruction();
- if (!block->IsCatchBlock() &&
- last->IsIf() &&
- block->HasSinglePhi() &&
+ if (!last->IsIf()) {
+ continue;
+ }
+
+ if (block->IsLoopHeader()) {
+ // We do not apply this optimization to loop headers as this could create irreducible loops.
+ continue;
+ }
+
+ // We will add a Phi which allows the simplification to take place in cases where it wouldn't.
+ MaybeAddPhi(block);
+
+ // TODO(solanes): Investigate support for multiple phis in `block`. We can potentially "push
+ // downwards" existing Phis into the true/false branches. For example, let's say we have another
+ // Phi: Phi(x1,x2,x3,x4,x5,x6). This could turn into Phi(x1,x2) in the true branch, Phi(x3,x4)
+ // in the false branch, and remain as Phi(x5,x6) in `block` (for edges that we couldn't
+ // redirect). We might even be able to remove some phis altogether as they will have only one
+ // value.
+ if (block->HasSinglePhi() &&
block->GetFirstPhi()->HasOnlyOneNonEnvironmentUse()) {
+ HInstruction* first = block->GetFirstInstruction();
bool has_only_phi_and_if = (last == first) && (last->InputAt(0) == block->GetFirstPhi());
bool has_only_phi_condition_and_if =
!has_only_phi_and_if &&
@@ -361,7 +362,6 @@
first->HasOnlyOneNonEnvironmentUse();
if (has_only_phi_and_if || has_only_phi_condition_and_if) {
- DCHECK(!block->IsLoopHeader());
HPhi* phi = block->GetFirstPhi()->AsPhi();
bool phi_input_is_left = (first->InputAt(0) == phi);
@@ -446,6 +446,125 @@
return simplified_one_or_more_ifs;
}
+void HDeadCodeElimination::MaybeAddPhi(HBasicBlock* block) {
+ DCHECK(block->GetLastInstruction()->IsIf());
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ if (if_instruction->InputAt(0)->IsConstant()) {
+ // Constant values are handled in RemoveDeadBlocks.
+ return;
+ }
+
+ if (block->GetNumberOfPredecessors() < 2u) {
+ // Nothing to redirect.
+ return;
+ }
+
+ if (!block->GetPhis().IsEmpty()) {
+ // SimplifyIf doesn't currently work with multiple phis. Adding a phi here won't help that
+ // optimization.
+ return;
+ }
+
+ HBasicBlock* dominator = block->GetDominator();
+ if (!dominator->EndsWithIf()) {
+ return;
+ }
+
+ HInstruction* input = if_instruction->InputAt(0);
+ HInstruction* dominator_input = dominator->GetLastInstruction()->AsIf()->InputAt(0);
+ const bool same_input = dominator_input == input;
+ if (!same_input) {
+ // Try to see if the dominator has the opposite input (e.g. if(cond) and if(!cond)). If that's
+ // the case, we can perform the optimization with the false and true branches reversed.
+ if (!dominator_input->IsCondition() || !input->IsCondition()) {
+ return;
+ }
+
+ HCondition* block_cond = input->AsCondition();
+ HCondition* dominator_cond = dominator_input->AsCondition();
+
+ if (block_cond->GetLeft() != dominator_cond->GetLeft() ||
+ block_cond->GetRight() != dominator_cond->GetRight() ||
+ block_cond->GetOppositeCondition() != dominator_cond->GetCondition()) {
+ return;
+ }
+ }
+
+ if (kIsDebugBuild) {
+ // `block`'s successors should have only one predecessor. Otherwise, we have a critical edge in
+ // the graph.
+ for (HBasicBlock* succ : block->GetSuccessors()) {
+ DCHECK_EQ(succ->GetNumberOfPredecessors(), 1u);
+ }
+ }
+
+ const size_t pred_size = block->GetNumberOfPredecessors();
+ HPhi* new_phi = new (graph_->GetAllocator())
+ HPhi(graph_->GetAllocator(), kNoRegNumber, pred_size, DataType::Type::kInt32);
+
+ for (size_t index = 0; index < pred_size; index++) {
+ HBasicBlock* pred = block->GetPredecessors()[index];
+ const bool dominated_by_true =
+ dominator->GetLastInstruction()->AsIf()->IfTrueSuccessor()->Dominates(pred);
+ const bool dominated_by_false =
+ dominator->GetLastInstruction()->AsIf()->IfFalseSuccessor()->Dominates(pred);
+ if (dominated_by_true == dominated_by_false) {
+ // In this case, we can't know if we are coming from the true branch, or the false branch. It
+ // happens in cases like:
+ // 1 (outer if)
+ // / \
+ // 2 3 (inner if)
+ // | / \
+ // | 4 5
+ // \/ |
+ // 6 |
+ // \ |
+ // 7 (has the same if(cond) as 1)
+ // |
+ // 8
+ // `7` (which would be `block` in this example), and `6` will come from both the true path and
+ // the false path of `1`. We bumped into something similar in SelectGenerator. See
+ // HSelectGenerator::TryFixupDoubleDiamondPattern.
+ // TODO(solanes): Figure out if we can fix up the graph into a double diamond in a generic way
+ // so that DeadCodeElimination and SelectGenerator can take advantage of it.
+
+ if (!same_input) {
+ // `1` and `7` having the opposite condition is a case we are missing. We could potentially
+ // add a BooleanNot instruction to be able to add the Phi, but it seems like overkill since
+ // this case is not that common.
+ return;
+ }
+
+ // The Phi will have `0`, `1`, and `cond` as inputs. If SimplifyIf redirects 0s and 1s, we
+ // will end up with Phi(cond,...,cond) which will be replaced by `cond`. Effectively, we will
+ // redirect edges that we are able to redirect and the rest will remain as before (i.e. we
+ // won't have an extra Phi).
+ new_phi->SetRawInputAt(index, input);
+ } else {
+ // Redirect to either the true branch (1), or the false branch (0).
+ // Given that `dominated_by_true` is the exact opposite of `dominated_by_false`,
+ // `(same_input && dominated_by_true) || (!same_input && dominated_by_false)` is equivalent to
+ // `same_input == dominated_by_true`.
+ new_phi->SetRawInputAt(
+ index,
+ same_input == dominated_by_true ? graph_->GetIntConstant(1) : graph_->GetIntConstant(0));
+ }
+ }
+
+ block->AddPhi(new_phi);
+ if_instruction->ReplaceInput(new_phi, 0);
+
+ // Remove the old input now, if possible. This allows the branch redirection in SimplifyIf to
+ // work without waiting for another pass of DCE.
+ if (input->IsDeadAndRemovable()) {
+ DCHECK(!same_input)
+ << " if both blocks have the same condition, it shouldn't be dead and removable since the "
+ << "dominator block's If instruction would be using that condition.";
+ input->GetBlock()->RemoveInstruction(input);
+ }
+ MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyIfAddedPhi);
+}
+
void HDeadCodeElimination::ConnectSuccessiveBlocks() {
// Order does not matter. Skip the entry block by starting at index 1 in reverse post order.
for (size_t i = 1u, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
@@ -466,7 +585,192 @@
}
}
-bool HDeadCodeElimination::RemoveDeadBlocks() {
+struct HDeadCodeElimination::TryBelongingInformation {
+ explicit TryBelongingInformation(ScopedArenaAllocator* allocator)
+ : blocks_in_try(allocator->Adapter(kArenaAllocDCE)),
+ coalesced_try_entries(allocator->Adapter(kArenaAllocDCE)) {}
+
+ // Which blocks belong in the try.
+ ScopedArenaSet<HBasicBlock*> blocks_in_try;
+ // Which other try entries are referencing this same try.
+ ScopedArenaSet<HBasicBlock*> coalesced_try_entries;
+};
+
+bool HDeadCodeElimination::CanPerformTryRemoval(const TryBelongingInformation& try_belonging_info) {
+ for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ if (it.Current()->CanThrow()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void HDeadCodeElimination::DisconnectHandlersAndUpdateTryBoundary(
+ HBasicBlock* block,
+ /* out */ bool* any_block_in_loop) {
+ if (block->IsInLoop()) {
+ *any_block_in_loop = true;
+ }
+
+ // Disconnect the handlers.
+ while (block->GetSuccessors().size() > 1) {
+ HBasicBlock* handler = block->GetSuccessors()[1];
+ DCHECK(handler->IsCatchBlock());
+ block->RemoveSuccessor(handler);
+ handler->RemovePredecessor(block);
+ if (handler->IsInLoop()) {
+ *any_block_in_loop = true;
+ }
+ }
+
+ // Change TryBoundary to Goto.
+ DCHECK(block->EndsWithTryBoundary());
+ HInstruction* last = block->GetLastInstruction();
+ block->RemoveInstruction(last);
+ block->AddInstruction(new (graph_->GetAllocator()) HGoto(last->GetDexPc()));
+ DCHECK_EQ(block->GetSuccessors().size(), 1u);
+}
+
+void HDeadCodeElimination::RemoveTry(HBasicBlock* try_entry,
+ const TryBelongingInformation& try_belonging_info,
+ /* out */ bool* any_block_in_loop) {
+ // Update all try entries.
+ DCHECK(try_entry->EndsWithTryBoundary());
+ DCHECK(try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
+ DisconnectHandlersAndUpdateTryBoundary(try_entry, any_block_in_loop);
+
+ for (HBasicBlock* other_try_entry : try_belonging_info.coalesced_try_entries) {
+ DCHECK(other_try_entry->EndsWithTryBoundary());
+ DCHECK(other_try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
+ DisconnectHandlersAndUpdateTryBoundary(other_try_entry, any_block_in_loop);
+ }
+
+ // Update the blocks in the try.
+ for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
+ // Update the try catch information since now the try doesn't exist.
+ block->SetTryCatchInformation(nullptr);
+ if (block->IsInLoop()) {
+ *any_block_in_loop = true;
+ }
+
+ if (block->EndsWithTryBoundary()) {
+ // Try exits.
+ DCHECK(!block->GetLastInstruction()->AsTryBoundary()->IsEntry());
+ DisconnectHandlersAndUpdateTryBoundary(block, any_block_in_loop);
+
+ if (block->GetSingleSuccessor()->IsExitBlock()) {
+ // `block` used to be a single exit TryBoundary that got turned into a Goto. It
+ // is now pointing to the exit which we don't allow. To fix it, we disconnect
+ // `block` from its predecessor and RemoveDeadBlocks will remove it from the
+ // graph.
+ DCHECK(block->IsSingleGoto());
+ HBasicBlock* predecessor = block->GetSinglePredecessor();
+ predecessor->ReplaceSuccessor(block, graph_->GetExitBlock());
+
+ if (!block->GetDominatedBlocks().empty()) {
+ // Update domination tree if `block` dominates a block to keep the graph consistent.
+ DCHECK_EQ(block->GetDominatedBlocks().size(), 1u);
+ DCHECK_EQ(graph_->GetExitBlock()->GetDominator(), block);
+ predecessor->AddDominatedBlock(graph_->GetExitBlock());
+ graph_->GetExitBlock()->SetDominator(predecessor);
+ block->RemoveDominatedBlock(graph_->GetExitBlock());
+ }
+ }
+ }
+ }
+}
+
+bool HDeadCodeElimination::RemoveUnneededTries() {
+ if (!graph_->HasTryCatch()) {
+ return false;
+ }
+
+ // Use local allocator for allocating memory.
+ ScopedArenaAllocator allocator(graph_->GetArenaStack());
+
+ // Collect which blocks are part of which try.
+ std::unordered_map<HBasicBlock*, TryBelongingInformation> tries;
+ for (HBasicBlock* block : graph_->GetReversePostOrderSkipEntryBlock()) {
+ if (block->IsTryBlock()) {
+ HBasicBlock* key = block->GetTryCatchInformation()->GetTryEntry().GetBlock();
+ auto it = tries.find(key);
+ if (it == tries.end()) {
+ it = tries.insert({key, TryBelongingInformation(&allocator)}).first;
+ }
+ it->second.blocks_in_try.insert(block);
+ }
+ }
+
+ // Deduplicate the tries which have different try entries but they are really the same try.
+ for (auto it = tries.begin(); it != tries.end(); it++) {
+ DCHECK(it->first->EndsWithTryBoundary());
+ HTryBoundary* try_boundary = it->first->GetLastInstruction()->AsTryBoundary();
+ for (auto other_it = next(it); other_it != tries.end(); /*other_it++ in the loop*/) {
+ DCHECK(other_it->first->EndsWithTryBoundary());
+ HTryBoundary* other_try_boundary = other_it->first->GetLastInstruction()->AsTryBoundary();
+ if (try_boundary->HasSameExceptionHandlersAs(*other_try_boundary)) {
+ // Merge the entries as they are really the same one.
+ // Block merging.
+ it->second.blocks_in_try.insert(other_it->second.blocks_in_try.begin(),
+ other_it->second.blocks_in_try.end());
+
+ // Add the coalesced try entry to update it too.
+ it->second.coalesced_try_entries.insert(other_it->first);
+
+ // Erase the other entry.
+ other_it = tries.erase(other_it);
+ } else {
+ other_it++;
+ }
+ }
+ }
+
+ size_t removed_tries = 0;
+ bool any_block_in_loop = false;
+
+ // Check which tries contain throwing instructions.
+ for (const auto& entry : tries) {
+ if (CanPerformTryRemoval(entry.second)) {
+ ++removed_tries;
+ RemoveTry(entry.first, entry.second, &any_block_in_loop);
+ }
+ }
+
+ if (removed_tries != 0) {
+ // We want to:
+ // 1) Update the dominance information
+ // 2) Remove catch block subtrees, if they are now unreachable.
+ // If we run the dominance recomputation without removing the code, those catch blocks will
+ // not be part of the post order and won't be removed. If we don't run the dominance
+ // recomputation, we risk RemoveDeadBlocks not running it and leaving the graph in an
+ // inconsistent state. So, what we can do is run RemoveDeadBlocks and force a recomputation.
+ // Note that we are not guaranteed to remove a catch block if we have nested try blocks:
+ //
+ // try {
+ // ... nothing can throw. TryBoundary A ...
+ // try {
+ // ... can throw. TryBoundary B...
+ // } catch (Error e) {}
+ // } catch (Exception e) {}
+ //
+ // In the example above, we can remove the TryBoundary A but the Exception catch cannot be
+ // removed as the TryBoundary B might still throw into that catch. TryBoundary A and B don't get
+ // coalesced since they have different catch handlers.
+
+ RemoveDeadBlocks(/* force_recomputation= */ true, any_block_in_loop);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedTry, removed_tries);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool HDeadCodeElimination::RemoveDeadBlocks(bool force_recomputation,
+ bool force_loop_recomputation) {
+ DCHECK_IMPLIES(force_loop_recomputation, force_recomputation);
+
// Use local allocator for allocating memory.
ScopedArenaAllocator allocator(graph_->GetArenaStack());
@@ -495,8 +799,8 @@
// If we removed at least one block, we need to recompute the full
// dominator tree and try block membership.
- if (removed_one_or_more_blocks) {
- if (rerun_dominance_and_loop_analysis) {
+ if (removed_one_or_more_blocks || force_recomputation) {
+ if (rerun_dominance_and_loop_analysis || force_loop_recomputation) {
graph_->ClearLoopInformation();
graph_->ClearDominanceInformation();
graph_->BuildDominatorTree();
@@ -530,6 +834,33 @@
}
}
+void HDeadCodeElimination::UpdateGraphFlags() {
+ bool has_monitor_operations = false;
+ bool has_simd = false;
+ bool has_bounds_checks = false;
+ bool has_always_throwing_invokes = false;
+
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsMonitorOperation()) {
+ has_monitor_operations = true;
+ } else if (instruction->IsVecOperation()) {
+ has_simd = true;
+ } else if (instruction->IsBoundsCheck()) {
+ has_bounds_checks = true;
+ } else if (instruction->IsInvoke() && instruction->AsInvoke()->AlwaysThrows()) {
+ has_always_throwing_invokes = true;
+ }
+ }
+ }
+
+ graph_->SetHasMonitorOperations(has_monitor_operations);
+ graph_->SetHasSIMD(has_simd);
+ graph_->SetHasBoundsChecks(has_bounds_checks);
+ graph_->SetHasAlwaysThrowingInvokes(has_always_throwing_invokes);
+}
+
bool HDeadCodeElimination::Run() {
// Do not eliminate dead blocks if the graph has irreducible loops. We could
// support it, but that would require changes in our loop representation to handle
@@ -541,6 +872,11 @@
did_any_simplification |= SimplifyAlwaysThrows();
did_any_simplification |= SimplifyIfs();
did_any_simplification |= RemoveDeadBlocks();
+ // We call RemoveDeadBlocks before RemoveUnneededTries to remove the dead blocks from the
+ // previous optimizations. Otherwise, we might detect that a try has throwing instructions but
+ // they are actually dead code. RemoveUnneededTryBoundary will call RemoveDeadBlocks again if
+ // needed.
+ did_any_simplification |= RemoveUnneededTries();
if (did_any_simplification) {
// Connect successive blocks created by dead branches.
ConnectSuccessiveBlocks();
@@ -548,6 +884,7 @@
}
SsaRedundantPhiElimination(graph_).Run();
RemoveDeadInstructions();
+ UpdateGraphFlags();
return true;
}
diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h
index 799721a..ddd01f7 100644
--- a/compiler/optimizing/dead_code_elimination.h
+++ b/compiler/optimizing/dead_code_elimination.h
@@ -17,11 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
#include "optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization pass performing dead code elimination (removal of
@@ -39,11 +40,87 @@
private:
void MaybeRecordDeadBlock(HBasicBlock* block);
void MaybeRecordSimplifyIf();
- bool RemoveDeadBlocks();
+ // If `force_recomputation` is true, we will recompute the dominance information even when we
+ // didn't delete any blocks. `force_loop_recomputation` is similar but it also forces the loop
+ // information recomputation.
+ bool RemoveDeadBlocks(bool force_recomputation = false, bool force_loop_recomputation = false);
void RemoveDeadInstructions();
bool SimplifyAlwaysThrows();
+ // Simplify the pattern:
+ //
+ // B1 B2 ...
+ // goto goto goto
+ // \ | /
+ // \ | /
+ // B3
+ // i1 = phi(input, input)
+ // (i2 = condition on i1)
+ // if i1 (or i2)
+ // / \
+ // / \
+ // B4 B5
+ //
+ // Into:
+ //
+ // B1 B2 ...
+ // | | |
+ // B4 B5 B?
+ //
+ // Note that individual edges can be redirected (for example B2->B3
+ // can be redirected as B2->B5) without applying this optimization
+ // to other incoming edges.
+ //
+ // Note that we rely on the dead code elimination to get rid of B3.
bool SimplifyIfs();
void ConnectSuccessiveBlocks();
+ // Updates the graph flags related to instructions (e.g. HasSIMD()) since we may have eliminated
+ // the relevant instructions. There's no need to update `SetHasTryCatch` since we do that in
+ // `ComputeTryBlockInformation`. Similarly with `HasLoops` and `HasIrreducibleLoops`: They are
+ // cleared in `ClearLoopInformation` and then set as true as part of `HLoopInformation::Populate`,
+ // if needed.
+ void UpdateGraphFlags();
+
+ // Helper struct to eliminate tries.
+ struct TryBelongingInformation;
+ // Disconnects `block`'s handlers and update its `TryBoundary` instruction to a `Goto`.
+ // Sets `any_block_in_loop` to true if any block is currently a loop to later update the loop
+ // information if needed.
+ void DisconnectHandlersAndUpdateTryBoundary(HBasicBlock* block,
+ /* out */ bool* any_block_in_loop);
+ // Returns true iff the try doesn't contain throwing instructions.
+ bool CanPerformTryRemoval(const TryBelongingInformation& try_belonging_info);
+ // Removes the try by disconnecting all try entries and exits from their handlers. Also updates
+ // the graph in the case that a `TryBoundary` instruction of kind `exit` has the Exit block as
+ // its successor.
+ void RemoveTry(HBasicBlock* try_entry,
+ const TryBelongingInformation& try_belonging_info,
+ bool* any_block_in_loop);
+ // Checks which tries (if any) are currently in the graph, coalesces the different try entries
+ // that are referencing the same try, and removes the tries which don't contain any throwing
+ // instructions.
+ bool RemoveUnneededTries();
+
+ // Adds a phi in `block`, if `block` and its dominator have the same (or opposite) condition.
+ // For example it turns:
+ // if(cond)
+ // / \
+ // B1 B2
+ // \ /
+ // if(cond)
+ // / \
+ // B3 B4
+ //
+ // into:
+ // if(cond)
+ // / \
+ // B1 B2
+ // \ /
+ // if(Phi(1, 0))
+ // / \
+ // B3 B4
+ //
+ // Following this, SimplifyIfs is able to connect B1->B3 and B2->B4 effectively skipping an if.
+ void MaybeAddPhi(HBasicBlock* block);
DISALLOW_COPY_AND_ASSIGN(HDeadCodeElimination);
};
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index f5cd4dc..fcf4ca3 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -16,6 +16,7 @@
#include "dead_code_elimination.h"
+#include "base/macros.h"
#include "driver/compiler_options.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
@@ -23,7 +24,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class DeadCodeEliminationTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc
index 1d72ba1..d3b8cb1 100644
--- a/compiler/optimizing/dominator_test.cc
+++ b/compiler/optimizing/dominator_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_instruction.h"
#include "nodes.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class OptimizerTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc
index 617833c..cebe94f 100644
--- a/compiler/optimizing/escape.cc
+++ b/compiler/optimizing/escape.cc
@@ -18,7 +18,7 @@
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void VisitEscapes(HInstruction* reference, EscapeVisitor& escape_visitor) {
// References not allocated in the method are intrinsically escaped.
diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h
index 5402cb1..3b284fb 100644
--- a/compiler/optimizing/escape.h
+++ b/compiler/optimizing/escape.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_OPTIMIZING_ESCAPE_H_
#define ART_COMPILER_OPTIMIZING_ESCAPE_H_
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HInstruction;
diff --git a/compiler/optimizing/execution_subgraph.cc b/compiler/optimizing/execution_subgraph.cc
index 66fdfcd..06aabbe 100644
--- a/compiler/optimizing/execution_subgraph.cc
+++ b/compiler/optimizing/execution_subgraph.cc
@@ -26,7 +26,7 @@
#include "base/scoped_arena_allocator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
ExecutionSubgraph::ExecutionSubgraph(HGraph* graph, ScopedArenaAllocator* allocator)
: graph_(graph),
diff --git a/compiler/optimizing/execution_subgraph.h b/compiler/optimizing/execution_subgraph.h
index 7d2a660..5ddf17d 100644
--- a/compiler/optimizing/execution_subgraph.h
+++ b/compiler/optimizing/execution_subgraph.h
@@ -27,6 +27,7 @@
#include "base/bit_vector-inl.h"
#include "base/globals.h"
#include "base/iteration_range.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
@@ -34,7 +35,7 @@
#include "base/transform_iterator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Helper for transforming blocks to block_ids.
class BlockToBlockIdTransformer {
diff --git a/compiler/optimizing/execution_subgraph_test.cc b/compiler/optimizing/execution_subgraph_test.cc
index 74c243b..921ef05 100644
--- a/compiler/optimizing/execution_subgraph_test.cc
+++ b/compiler/optimizing/execution_subgraph_test.cc
@@ -37,7 +37,7 @@
#include "optimizing_unit_test.h"
#include "scoped_thread_state_change.h"
-namespace art {
+namespace art HIDDEN {
using BlockSet = std::unordered_set<const HBasicBlock*>;
diff --git a/compiler/optimizing/execution_subgraph_test.h b/compiler/optimizing/execution_subgraph_test.h
index 13cb2bc..cee105a 100644
--- a/compiler/optimizing/execution_subgraph_test.h
+++ b/compiler/optimizing/execution_subgraph_test.h
@@ -19,7 +19,9 @@
#include "android-base/macros.h"
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HGraph;
class ExecutionSubgraph;
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index 75b8e96..8d8c3a1 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction.h"
@@ -25,7 +26,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class FindLoopsTest : public OptimizingUnitTest {};
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index d1769ce..ca29a09 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -32,7 +32,7 @@
#include "scoped_thread_state_change-inl.h"
#include "subtype_check.h"
-namespace art {
+namespace art HIDDEN {
using android::base::StringPrintf;
@@ -80,9 +80,91 @@
// as the latter might visit dead blocks removed by the dominator
// computation.
VisitReversePostOrder();
+ CheckGraphFlags();
return current_size;
}
+void GraphChecker::VisitReversePostOrder() {
+ for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
+ if (block->IsInLoop()) {
+ flag_info_.seen_loop = true;
+ if (block->GetLoopInformation()->IsIrreducible()) {
+ flag_info_.seen_irreducible_loop = true;
+ }
+ }
+
+ VisitBasicBlock(block);
+ }
+}
+
+static const char* StrBool(bool val) {
+ return val ? "true" : "false";
+}
+
+void GraphChecker::CheckGraphFlags() {
+ if (GetGraph()->HasMonitorOperations() != flag_info_.seen_monitor_operation) {
+ AddError(
+ StringPrintf("Flag mismatch: HasMonitorOperations() (%s) should be equal to "
+ "flag_info_.seen_monitor_operation (%s)",
+ StrBool(GetGraph()->HasMonitorOperations()),
+ StrBool(flag_info_.seen_monitor_operation)));
+ }
+
+ if (GetGraph()->HasTryCatch() != flag_info_.seen_try_boundary) {
+ AddError(
+ StringPrintf("Flag mismatch: HasTryCatch() (%s) should be equal to "
+ "flag_info_.seen_try_boundary (%s)",
+ StrBool(GetGraph()->HasTryCatch()),
+ StrBool(flag_info_.seen_try_boundary)));
+ }
+
+ if (GetGraph()->HasLoops() != flag_info_.seen_loop) {
+ AddError(
+ StringPrintf("Flag mismatch: HasLoops() (%s) should be equal to "
+ "flag_info_.seen_loop (%s)",
+ StrBool(GetGraph()->HasLoops()),
+ StrBool(flag_info_.seen_loop)));
+ }
+
+ if (GetGraph()->HasIrreducibleLoops() && !GetGraph()->HasLoops()) {
+ AddError(StringPrintf("Flag mismatch: HasIrreducibleLoops() (%s) implies HasLoops() (%s)",
+ StrBool(GetGraph()->HasIrreducibleLoops()),
+ StrBool(GetGraph()->HasLoops())));
+ }
+
+ if (GetGraph()->HasIrreducibleLoops() != flag_info_.seen_irreducible_loop) {
+ AddError(
+ StringPrintf("Flag mismatch: HasIrreducibleLoops() (%s) should be equal to "
+ "flag_info_.seen_irreducible_loop (%s)",
+ StrBool(GetGraph()->HasIrreducibleLoops()),
+ StrBool(flag_info_.seen_irreducible_loop)));
+ }
+
+ if (GetGraph()->HasSIMD() != flag_info_.seen_SIMD) {
+ AddError(
+ StringPrintf("Flag mismatch: HasSIMD() (%s) should be equal to "
+ "flag_info_.seen_SIMD (%s)",
+ StrBool(GetGraph()->HasSIMD()),
+ StrBool(flag_info_.seen_SIMD)));
+ }
+
+ if (GetGraph()->HasBoundsChecks() != flag_info_.seen_bounds_checks) {
+ AddError(
+ StringPrintf("Flag mismatch: HasBoundsChecks() (%s) should be equal to "
+ "flag_info_.seen_bounds_checks (%s)",
+ StrBool(GetGraph()->HasBoundsChecks()),
+ StrBool(flag_info_.seen_bounds_checks)));
+ }
+
+ if (GetGraph()->HasAlwaysThrowingInvokes() != flag_info_.seen_always_throwing_invokes) {
+ AddError(
+ StringPrintf("Flag mismatch: HasAlwaysThrowingInvokes() (%s) should be equal to "
+ "flag_info_.seen_always_throwing_invokes (%s)",
+ StrBool(GetGraph()->HasAlwaysThrowingInvokes()),
+ StrBool(flag_info_.seen_always_throwing_invokes)));
+ }
+}
+
void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
current_block_ = block;
@@ -159,6 +241,24 @@
}
}
+ // Make sure the first instruction of a catch block is always a Nop that emits an environment.
+ if (block->IsCatchBlock()) {
+ if (!block->GetFirstInstruction()->IsNop()) {
+ AddError(StringPrintf("Block %d doesn't have a Nop as its first instruction.",
+ current_block_->GetBlockId()));
+ } else {
+ HNop* nop = block->GetFirstInstruction()->AsNop();
+ if (!nop->NeedsEnvironment()) {
+ AddError(
+ StringPrintf("%s:%d is a Nop and the first instruction of block %d, but it doesn't "
+ "need an environment.",
+ nop->DebugName(),
+ nop->GetId(),
+ current_block_->GetBlockId()));
+ }
+ }
+ }
+
// Visit this block's list of phis.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
@@ -291,27 +391,30 @@
}
void GraphChecker::VisitBoundsCheck(HBoundsCheck* check) {
+ VisitInstruction(check);
+
if (!GetGraph()->HasBoundsChecks()) {
- AddError(StringPrintf("Instruction %s:%d is a HBoundsCheck, "
- "but HasBoundsChecks() returns false",
- check->DebugName(),
- check->GetId()));
+ AddError(
+ StringPrintf("The graph doesn't have the HasBoundsChecks flag set but we saw "
+ "%s:%d in block %d.",
+ check->DebugName(),
+ check->GetId(),
+ check->GetBlock()->GetBlockId()));
}
- // Perform the instruction base checks too.
- VisitInstruction(check);
+ flag_info_.seen_bounds_checks = true;
}
void GraphChecker::VisitDeoptimize(HDeoptimize* deopt) {
+ VisitInstruction(deopt);
if (GetGraph()->IsCompilingOsr()) {
AddError(StringPrintf("A graph compiled OSR cannot have a HDeoptimize instruction"));
}
-
- // Perform the instruction base checks too.
- VisitInstruction(deopt);
}
void GraphChecker::VisitTryBoundary(HTryBoundary* try_boundary) {
+ VisitInstruction(try_boundary);
+
ArrayRef<HBasicBlock* const> handlers = try_boundary->GetExceptionHandlers();
// Ensure that all exception handlers are catch blocks.
@@ -338,24 +441,51 @@
}
}
- VisitInstruction(try_boundary);
+ if (!GetGraph()->HasTryCatch()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasTryCatch flag set but we saw "
+ "%s:%d in block %d.",
+ try_boundary->DebugName(),
+ try_boundary->GetId(),
+ try_boundary->GetBlock()->GetBlockId()));
+ }
+
+ flag_info_.seen_try_boundary = true;
}
void GraphChecker::VisitLoadException(HLoadException* load) {
- // Ensure that LoadException is the first instruction in a catch block.
+ VisitInstruction(load);
+
+ // Ensure that LoadException is the second instruction in a catch block. The first one should be a
+ // Nop (checked separately).
if (!load->GetBlock()->IsCatchBlock()) {
AddError(StringPrintf("%s:%d is in a non-catch block %d.",
load->DebugName(),
load->GetId(),
load->GetBlock()->GetBlockId()));
- } else if (load->GetBlock()->GetFirstInstruction() != load) {
- AddError(StringPrintf("%s:%d is not the first instruction in catch block %d.",
+ } else if (load->GetBlock()->GetFirstInstruction()->GetNext() != load) {
+ AddError(StringPrintf("%s:%d is not the second instruction in catch block %d.",
load->DebugName(),
load->GetId(),
load->GetBlock()->GetBlockId()));
}
}
+void GraphChecker::VisitMonitorOperation(HMonitorOperation* monitor_op) {
+ VisitInstruction(monitor_op);
+
+ if (!GetGraph()->HasMonitorOperations()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasMonitorOperations flag set but we saw "
+ "%s:%d in block %d.",
+ monitor_op->DebugName(),
+ monitor_op->GetId(),
+ monitor_op->GetBlock()->GetBlockId()));
+ }
+
+ flag_info_.seen_monitor_operation = true;
+}
+
void GraphChecker::VisitInstruction(HInstruction* instruction) {
if (seen_ids_.IsBitSet(instruction->GetId())) {
AddError(StringPrintf("Instruction id %d is duplicate in graph.",
@@ -513,17 +643,10 @@
instruction->GetId(),
current_block_->GetBlockId()));
} else if (instruction->CanThrowIntoCatchBlock()) {
- // Find the top-level environment. This corresponds to the environment of
- // the catch block since we do not inline methods with try/catch.
- HEnvironment* environment = instruction->GetEnvironment();
- while (environment->GetParent() != nullptr) {
- environment = environment->GetParent();
- }
-
- // Find all catch blocks and test that `instruction` has an environment
- // value for each one.
+ // Find all catch blocks and test that `instruction` has an environment value for each one.
const HTryBoundary& entry = instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
for (HBasicBlock* catch_block : entry.GetExceptionHandlers()) {
+ const HEnvironment* environment = catch_block->GetFirstInstruction()->GetEnvironment();
for (HInstructionIterator phi_it(catch_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
HPhi* catch_phi = phi_it.Current()->AsPhi();
if (environment->GetInstructionAt(catch_phi->GetRegNumber()) == nullptr) {
@@ -541,9 +664,26 @@
}
}
-void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+void GraphChecker::VisitInvoke(HInvoke* invoke) {
VisitInstruction(invoke);
+ if (invoke->AlwaysThrows()) {
+ if (!GetGraph()->HasAlwaysThrowingInvokes()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasAlwaysThrowingInvokes flag set but we saw "
+ "%s:%d in block %d and it always throws.",
+ invoke->DebugName(),
+ invoke->GetId(),
+ invoke->GetBlock()->GetBlockId()));
+ }
+ flag_info_.seen_always_throwing_invokes = true;
+ }
+}
+
+void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ // We call VisitInvoke and not VisitInstruction to de-duplicate the always throwing code check.
+ VisitInvoke(invoke);
+
if (invoke->IsStaticWithExplicitClinitCheck()) {
const HInstruction* last_input = invoke->GetInputs().back();
if (last_input == nullptr) {
@@ -674,13 +814,14 @@
loop_information->GetPreHeader()->GetSuccessors().size()));
}
- if (loop_information->GetSuspendCheck() == nullptr) {
- AddError(StringPrintf(
- "Loop with header %d does not have a suspend check.",
- loop_header->GetBlockId()));
+ if (!GetGraph()->SuspendChecksAreAllowedToNoOp() &&
+ loop_information->GetSuspendCheck() == nullptr) {
+ AddError(StringPrintf("Loop with header %d does not have a suspend check.",
+ loop_header->GetBlockId()));
}
- if (loop_information->GetSuspendCheck() != loop_header->GetFirstInstructionDisregardMoves()) {
+ if (!GetGraph()->SuspendChecksAreAllowedToNoOp() &&
+ loop_information->GetSuspendCheck() != loop_header->GetFirstInstructionDisregardMoves()) {
AddError(StringPrintf(
"Loop header %d does not have the loop suspend check as the first instruction.",
loop_header->GetBlockId()));
@@ -1051,6 +1192,21 @@
}
}
+void GraphChecker::VisitArraySet(HArraySet* instruction) {
+ VisitInstruction(instruction);
+
+ if (instruction->NeedsTypeCheck() !=
+ instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
+ AddError(
+ StringPrintf("%s %d has a flag mismatch. An ArraySet instruction can trigger a GC iff it "
+ "needs a type check. Needs type check: %s, Can trigger GC: %s",
+ instruction->DebugName(),
+ instruction->GetId(),
+ StrBool(instruction->NeedsTypeCheck()),
+ StrBool(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()))));
+ }
+}
+
void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
VisitInstruction(op);
DataType::Type lhs_type = op->InputAt(0)->GetType();
@@ -1111,6 +1267,8 @@
}
void GraphChecker::VisitConstant(HConstant* instruction) {
+ VisitInstruction(instruction);
+
HBasicBlock* block = instruction->GetBlock();
if (!block->IsEntryBlock()) {
AddError(StringPrintf(
@@ -1149,6 +1307,18 @@
void GraphChecker::VisitVecOperation(HVecOperation* instruction) {
VisitInstruction(instruction);
+
+ if (!GetGraph()->HasSIMD()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasSIMD flag set but we saw "
+ "%s:%d in block %d.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ instruction->GetBlock()->GetBlockId()));
+ }
+
+ flag_info_.seen_SIMD = true;
+
if (codegen_ == nullptr) {
return;
}
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 04c8d21..674798e 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -21,10 +21,11 @@
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
@@ -54,6 +55,7 @@
void VisitInstruction(HInstruction* instruction) override;
void VisitPhi(HPhi* phi) override;
+ void VisitArraySet(HArraySet* instruction) override;
void VisitBinaryOperation(HBinaryOperation* op) override;
void VisitBooleanNot(HBooleanNot* instruction) override;
void VisitBoundType(HBoundType* instruction) override;
@@ -64,8 +66,10 @@
void VisitDeoptimize(HDeoptimize* instruction) override;
void VisitIf(HIf* instruction) override;
void VisitInstanceOf(HInstanceOf* check) override;
+ void VisitInvoke(HInvoke* invoke) override;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override;
void VisitLoadException(HLoadException* load) override;
+ void VisitMonitorOperation(HMonitorOperation* monitor_operation) override;
void VisitNeg(HNeg* instruction) override;
void VisitPackedSwitch(HPackedSwitch* instruction) override;
void VisitReturn(HReturn* ret) override;
@@ -123,6 +127,11 @@
ArenaVector<std::string> errors_;
private:
+ void VisitReversePostOrder();
+
+ // Checks that the graph's flags are set correctly.
+ void CheckGraphFlags();
+
// String displayed before dumped errors.
const char* const dump_prefix_;
ScopedArenaAllocator allocator_;
@@ -135,6 +144,17 @@
// Used to access target information.
CodeGenerator* codegen_;
+ struct FlagInfo {
+ bool seen_try_boundary = false;
+ bool seen_monitor_operation = false;
+ bool seen_loop = false;
+ bool seen_irreducible_loop = false;
+ bool seen_SIMD = false;
+ bool seen_bounds_checks = false;
+ bool seen_always_throwing_invokes = false;
+ };
+ FlagInfo flag_info_;
+
DISALLOW_COPY_AND_ASSIGN(GraphChecker);
};
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index 08bfa5d..9a2fc89 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
class GraphCheckerTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc
index 29af808..b5d7127 100644
--- a/compiler/optimizing/graph_test.cc
+++ b/compiler/optimizing/graph_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class GraphTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4a6ee13..96eaa61 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -43,7 +43,7 @@
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
// Unique pass-name to identify that the dump is for printing to log.
constexpr const char* kDebugDumpName = "debug";
@@ -480,6 +480,10 @@
<< array_set->GetValueCanBeNull() << std::noboolalpha;
StartAttributeStream("needs_type_check") << std::boolalpha
<< array_set->NeedsTypeCheck() << std::noboolalpha;
+ StartAttributeStream("can_trigger_gc")
+ << std::boolalpha << array_set->GetSideEffects().Includes(SideEffects::CanTriggerGC())
+ << std::noboolalpha;
+ StartAttributeStream("write_barrier_kind") << array_set->GetWriteBarrierKind();
}
void VisitCompare(HCompare* compare) override {
@@ -549,7 +553,9 @@
iset->GetFieldInfo().GetDexFile().PrettyField(iset->GetFieldInfo().GetFieldIndex(),
/* with type */ false);
StartAttributeStream("field_type") << iset->GetFieldType();
- StartAttributeStream("predicated") << std::boolalpha << iset->GetIsPredicatedSet();
+ StartAttributeStream("predicated")
+ << std::boolalpha << iset->GetIsPredicatedSet() << std::noboolalpha;
+ StartAttributeStream("write_barrier_kind") << iset->GetWriteBarrierKind();
}
void VisitStaticFieldGet(HStaticFieldGet* sget) override {
@@ -564,6 +570,7 @@
sset->GetFieldInfo().GetDexFile().PrettyField(sset->GetFieldInfo().GetFieldIndex(),
/* with type */ false);
StartAttributeStream("field_type") << sset->GetFieldType();
+ StartAttributeStream("write_barrier_kind") << sset->GetWriteBarrierKind();
}
void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) override {
@@ -904,6 +911,11 @@
if (block->IsCatchBlock()) {
PrintProperty("flags", "catch_block");
+ } else if (block->IsTryBlock()) {
+ std::stringstream flags_properties;
+ flags_properties << "try_start "
+ << namer_.GetName(block->GetTryCatchInformation()->GetTryEntry().GetBlock());
+ PrintProperty("flags", flags_properties.str().c_str());
} else if (!IsDebugDump()) {
// Don't print useless information to logcat
PrintEmptyProperty("flags");
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 3429c11..9878917 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -22,10 +22,11 @@
#include "arch/instruction_set.h"
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "base/value_object.h"
#include "block_namer.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index c7cd223..a6ca057 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -23,7 +23,7 @@
#include "base/utils.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
/**
* A ValueSet holds instructions that can replace other instructions. It is updated
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
index bbf2265..df4e3a8 100644
--- a/compiler/optimizing/gvn.h
+++ b/compiler/optimizing/gvn.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_GVN_H_
#define ART_COMPILER_OPTIMIZING_GVN_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 3bf4cc3..1eb6307 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -17,12 +17,13 @@
#include "gvn.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
class GVNTest : public OptimizingUnitTest {};
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 3b5a2f1..be6c268 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -16,9 +16,10 @@
#include "induction_var_analysis.h"
+#include "base/scoped_arena_containers.h"
#include "induction_var_range.h"
-namespace art {
+namespace art HIDDEN {
/**
* Returns true if the from/to types denote a narrowing, integral conversion (precision loss).
@@ -214,18 +215,25 @@
size_t low_depth;
};
-HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph, const char* name)
- : HOptimization(graph, name),
+HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph,
+ OptimizingCompilerStats* stats,
+ const char* name)
+ : HOptimization(graph, name, stats),
induction_(std::less<const HLoopInformation*>(),
graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)),
- cycles_(std::less<HPhi*>(),
- graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)) {
+ cycles_(std::less<HPhi*>(), graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)) {
}
bool HInductionVarAnalysis::Run() {
// Detects sequence variables (generalized induction variables) during an outer to inner
// traversal of all loops using Gerlek's algorithm. The order is important to enable
// range analysis on outer loop while visiting inner loops.
+
+ if (IsPathologicalCase()) {
+ MaybeRecordStat(stats_, MethodCompilationStat::kNotVarAnalyzedPathological);
+ return false;
+ }
+
for (HBasicBlock* graph_block : graph_->GetReversePostOrder()) {
// Don't analyze irreducible loops.
if (graph_block->IsLoopHeader() && !graph_block->GetLoopInformation()->IsIrreducible()) {
@@ -1576,4 +1584,84 @@
return "";
}
+void HInductionVarAnalysis::CalculateLoopHeaderPhisInARow(
+ HPhi* initial_phi,
+ ScopedArenaSafeMap<HPhi*, int>& cached_values,
+ ScopedArenaAllocator& allocator) {
+ DCHECK(initial_phi->IsLoopHeaderPhi());
+ ScopedArenaQueue<HPhi*> worklist(allocator.Adapter(kArenaAllocInductionVarAnalysis));
+ worklist.push(initial_phi);
+ // Used to check which phis are in the current chain we are checking.
+ ScopedArenaSet<HPhi*> phis_in_chain(allocator.Adapter(kArenaAllocInductionVarAnalysis));
+ while (!worklist.empty()) {
+ HPhi* current_phi = worklist.front();
+ DCHECK(current_phi->IsLoopHeaderPhi());
+ if (cached_values.find(current_phi) != cached_values.end()) {
+ // Already processed.
+ worklist.pop();
+ continue;
+ }
+
+ phis_in_chain.insert(current_phi);
+ int max_value = 0;
+ bool pushed_other_phis = false;
+ for (size_t index = 0; index < current_phi->InputCount(); index++) {
+ // If the input is not a loop header phi, we only have 1 (current_phi).
+ int current_value = 1;
+ if (current_phi->InputAt(index)->IsLoopHeaderPhi()) {
+ HPhi* loop_header_phi = current_phi->InputAt(index)->AsPhi();
+ auto it = cached_values.find(loop_header_phi);
+ if (it != cached_values.end()) {
+ current_value += it->second;
+ } else if (phis_in_chain.find(current_phi) == phis_in_chain.end()) {
+ // Push phis which aren't in the chain already to be processed.
+ pushed_other_phis = true;
+ worklist.push(loop_header_phi);
+ }
+ // Phis in the chain will get processed later. We keep `current_value` as 1 to avoid
+ // double counting `loop_header_phi`.
+ }
+ max_value = std::max(max_value, current_value);
+ }
+
+ if (!pushed_other_phis) {
+ // Only finish processing after all inputs were processed.
+ worklist.pop();
+ phis_in_chain.erase(current_phi);
+ cached_values.FindOrAdd(current_phi, max_value);
+ }
+ }
+}
+
+bool HInductionVarAnalysis::IsPathologicalCase() {
+ ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
+ ScopedArenaSafeMap<HPhi*, int> cached_values(
+ std::less<HPhi*>(), local_allocator.Adapter(kArenaAllocInductionVarAnalysis));
+
+ // Due to how our induction passes work, we will take a lot of time compiling if we have several
+ // loop header phis in a row. If we have more than 15 different loop header phis in a row, we
+ // don't perform the analysis.
+ constexpr int kMaximumLoopHeaderPhisInARow = 15;
+
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ if (!block->IsLoopHeader()) {
+ continue;
+ }
+
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ DCHECK(it.Current()->IsLoopHeaderPhi());
+ HPhi* phi = it.Current()->AsPhi();
+ CalculateLoopHeaderPhisInARow(phi, cached_values, local_allocator);
+ DCHECK(cached_values.find(phi) != cached_values.end())
+ << " we should have a value for Phi " << phi->GetId()
+ << " in block " << phi->GetBlock()->GetBlockId();
+ if (cached_values.find(phi)->second > kMaximumLoopHeaderPhisInARow) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
} // namespace art
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 0941772..0509500 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -21,11 +21,12 @@
#include "base/arena_containers.h"
#include "base/array_ref.h"
+#include "base/macros.h"
#include "base/scoped_arena_containers.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Induction variable analysis. This class does not have a direct public API.
@@ -38,7 +39,9 @@
*/
class HInductionVarAnalysis : public HOptimization {
public:
- explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName);
+ explicit HInductionVarAnalysis(HGraph* graph,
+ OptimizingCompilerStats* stats = nullptr,
+ const char* name = kInductionPassName);
bool Run() override;
@@ -307,6 +310,15 @@
static std::string FetchToString(HInstruction* fetch);
static std::string InductionToString(InductionInfo* info);
+ // Returns true if we have a pathological case we don't want to analyze.
+ bool IsPathologicalCase();
+ // Starting with initial_phi, it calculates how many loop header phis in a row we have. To do
+ // this, we count the loop header phi which are used as an input of other loop header phis. It
+ // uses `cached_values` to avoid recomputing results.
+ void CalculateLoopHeaderPhisInARow(HPhi* initial_phi,
+ ScopedArenaSafeMap<HPhi*, int>& cached_values,
+ ScopedArenaAllocator& allocator);
+
/**
* Maintains the results of the analysis as a mapping from loops to a mapping from instructions
* to the induction information for that instruction in that loop.
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 4c11ad4..80c1537 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -17,12 +17,13 @@
#include <regex>
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "induction_var_analysis.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the InductionVarAnalysis tests.
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index ad3d1a9..9b78699 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -17,8 +17,9 @@
#include "induction_var_range.h"
#include <limits>
+#include "optimizing/nodes.h"
-namespace art {
+namespace art HIDDEN {
/** Returns true if 64-bit constant fits in 32-bit constant. */
static bool CanLongValueFitIntoInt(int64_t c) {
@@ -1064,10 +1065,13 @@
case HInductionVarAnalysis::kLinear:
if (*stride_value > 0) {
lower = nullptr;
+ return GenerateLastValueLinear(
+ context, loop, info, trip, graph, block, /*is_min=*/false, upper);
} else {
upper = nullptr;
+ return GenerateLastValueLinear(
+ context, loop, info, trip, graph, block, /*is_min=*/true, lower);
}
- break;
case HInductionVarAnalysis::kPolynomial:
return GenerateLastValuePolynomial(context, loop, info, trip, graph, block, lower);
case HInductionVarAnalysis::kGeometric:
@@ -1113,6 +1117,54 @@
GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ false, upper);
}
+bool InductionVarRange::GenerateLastValueLinear(const HBasicBlock* context,
+ const HLoopInformation* loop,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ HGraph* graph,
+ HBasicBlock* block,
+ bool is_min,
+ /*out*/ HInstruction** result) const {
+ DataType::Type type = info->type;
+ // Avoid any narrowing linear induction or any type mismatch between the linear induction and the
+ // trip count expression.
+ if (HInductionVarAnalysis::IsNarrowingLinear(info) || trip->type != type) {
+ return false;
+ }
+
+ // Stride value must be a known constant that fits into int32.
+ int64_t stride_value = 0;
+ if (!IsConstant(context, loop, info->op_a, kExact, &stride_value) ||
+ !CanLongValueFitIntoInt(stride_value)) {
+ return false;
+ }
+
+ // We require `a` to be a constant value that didn't overflow.
+ const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
+ Value val_a = GetVal(context, loop, trip, trip, is_min_a);
+ HInstruction* opb;
+ if (!IsConstantValue(val_a) ||
+ !GenerateCode(context, loop, info->op_b, trip, graph, block, is_min, &opb)) {
+ return false;
+ }
+
+ if (graph != nullptr) {
+ ArenaAllocator* allocator = graph->GetAllocator();
+ HInstruction* oper;
+ HInstruction* opa = graph->GetConstant(type, val_a.b_constant);
+ if (stride_value == 1) {
+ oper = new (allocator) HAdd(type, opa, opb);
+ } else if (stride_value == -1) {
+ oper = new (graph->GetAllocator()) HSub(type, opb, opa);
+ } else {
+ HInstruction* mul = new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
+ oper = new (allocator) HAdd(type, Insert(block, mul), opb);
+ }
+ *result = Insert(block, oper);
+ }
+ return true;
+}
+
bool InductionVarRange::GenerateLastValuePolynomial(const HBasicBlock* context,
const HLoopInformation* loop,
HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 552837c..3e1212b 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
#define ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
+#include "base/macros.h"
#include "induction_var_analysis.h"
-namespace art {
+namespace art HIDDEN {
/**
* This class implements range analysis on expressions within loops. It takes the results
@@ -317,6 +318,15 @@
/*out*/ bool* needs_finite_test,
/*out*/ bool* needs_taken_test) const;
+ bool GenerateLastValueLinear(const HBasicBlock* context,
+ const HLoopInformation* loop,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ HGraph* graph,
+ HBasicBlock* block,
+ bool is_min,
+ /*out*/ HInstruction** result) const;
+
bool GenerateLastValuePolynomial(const HBasicBlock* context,
const HLoopInformation* loop,
HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 962123d..d879897 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -17,12 +17,13 @@
#include "induction_var_range.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "induction_var_analysis.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
using Value = InductionVarRange::Value;
@@ -1064,10 +1065,6 @@
HInstruction* last = range_.GenerateLastValue(phi, graph_, loop_preheader_);
ASSERT_TRUE(last->IsSub());
ExpectInt(1000, last->InputAt(0));
- ASSERT_TRUE(last->InputAt(1)->IsNeg());
- last = last->InputAt(1)->InputAt(0);
- ASSERT_TRUE(last->IsSub());
- ExpectInt(0, last->InputAt(0));
ExpectInt(1000, last->InputAt(1));
// Loop logic.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index f73c0d3..01f97ef 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -46,7 +46,7 @@
#include "thread.h"
#include "verifier/verifier_compiler_binding.h"
-namespace art {
+namespace art HIDDEN {
// Instruction limit to control memory.
static constexpr size_t kMaximumNumberOfTotalInstructions = 1024;
@@ -72,6 +72,9 @@
// Controls the use of inline caches in AOT mode.
static constexpr bool kUseAOTInlineCaches = true;
+// Controls the use of inlining try catches.
+static constexpr bool kInlineTryCatches = true;
+
// We check for line numbers to make sure the DepthString implementation
// aligns the output nicely.
#define LOG_INTERNAL(msg) \
@@ -141,7 +144,11 @@
}
bool did_inline = false;
- bool did_set_always_throws = false;
+ // The inliner is the only phase that sets invokes as `always throwing`, and since we only run the
+ // inliner once per graph this value should always be false at the beginning of the inlining
+ // phase. This is important since we use `HasAlwaysThrowingInvokes` to know whether the inliner
+ // phase performed a relevant change in the graph.
+ DCHECK(!graph_->HasAlwaysThrowingInvokes());
// Initialize the number of instructions for the method being compiled. Recursive calls
// to HInliner::Run have already updated the instruction count.
@@ -182,7 +189,7 @@
call->GetMethodReference().PrettyMethod(/* with_signature= */ false);
// Tests prevent inlining by having $noinline$ in their method names.
if (callee_name.find("$noinline$") == std::string::npos) {
- if (TryInline(call, &did_set_always_throws)) {
+ if (TryInline(call)) {
did_inline = true;
} else if (honor_inline_directives) {
bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos);
@@ -192,7 +199,7 @@
} else {
DCHECK(!honor_inline_directives);
// Normal case: try to inline.
- if (TryInline(call, &did_set_always_throws)) {
+ if (TryInline(call)) {
did_inline = true;
}
}
@@ -201,7 +208,9 @@
}
}
- return did_inline || did_set_always_throws;
+ // We return true if we either inlined at least one method, or we marked one of our methods as
+ // always throwing.
+ return did_inline || graph_->HasAlwaysThrowingInvokes();
}
static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
@@ -436,7 +445,7 @@
return throw_seen;
}
-bool HInliner::TryInline(HInvoke* invoke_instruction, /*inout*/ bool* did_set_always_throws) {
+bool HInliner::TryInline(HInvoke* invoke_instruction) {
MaybeRecordStat(stats_, MethodCompilationStat::kTryInline);
// Don't bother to move further if we know the method is unresolved or the invocation is
@@ -472,7 +481,8 @@
bool result = TryInlineAndReplace(invoke_instruction,
actual_method,
ReferenceTypeInfo::CreateInvalid(),
- /* do_rtp= */ true);
+ /* do_rtp= */ true,
+ /* is_speculative= */ false);
if (result) {
MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
if (outermost_graph_ == graph_) {
@@ -487,11 +497,10 @@
} else {
invoke_to_analyze = invoke_instruction;
}
- // Set always throws property for non-inlined method call with single
- // target.
- if (AlwaysThrows(actual_method)) {
- invoke_to_analyze->SetAlwaysThrows(true);
- *did_set_always_throws = true;
+ // Set always throws property for non-inlined method call with single target.
+ if (invoke_instruction->AlwaysThrows() || AlwaysThrows(actual_method)) {
+ invoke_to_analyze->SetAlwaysThrows(/* always_throws= */ true);
+ graph_->SetHasAlwaysThrowingInvokes(/* value= */ true);
}
}
return result;
@@ -499,10 +508,27 @@
DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
+ // No try catch inlining allowed here, or recursively. For try catch inlining we are banking on
+ // the fact that we have a unique dex pc list. We cannot guarantee that for some TryInline methods
+ // e.g. `TryInlinePolymorphicCall`.
+ // TODO(solanes): Setting `try_catch_inlining_allowed_` to false here covers all cases from
+ // `TryInlineFromCHA` and from `TryInlineFromInlineCache` as well (e.g.
+ // `TryInlinePolymorphicCall`). Reassess to see if we can inline inline catch blocks in
+ // `TryInlineFromCHA`, `TryInlineMonomorphicCall` and `TryInlinePolymorphicCallToSameTarget`.
+
+ // We store the value to restore it since we will use the same HInliner instance for other inlinee
+ // candidates.
+ const bool previous_value = try_catch_inlining_allowed_;
+ try_catch_inlining_allowed_ = false;
+
if (TryInlineFromCHA(invoke_instruction)) {
+ try_catch_inlining_allowed_ = previous_value;
return true;
}
- return TryInlineFromInlineCache(invoke_instruction);
+
+ const bool result = TryInlineFromInlineCache(invoke_instruction);
+ try_catch_inlining_allowed_ = previous_value;
+ return result;
}
bool HInliner::TryInlineFromCHA(HInvoke* invoke_instruction) {
@@ -518,7 +544,8 @@
if (!TryInlineAndReplace(invoke_instruction,
method,
ReferenceTypeInfo::CreateInvalid(),
- /* do_rtp= */ true)) {
+ /* do_rtp= */ true,
+ /* is_speculative= */ true)) {
return false;
}
AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor);
@@ -786,7 +813,8 @@
if (!TryInlineAndReplace(invoke_instruction,
resolved_method,
ReferenceTypeInfo::Create(monomorphic_type, /* is_exact= */ true),
- /* do_rtp= */ false)) {
+ /* do_rtp= */ false,
+ /* is_speculative= */ true)) {
return false;
}
@@ -802,7 +830,6 @@
// Run type propagation to get the guard typed, and eventually propagate the
// type of the receiver.
ReferenceTypePropagation rtp_fixup(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false);
rtp_fixup.Run();
@@ -982,7 +1009,8 @@
!TryBuildAndInline(invoke_instruction,
method,
ReferenceTypeInfo::Create(handle, /* is_exact= */ true),
- &return_replacement)) {
+ &return_replacement,
+ /* is_speculative= */ true)) {
all_targets_inlined = false;
} else {
one_target_inlined = true;
@@ -1024,7 +1052,6 @@
// Run type propagation to get the guards typed.
ReferenceTypePropagation rtp_fixup(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false);
rtp_fixup.Run();
@@ -1160,7 +1187,8 @@
if (!TryBuildAndInline(invoke_instruction,
actual_method,
ReferenceTypeInfo::CreateInvalid(),
- &return_replacement)) {
+ &return_replacement,
+ /* is_speculative= */ true)) {
return false;
}
@@ -1215,7 +1243,6 @@
// Run type propagation to get the guard typed.
ReferenceTypePropagation rtp_fixup(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false);
rtp_fixup.Run();
@@ -1232,7 +1259,6 @@
// Actual return value has a more specific type than the method's declared
// return type. Run RTP again on the outer graph to propagate it.
ReferenceTypePropagation(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false).Run();
}
@@ -1316,11 +1342,13 @@
bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
ArtMethod* method,
ReferenceTypeInfo receiver_type,
- bool do_rtp) {
+ bool do_rtp,
+ bool is_speculative) {
DCHECK(!invoke_instruction->IsIntrinsic());
HInstruction* return_replacement = nullptr;
- if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) {
+ if (!TryBuildAndInline(
+ invoke_instruction, method, receiver_type, &return_replacement, is_speculative)) {
return false;
}
@@ -1378,6 +1406,15 @@
return false;
}
+ if (annotations::MethodIsNeverInline(*method->GetDexFile(),
+ method->GetClassDef(),
+ method->GetDexMethodIndex())) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNeverInlineAnnotation)
+ << "Method " << method->PrettyMethod()
+ << " has the @NeverInline annotation so it won't be inlined";
+ return false;
+ }
+
return true;
}
@@ -1397,9 +1434,25 @@
}
if (accessor.TriesSize() != 0) {
- LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCallee)
- << "Method " << method->PrettyMethod() << " is not inlined because of try block";
- return false;
+ if (!kInlineTryCatches) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchDisabled)
+ << "Method " << method->PrettyMethod()
+ << " is not inlined because inlining try catches is disabled globally";
+ return false;
+ }
+ const bool disallowed_try_catch_inlining =
+ // Direct parent is a try block.
+ invoke_instruction->GetBlock()->IsTryBlock() ||
+ // Indirect parent disallows try catch inlining.
+ !try_catch_inlining_allowed_;
+ if (disallowed_try_catch_inlining) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCallee)
+ << "Method " << method->PrettyMethod()
+ << " is not inlined because it has a try catch and we are not supporting it for this"
+ << " particular call. This is could be because e.g. it would be inlined inside another"
+ << " try block, we arrived here from TryInlinePolymorphicCall, etc.";
+ return false;
+ }
}
if (invoke_instruction->IsInvokeStaticOrDirect() &&
@@ -1416,9 +1469,9 @@
return true;
}
-// Returns whether our resource limits allow inlining this method.
-bool HInliner::IsInliningBudgetAvailable(ArtMethod* method,
- const CodeItemDataAccessor& accessor) const {
+bool HInliner::IsInliningEncouraged(const HInvoke* invoke_instruction,
+ ArtMethod* method,
+ const CodeItemDataAccessor& accessor) const {
if (CountRecursiveCallsOf(method) > kMaximumNumberOfRecursiveCalls) {
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRecursiveBudget)
<< "Method "
@@ -1438,13 +1491,21 @@
return false;
}
+ if (invoke_instruction->GetBlock()->GetLastInstruction()->IsThrow()) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedEndsWithThrow)
+ << "Method " << method->PrettyMethod()
+ << " is not inlined because its block ends with a throw";
+ return false;
+ }
+
return true;
}
bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
ArtMethod* method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement) {
+ HInstruction** return_replacement,
+ bool is_speculative) {
// If invoke_instruction is devirtualized to a different method, give intrinsics
// another chance before we try to inline it.
if (invoke_instruction->GetResolvedMethod() != method && method->IsIntrinsic()) {
@@ -1503,12 +1564,12 @@
return false;
}
- if (!IsInliningBudgetAvailable(method, accessor)) {
+ if (!IsInliningEncouraged(invoke_instruction, method, accessor)) {
return false;
}
if (!TryBuildAndInlineHelper(
- invoke_instruction, method, receiver_type, return_replacement)) {
+ invoke_instruction, method, receiver_type, return_replacement, is_speculative)) {
return false;
}
@@ -1684,7 +1745,6 @@
Handle<mirror::DexCache> dex_cache =
graph_->GetHandleCache()->NewHandle(referrer->GetDexCache());
ReferenceTypePropagation rtp(graph_,
- outer_compilation_unit_.GetClassLoader(),
dex_cache,
/* is_first_run= */ false);
rtp.Visit(iget);
@@ -1807,7 +1867,6 @@
// are more specific than the declared ones, run RTP again on the inner graph.
if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
ReferenceTypePropagation(callee_graph,
- outer_compilation_unit_.GetClassLoader(),
dex_compilation_unit.GetDexCache(),
/* is_first_run= */ false).Run();
}
@@ -1821,8 +1880,9 @@
// If this function returns true, it will also set out_number_of_instructions to
// the number of instructions in the inlined body.
bool HInliner::CanInlineBody(const HGraph* callee_graph,
- const HBasicBlock* target_block,
- size_t* out_number_of_instructions) const {
+ HInvoke* invoke,
+ size_t* out_number_of_instructions,
+ bool is_speculative) const {
ArtMethod* const resolved_method = callee_graph->GetArtMethod();
HBasicBlock* exit_block = callee_graph->GetExitBlock();
@@ -1835,15 +1895,30 @@
bool has_one_return = false;
for (HBasicBlock* predecessor : exit_block->GetPredecessors()) {
- if (predecessor->GetLastInstruction()->IsThrow()) {
- if (target_block->IsTryBlock()) {
- // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto.
- LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCaller)
+ const HInstruction* last_instruction = predecessor->GetLastInstruction();
+ // On inlinees, we can have Return/ReturnVoid/Throw -> TryBoundary -> Exit. To check for the
+ // actual last instruction, we have to skip the TryBoundary instruction.
+ if (last_instruction->IsTryBoundary()) {
+ predecessor = predecessor->GetSinglePredecessor();
+ last_instruction = predecessor->GetLastInstruction();
+
+ // If the last instruction chain is Return/ReturnVoid -> TryBoundary -> Exit we will have to
+ // split a critical edge in InlineInto and might recompute loop information, which is
+ // unsupported for irreducible loops.
+ if (!last_instruction->IsThrow() && graph_->HasIrreducibleLoops()) {
+ DCHECK(last_instruction->IsReturn() || last_instruction->IsReturnVoid());
+ // TODO(ngeoffray): Support re-computing loop information to graphs with
+ // irreducible loops?
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoopCaller)
<< "Method " << resolved_method->PrettyMethod()
- << " could not be inlined because one branch always throws and"
- << " caller is in a try/catch block";
+ << " could not be inlined because we will have to recompute the loop information and"
+ << " the caller has irreducible loops";
return false;
- } else if (graph_->GetExitBlock() == nullptr) {
+ }
+ }
+
+ if (last_instruction->IsThrow()) {
+ if (graph_->GetExitBlock() == nullptr) {
// TODO(ngeoffray): Support adding HExit in the caller graph.
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop)
<< "Method " << resolved_method->PrettyMethod()
@@ -1853,9 +1928,10 @@
} else if (graph_->HasIrreducibleLoops()) {
// TODO(ngeoffray): Support re-computing loop information to graphs with
// irreducible loops?
- VLOG(compiler) << "Method " << resolved_method->PrettyMethod()
- << " could not be inlined because one branch always throws and"
- << " caller has irreducible loops";
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoopCaller)
+ << "Method " << resolved_method->PrettyMethod()
+ << " could not be inlined because one branch always throws and"
+ << " the caller has irreducible loops";
return false;
}
} else {
@@ -1864,6 +1940,15 @@
}
if (!has_one_return) {
+ if (!is_speculative) {
+ // If we know that the method always throws with the particular parameters, set it as such.
+ // This is better than using the dex instructions as we have more information about this
+ // particular call. We don't mark speculative inlines (e.g. the ones from the inline cache) as
+ // always throwing since they might not throw when executed.
+ invoke->SetAlwaysThrows(/* always_throws= */ true);
+ graph_->SetHasAlwaysThrowingInvokes(/* value= */ true);
+ }
+
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedAlwaysThrows)
<< "Method " << resolved_method->PrettyMethod()
<< " could not be inlined because it always throws";
@@ -1882,7 +1967,7 @@
if (block->GetLoopInformation()->IsIrreducible()) {
// Don't inline methods with irreducible loops, they could prevent some
// optimizations to run.
- LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoop)
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoopCallee)
<< "Method " << resolved_method->PrettyMethod()
<< " could not be inlined because it contains an irreducible loop";
return false;
@@ -1964,7 +2049,8 @@
bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement) {
+ HInstruction** return_replacement,
+ bool is_speculative) {
DCHECK(!(resolved_method->IsStatic() && receiver_type.IsValid()));
const dex::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
@@ -2057,10 +2143,18 @@
SubstituteArguments(callee_graph, invoke_instruction, receiver_type, dex_compilation_unit);
- RunOptimizations(callee_graph, code_item, dex_compilation_unit);
+ const bool try_catch_inlining_allowed_for_recursive_inline =
+ // It was allowed previously.
+ try_catch_inlining_allowed_ &&
+ // The current invoke is not a try block.
+ !invoke_instruction->GetBlock()->IsTryBlock();
+ RunOptimizations(callee_graph,
+ code_item,
+ dex_compilation_unit,
+ try_catch_inlining_allowed_for_recursive_inline);
size_t number_of_instructions = 0;
- if (!CanInlineBody(callee_graph, invoke_instruction->GetBlock(), &number_of_instructions)) {
+ if (!CanInlineBody(callee_graph, invoke_instruction, &number_of_instructions, is_speculative)) {
return false;
}
@@ -2095,16 +2189,17 @@
void HInliner::RunOptimizations(HGraph* callee_graph,
const dex::CodeItem* code_item,
- const DexCompilationUnit& dex_compilation_unit) {
+ const DexCompilationUnit& dex_compilation_unit,
+ bool try_catch_inlining_allowed_for_recursive_inline) {
// Note: if the outermost_graph_ is being compiled OSR, we should not run any
// optimization that could lead to a HDeoptimize. The following optimizations do not.
HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner");
- HConstantFolding fold(callee_graph, "constant_folding$inliner");
+ HConstantFolding fold(callee_graph, inline_stats_, "constant_folding$inliner");
InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_);
HOptimization* optimizations[] = {
- &simplify,
&fold,
+ &simplify,
&dce,
};
@@ -2141,7 +2236,8 @@
total_number_of_dex_registers_ + accessor.RegistersSize(),
total_number_of_instructions_ + number_of_instructions,
this,
- depth_ + 1);
+ depth_ + 1,
+ try_catch_inlining_allowed_for_recursive_inline);
inliner.Run();
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index a2c2085..af067da 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -17,13 +17,14 @@
#ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
#define ART_COMPILER_OPTIMIZING_INLINER_H_
+#include "base/macros.h"
#include "dex/dex_file_types.h"
#include "dex/invoke_type.h"
#include "jit/profiling_info.h"
#include "optimization.h"
#include "profile/profile_compilation_info.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
@@ -42,7 +43,8 @@
size_t total_number_of_dex_registers,
size_t total_number_of_instructions,
HInliner* parent,
- size_t depth = 0,
+ size_t depth,
+ bool try_catch_inlining_allowed,
const char* name = kInlinerPassName)
: HOptimization(outer_graph, name, stats),
outermost_graph_(outermost_graph),
@@ -54,6 +56,7 @@
parent_(parent),
depth_(depth),
inlining_budget_(0),
+ try_catch_inlining_allowed_(try_catch_inlining_allowed),
inline_stats_(nullptr) {}
bool Run() override;
@@ -70,9 +73,7 @@
kInlineCacheMissingTypes = 5
};
- // We set `did_set_always_throws` as true if we analyzed `invoke_instruction` and it always
- // throws.
- bool TryInline(HInvoke* invoke_instruction, /*inout*/ bool* did_set_always_throws);
+ bool TryInline(HInvoke* invoke_instruction);
// Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether
// reference type propagation can run after the inlining. If the inlining is successful, this
@@ -80,19 +81,22 @@
bool TryInlineAndReplace(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- bool do_rtp)
+ bool do_rtp,
+ bool is_speculative)
REQUIRES_SHARED(Locks::mutator_lock_);
bool TryBuildAndInline(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement)
+ HInstruction** return_replacement,
+ bool is_speculative)
REQUIRES_SHARED(Locks::mutator_lock_);
bool TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement)
+ HInstruction** return_replacement,
+ bool is_speculative)
REQUIRES_SHARED(Locks::mutator_lock_);
// Substitutes parameters in the callee graph with their values from the caller.
@@ -105,8 +109,9 @@
// Run simple optimizations on `callee_graph`.
void RunOptimizations(HGraph* callee_graph,
const dex::CodeItem* code_item,
- const DexCompilationUnit& dex_compilation_unit)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ const DexCompilationUnit& dex_compilation_unit,
+ bool try_catch_inlining_allowed_for_recursive_inline)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
bool TryPatternSubstitution(HInvoke* invoke_instruction,
@@ -129,12 +134,14 @@
const CodeItemDataAccessor& accessor) const
REQUIRES_SHARED(Locks::mutator_lock_);
- // Returns whether the inlining budget allows inlining method.
+ // Returns whether inlining is encouraged.
//
// For example, this checks whether the function has grown too large and
// inlining should be prevented.
- bool IsInliningBudgetAvailable(art::ArtMethod* method, const CodeItemDataAccessor& accessor) const
- REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsInliningEncouraged(const HInvoke* invoke_instruction,
+ art::ArtMethod* method,
+ const CodeItemDataAccessor& accessor) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Inspects the body of a method (callee_graph) and returns whether it can be
// inlined.
@@ -142,8 +149,9 @@
// This checks for instructions and constructs that we do not support
// inlining, such as inlining a throw instruction into a try block.
bool CanInlineBody(const HGraph* callee_graph,
- const HBasicBlock* target_block,
- size_t* out_number_of_instructions) const
+ HInvoke* invoke,
+ size_t* out_number_of_instructions,
+ bool is_speculative) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Create a new HInstanceFieldGet.
@@ -320,6 +328,9 @@
// The budget left for inlining, in number of instructions.
size_t inlining_budget_;
+ // States if we are allowing try catch inlining to occur at this particular instance of inlining.
+ bool try_catch_inlining_allowed_;
+
// Used to record stats about optimizations on the inlined graph.
// If the inlining is successful, these stats are merged to the caller graph's stats.
OptimizingCompilerStats* inline_stats_;
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index e0bdd09..f9a5138 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -42,7 +42,7 @@
#include "ssa_builder.h"
#include "well_known_classes.h"
-namespace art {
+namespace art HIDDEN {
namespace {
@@ -343,6 +343,10 @@
// Suspend checks were inserted into loop headers during building of dominator tree.
DCHECK(block->GetFirstInstruction()->IsSuspendCheck());
return block->GetFirstInstruction() != block->GetLastInstruction();
+ } else if (block->IsCatchBlock()) {
+ // Nops were inserted into the beginning of catch blocks.
+ DCHECK(block->GetFirstInstruction()->IsNop());
+ return block->GetFirstInstruction() != block->GetLastInstruction();
} else {
return !block->GetInstructions().IsEmpty();
}
@@ -387,6 +391,11 @@
// This is slightly odd because the loop header might not be empty (TryBoundary).
// But we're still creating the environment with locals from the top of the block.
InsertInstructionAtTop(suspend_check);
+ } else if (current_block_->IsCatchBlock()) {
+ // We add an environment emitting instruction at the beginning of each catch block, in order
+ // to support try catch inlining.
+ // This is slightly odd because the catch block might not be empty (TryBoundary).
+ InsertInstructionAtTop(new (allocator_) HNop(block_dex_pc, /* needs_environment= */ true));
}
if (block_dex_pc == kNoDexPc || current_block_ != block_builder_->GetBlockAt(block_dex_pc)) {
@@ -414,7 +423,7 @@
}
if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
- AppendInstruction(new (allocator_) HNativeDebugInfo(dex_pc));
+ AppendInstruction(new (allocator_) HNop(dex_pc, /* needs_environment= */ true));
}
// Note: There may be no Thread for gtests.
@@ -972,11 +981,11 @@
*imt_or_vtable_index = resolved_method->GetVtableIndex();
} else if (*invoke_type == kInterface) {
// For HInvokeInterface we need the IMT index.
- *imt_or_vtable_index = ImTable::GetImtIndex(resolved_method);
+ *imt_or_vtable_index = resolved_method->GetImtIndex();
+ DCHECK_EQ(*imt_or_vtable_index, ImTable::GetImtIndex(resolved_method));
}
- *is_string_constructor =
- resolved_method->IsConstructor() && resolved_method->GetDeclaringClass()->IsStringClass();
+ *is_string_constructor = resolved_method->IsStringConstructor();
return resolved_method;
}
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 817fbaa..3d65d8f 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
#include "base/array_ref.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "data_type.h"
@@ -27,7 +28,7 @@
#include "handle.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class ArenaBitVector;
class ArtField;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 789f077..86b9dba 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -31,7 +31,7 @@
#include "sharpening.h"
#include "string_builder_append.h"
-namespace art {
+namespace art HIDDEN {
// Whether to run an exhaustive test of individual HInstructions cloning when each instruction
// is replaced with its copy if it is clonable.
@@ -1142,13 +1142,13 @@
if (value->IsArrayGet()) {
if (value->AsArrayGet()->GetArray() == instruction->GetArray()) {
// If the code is just swapping elements in the array, no need for a type check.
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
}
if (value->IsNullConstant()) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
@@ -1160,13 +1160,13 @@
}
if (value_rti.IsValid() && array_rti.CanArrayHold(value_rti)) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
if (array_rti.IsObjectArray()) {
if (array_rti.IsExact()) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
instruction->SetStaticTypeOfArrayIsObjectArray();
@@ -1860,13 +1860,16 @@
// Search HDiv having the specified dividend and divisor which is in the specified basic block.
// Return nullptr if nothing has been found.
-static HInstruction* FindDivWithInputsInBasicBlock(HInstruction* dividend,
- HInstruction* divisor,
- HBasicBlock* basic_block) {
+static HDiv* FindDivWithInputsInBasicBlock(HInstruction* dividend,
+ HInstruction* divisor,
+ HBasicBlock* basic_block) {
for (const HUseListNode<HInstruction*>& use : dividend->GetUses()) {
HInstruction* user = use.GetUser();
- if (user->GetBlock() == basic_block && user->IsDiv() && user->InputAt(1) == divisor) {
- return user;
+ if (user->GetBlock() == basic_block &&
+ user->IsDiv() &&
+ user->InputAt(0) == dividend &&
+ user->InputAt(1) == divisor) {
+ return user->AsDiv();
}
}
return nullptr;
@@ -1900,7 +1903,7 @@
}
}
- HInstruction* quotient = FindDivWithInputsInBasicBlock(dividend, divisor, basic_block);
+ HDiv* quotient = FindDivWithInputsInBasicBlock(dividend, divisor, basic_block);
if (quotient == nullptr) {
return;
}
@@ -2647,15 +2650,13 @@
// Collect args and check for unexpected uses.
// We expect one call to a constructor with no arguments, one constructor fence (unless
// eliminated), some number of append calls and one call to StringBuilder.toString().
- bool constructor_inlined = false;
bool seen_constructor = false;
bool seen_constructor_fence = false;
bool seen_to_string = false;
uint32_t format = 0u;
uint32_t num_args = 0u;
+ bool has_fp_args = false;
HInstruction* args[StringBuilderAppend::kMaxArgs]; // Added in reverse order.
- // When inlining, `maybe_new_array` tracks an environment use that we want to allow.
- HInstruction* maybe_new_array = nullptr;
for (HBackwardInstructionIterator iter(block->GetInstructions()); !iter.Done(); iter.Advance()) {
HInstruction* user = iter.Current();
// Instructions of interest apply to `sb`, skip those that do not involve `sb`.
@@ -2700,6 +2701,14 @@
case Intrinsics::kStringBuilderAppendLong:
arg = StringBuilderAppend::Argument::kLong;
break;
+ case Intrinsics::kStringBuilderAppendFloat:
+ arg = StringBuilderAppend::Argument::kFloat;
+ has_fp_args = true;
+ break;
+ case Intrinsics::kStringBuilderAppendDouble:
+ arg = StringBuilderAppend::Argument::kDouble;
+ has_fp_args = true;
+ break;
case Intrinsics::kStringBuilderAppendCharSequence: {
ReferenceTypeInfo rti = user->AsInvokeVirtual()->InputAt(1)->GetReferenceTypeInfo();
if (!rti.IsValid()) {
@@ -2719,10 +2728,6 @@
}
break;
}
- case Intrinsics::kStringBuilderAppendFloat:
- case Intrinsics::kStringBuilderAppendDouble:
- // TODO: Unimplemented, needs to call FloatingDecimal.getBinaryToASCIIConverter().
- return false;
default: {
return false;
}
@@ -2736,25 +2741,13 @@
format = (format << StringBuilderAppend::kBitsPerArg) | static_cast<uint32_t>(arg);
args[num_args] = as_invoke_virtual->InputAt(1u);
++num_args;
- } else if (!seen_constructor) {
- // At this point, we should see the constructor. However, we might have inlined it so we have
- // to take care of both cases. We accept only the constructor with no extra arguments. This
- // means that if we inline it, we have to check it is setting its field to a new array.
- if (user->IsInvokeStaticOrDirect() &&
- user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
- user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
- user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) {
- constructor_inlined = false;
- } else if (user->IsInstanceFieldSet() &&
- user->AsInstanceFieldSet()->GetFieldType() == DataType::Type::kReference &&
- user->AsInstanceFieldSet()->InputAt(0) == sb &&
- user->AsInstanceFieldSet()->GetValue()->IsNewArray()) {
- maybe_new_array = user->AsInstanceFieldSet()->GetValue();
- constructor_inlined = true;
- } else {
- // We were expecting a constructor but we haven't seen it. Abort optimization.
- return false;
- }
+ } else if (user->IsInvokeStaticOrDirect() &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
+ user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) {
+ // After arguments, we should see the constructor.
+ // We accept only the constructor with no extra arguments.
+ DCHECK(!seen_constructor);
DCHECK(!seen_constructor_fence);
seen_constructor = true;
} else if (user->IsConstructorFence()) {
@@ -2780,17 +2773,6 @@
// Accept only calls on the StringBuilder (which shall all be removed).
// TODO: Carve-out for const-string? Or rely on environment pruning (to be implemented)?
if (holder->InputCount() == 0 || holder->InputAt(0) != sb) {
- // When inlining the constructor, we have a NewArray and may have a LoadClass as an
- // environment use.
- if (constructor_inlined) {
- if (holder == maybe_new_array) {
- continue;
- }
- if (holder == maybe_new_array->InputAt(0)) {
- DCHECK(holder->IsLoadClass());
- continue;
- }
- }
return false;
}
}
@@ -2798,8 +2780,8 @@
// Create replacement instruction.
HIntConstant* fmt = block->GetGraph()->GetIntConstant(static_cast<int32_t>(format));
ArenaAllocator* allocator = block->GetGraph()->GetAllocator();
- HStringBuilderAppend* append =
- new (allocator) HStringBuilderAppend(fmt, num_args, allocator, invoke->GetDexPc());
+ HStringBuilderAppend* append = new (allocator) HStringBuilderAppend(
+ fmt, num_args, has_fp_args, allocator, invoke->GetDexPc());
append->SetReferenceTypeInfo(invoke->GetReferenceTypeInfo());
for (size_t i = 0; i != num_args; ++i) {
append->SetArgumentAt(i, args[num_args - 1u - i]);
@@ -2824,33 +2806,6 @@
while (sb->HasNonEnvironmentUses()) {
block->RemoveInstruction(sb->GetUses().front().GetUser());
}
- if (constructor_inlined) {
- // We need to remove the inlined constructor instructions,
- // and all remaining environment uses (if any).
- DCHECK(sb->HasEnvironmentUses());
- DCHECK(maybe_new_array != nullptr);
- DCHECK(maybe_new_array->IsNewArray());
- DCHECK(maybe_new_array->HasNonEnvironmentUses());
- HInstruction* fence = maybe_new_array->GetUses().front().GetUser();
- DCHECK(fence->IsConstructorFence());
- block->RemoveInstruction(fence);
- block->RemoveInstruction(maybe_new_array);
- if (sb->HasEnvironmentUses()) {
- // We know the only remaining uses are from the LoadClass.
- HInstruction* load_class = maybe_new_array->InputAt(0);
- DCHECK(load_class->IsLoadClass());
- for (HEnvironment* env = load_class->GetEnvironment();
- env != nullptr;
- env = env->GetParent()) {
- for (size_t i = 0, size = env->Size(); i != size; ++i) {
- if (env->GetInstructionAt(i) == sb) {
- env->RemoveAsUserOfInput(i);
- env->SetRawEnvAt(i, /*instruction=*/ nullptr);
- }
- }
- }
- }
- }
DCHECK(!sb->HasEnvironmentUses());
block->RemoveInstruction(sb);
return true;
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index feea771..98ebaaf 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -17,11 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
#include "optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 1371ea7..88895b6 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -23,7 +23,7 @@
#include "mirror/string.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h
index fca9341..0517e4f 100644
--- a/compiler/optimizing/instruction_simplifier_arm.h
+++ b/compiler/optimizing/instruction_simplifier_arm.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class InstructionSimplifierArm : public HOptimization {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index a6ec020..bed7ff7 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -21,7 +21,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index 8d93c01..374638a 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM64_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM64_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
class InstructionSimplifierArm64 : public HOptimization {
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index fb8b01b..34daae2 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -18,7 +18,7 @@
#include "mirror/array-inl.h"
-namespace art {
+namespace art HIDDEN {
namespace {
diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h
index 876ed21..ddc3a86 100644
--- a/compiler/optimizing/instruction_simplifier_shared.h
+++ b/compiler/optimizing/instruction_simplifier_shared.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
namespace helpers {
diff --git a/compiler/optimizing/instruction_simplifier_test.cc b/compiler/optimizing/instruction_simplifier_test.cc
index c7c5b12..966f5b9 100644
--- a/compiler/optimizing/instruction_simplifier_test.cc
+++ b/compiler/optimizing/instruction_simplifier_test.cc
@@ -26,13 +26,15 @@
#include "optimizing/data_type.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
namespace mirror {
class ClassExt;
class Throwable;
} // namespace mirror
+static constexpr bool kDebugSimplifierTests = false;
+
template<typename SuperClass>
class InstructionSimplifierTestBase : public SuperClass, public OptimizingUnitTestHelper {
public:
@@ -49,6 +51,19 @@
SuperClass::TearDown();
gLogVerbosity.compiler = false;
}
+
+ void PerformSimplification(const AdjacencyListGraph& blks) {
+ if (kDebugSimplifierTests) {
+ LOG(INFO) << "Pre simplification " << blks;
+ }
+ graph_->ClearDominanceInformation();
+ graph_->BuildDominatorTree();
+ InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
+ simp.Run();
+ if (kDebugSimplifierTests) {
+ LOG(INFO) << "Post simplify " << blks;
+ }
+ }
};
class InstructionSimplifierTest : public InstructionSimplifierTestBase<CommonCompilerTest> {};
@@ -197,13 +212,7 @@
SetupExit(exit);
- LOG(INFO) << "Pre simplification " << blks;
- graph_->ClearDominanceInformation();
- graph_->BuildDominatorTree();
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post simplify " << blks;
+ PerformSimplification(blks);
EXPECT_INS_RETAINED(read_end);
@@ -289,13 +298,7 @@
SetupExit(exit);
- LOG(INFO) << "Pre simplification " << blks;
- graph_->ClearDominanceInformation();
- graph_->BuildDominatorTree();
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post simplify " << blks;
+ PerformSimplification(blks);
EXPECT_FALSE(obj3->CanBeNull());
EXPECT_INS_RETAINED(read_end);
@@ -373,13 +376,7 @@
SetupExit(exit);
- LOG(INFO) << "Pre simplification " << blks;
- graph_->ClearDominanceInformation();
- graph_->BuildDominatorTree();
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post simplify " << blks;
+ PerformSimplification(blks);
EXPECT_FALSE(obj1->CanBeNull());
EXPECT_FALSE(obj2->CanBeNull());
@@ -464,16 +461,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
-
- LOG(INFO) << "Pre simplification " << blks;
- graph_->ClearDominanceInformation();
- graph_->BuildDominatorTree();
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post simplify " << blks;
+ PerformSimplification(blks);
if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
EXPECT_INS_RETAINED(target_klass);
@@ -532,16 +520,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
-
- LOG(INFO) << "Pre simplification " << blks;
- graph_->ClearDominanceInformation();
- graph_->BuildDominatorTree();
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post simplify " << blks;
+ PerformSimplification(blks);
if (!GetConstantResult() || GetParam() == InstanceOfKind::kSelf) {
EXPECT_INS_RETAINED(target_klass);
diff --git a/compiler/optimizing/instruction_simplifier_x86.cc b/compiler/optimizing/instruction_simplifier_x86.cc
index 2d8f94a..805dd49 100644
--- a/compiler/optimizing/instruction_simplifier_x86.cc
+++ b/compiler/optimizing/instruction_simplifier_x86.cc
@@ -17,7 +17,7 @@
#include "instruction_simplifier_x86_shared.h"
#include "code_generator_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
diff --git a/compiler/optimizing/instruction_simplifier_x86.h b/compiler/optimizing/instruction_simplifier_x86.h
index 6f10006..25ebe20 100644
--- a/compiler/optimizing/instruction_simplifier_x86.h
+++ b/compiler/optimizing/instruction_simplifier_x86.h
@@ -16,10 +16,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
namespace x86 {
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.cc b/compiler/optimizing/instruction_simplifier_x86_64.cc
index 56c6b41..b783918 100644
--- a/compiler/optimizing/instruction_simplifier_x86_64.cc
+++ b/compiler/optimizing/instruction_simplifier_x86_64.cc
@@ -17,7 +17,7 @@
#include "instruction_simplifier_x86_shared.h"
#include "code_generator_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.h b/compiler/optimizing/instruction_simplifier_x86_64.h
index 6cae24d..1654dc4 100644
--- a/compiler/optimizing/instruction_simplifier_x86_64.h
+++ b/compiler/optimizing/instruction_simplifier_x86_64.h
@@ -16,10 +16,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.cc b/compiler/optimizing/instruction_simplifier_x86_shared.cc
index 2805abb..74c5ca2 100644
--- a/compiler/optimizing/instruction_simplifier_x86_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.cc
@@ -14,9 +14,10 @@
*/
#include "instruction_simplifier_x86_shared.h"
+
#include "nodes_x86.h"
-namespace art {
+namespace art HIDDEN {
bool TryCombineAndNot(HAnd* instruction) {
DataType::Type type = instruction->GetType();
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.h b/compiler/optimizing/instruction_simplifier_x86_shared.h
index 7f94d7e..1a44d0f 100644
--- a/compiler/optimizing/instruction_simplifier_x86_shared.h
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.h
@@ -16,13 +16,16 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
+
bool TryCombineAndNot(HAnd* instruction);
bool TryGenerateResetLeastSetBit(HAnd* instruction);
bool TryGenerateMaskUptoLeastSetBit(HXor* instruction);
bool AreLeastSetBitInputs(HInstruction* to_test, HInstruction* other);
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
diff --git a/compiler/optimizing/intrinsic_objects.cc b/compiler/optimizing/intrinsic_objects.cc
index 5f6f562..7e54211 100644
--- a/compiler/optimizing/intrinsic_objects.cc
+++ b/compiler/optimizing/intrinsic_objects.cc
@@ -22,7 +22,7 @@
#include "image.h"
#include "obj_ptr-inl.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kIntrinsicObjectsOffset =
enum_cast<size_t>(ImageHeader::kIntrinsicObjectsStart);
diff --git a/compiler/optimizing/intrinsic_objects.h b/compiler/optimizing/intrinsic_objects.h
index ed764bd..d750f29 100644
--- a/compiler/optimizing/intrinsic_objects.h
+++ b/compiler/optimizing/intrinsic_objects.h
@@ -19,9 +19,10 @@
#include "base/bit_field.h"
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "base/mutex.h"
-namespace art {
+namespace art HIDDEN {
class ClassLinker;
template <class MirrorType> class ObjPtr;
@@ -56,15 +57,15 @@
}
// Functions for retrieving data for Integer.valueOf().
- static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(
+ EXPORT static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(
Thread* self, ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_);
- static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerValueOfCache(
+ EXPORT static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerValueOfCache(
ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects)
REQUIRES_SHARED(Locks::mutator_lock_);
- static ObjPtr<mirror::Object> GetIntegerValueOfObject(
+ EXPORT static ObjPtr<mirror::Object> GetIntegerValueOfObject(
ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects,
uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_);
- static MemberOffset GetIntegerValueOfArrayDataOffset(
+ EXPORT static MemberOffset GetIntegerValueOfArrayDataOffset(
ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 0feb92d..aa5e4f3 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -32,7 +32,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-namespace art {
+namespace art HIDDEN {
std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
switch (intrinsic) {
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 5109882..893cd04 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_H_
+#include "base/macros.h"
#include "code_generator.h"
#include "nodes.h"
#include "optimization.h"
#include "parallel_move_resolver.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 0ce082b..40df479 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -46,7 +46,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
@@ -3335,7 +3335,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(dest.W(), Register(), /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -4898,7 +4898,7 @@
}
if (CodeGenerator::StoreNeedsWriteBarrier(value_type, invoke->InputAt(value_index))) {
- codegen->MarkGCCard(target.object, Register(value), /*value_can_be_null=*/ true);
+ codegen->MarkGCCard(target.object, Register(value), /* emit_null_check= */ true);
}
if (slow_path != nullptr) {
diff --git a/compiler/optimizing/intrinsics_arm64.h b/compiler/optimizing/intrinsics_arm64.h
index 9c46efd..a0ccf87 100644
--- a/compiler/optimizing/intrinsics_arm64.h
+++ b/compiler/optimizing/intrinsics_arm64.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
+#include "base/macros.h"
#include "intrinsics.h"
namespace vixl {
@@ -27,7 +28,7 @@
} // namespace aarch64
} // namespace vixl
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class HInvokeStaticOrDirect;
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index da47fa6..55d51fe 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -34,7 +34,7 @@
#include "aarch32/constants-aarch32.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
#define __ assembler->GetVIXLAssembler()->
@@ -1723,7 +1723,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -4699,7 +4699,7 @@
vixl32::Register temp = target.offset;
vixl32::Register card = temps.Acquire();
vixl32::Register value_reg = RegisterFrom(value);
- codegen->MarkGCCard(temp, card, target.object, value_reg, /*value_can_be_null=*/ true);
+ codegen->MarkGCCard(temp, card, target.object, value_reg, /* emit_null_check= */ true);
}
if (slow_path != nullptr) {
diff --git a/compiler/optimizing/intrinsics_arm_vixl.h b/compiler/optimizing/intrinsics_arm_vixl.h
index 3103cec..54475bc 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.h
+++ b/compiler/optimizing/intrinsics_arm_vixl.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_VIXL_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_VIXL_H_
+#include "base/macros.h"
#include "intrinsics.h"
#include "utils/arm/assembler_arm_vixl.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h
index 19f5e33..13cabda 100644
--- a/compiler/optimizing/intrinsics_utils.h
+++ b/compiler/optimizing/intrinsics_utils.h
@@ -29,7 +29,7 @@
#include "utils/assembler.h"
#include "utils/label.h"
-namespace art {
+namespace art HIDDEN {
// Default slow-path for fallback (calling the managed code to handle the intrinsic) in an
// intrinsified call. This will copy the arguments into the positions for a regular call.
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 0f6eb86..a76b773 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -38,7 +38,7 @@
#include "utils/x86/assembler_x86.h"
#include "utils/x86/constants_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
@@ -3259,7 +3259,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -4041,13 +4041,16 @@
InstructionCodeGeneratorX86* instr_codegen =
down_cast<InstructionCodeGeneratorX86*>(codegen->GetInstructionVisitor());
// Store the value to the field
- instr_codegen->HandleFieldSet(invoke,
- value_index,
- value_type,
- Address(reference, offset, TIMES_1, 0),
- reference,
- is_volatile,
- /* value_can_be_null */ true);
+ instr_codegen->HandleFieldSet(
+ invoke,
+ value_index,
+ value_type,
+ Address(reference, offset, TIMES_1, 0),
+ reference,
+ is_volatile,
+ /* value_can_be_null */ true,
+ // Value can be null, and this write barrier is not being relied on for other sets.
+ WriteBarrierKind::kEmitWithNullCheck);
__ Bind(slow_path->GetExitLabel());
}
@@ -4208,7 +4211,7 @@
&temp2);
}
codegen->MarkGCCard(
- temp, temp2, reference, value.AsRegister<Register>(), /* value_can_be_null= */ false);
+ temp, temp2, reference, value.AsRegister<Register>(), /* emit_null_check= */ false);
if (kPoisonHeapReferences) {
__ movl(temp, value.AsRegister<Register>());
__ PoisonHeapReference(temp);
diff --git a/compiler/optimizing/intrinsics_x86.h b/compiler/optimizing/intrinsics_x86.h
index ae150da..77c236d 100644
--- a/compiler/optimizing/intrinsics_x86.h
+++ b/compiler/optimizing/intrinsics_x86.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_X86_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_X86_H_
+#include "base/macros.h"
#include "intrinsics.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class HInvokeStaticOrDirect;
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 9921d90..cf3d63b 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -36,7 +36,7 @@
#include "utils/x86_64/assembler_x86_64.h"
#include "utils/x86_64/constants_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
@@ -1224,7 +1224,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(temp1, temp2, dest, CpuRegister(kNoRegister), /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(temp1, temp2, dest, CpuRegister(kNoRegister), /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -3985,16 +3985,19 @@
Address dst(CpuRegister(target.object), CpuRegister(target.offset), TIMES_1, 0);
// Store the value to the field.
- codegen->GetInstructionCodegen()->HandleFieldSet(invoke,
- value_index,
- last_temp_index,
- value_type,
- dst,
- CpuRegister(target.object),
- is_volatile,
- is_atomic,
- /*value_can_be_null=*/ true,
- byte_swap);
+ codegen->GetInstructionCodegen()->HandleFieldSet(
+ invoke,
+ value_index,
+ last_temp_index,
+ value_type,
+ dst,
+ CpuRegister(target.object),
+ is_volatile,
+ is_atomic,
+ /*value_can_be_null=*/true,
+ byte_swap,
+ // Value can be null, and this write barrier is not being relied on for other sets.
+ WriteBarrierKind::kEmitWithNullCheck);
// setVolatile needs kAnyAny barrier, but HandleFieldSet takes care of that.
@@ -4278,7 +4281,7 @@
&temp1,
&temp2);
}
- codegen->MarkGCCard(temp1, temp2, ref, valreg, /*value_can_be_null=*/ false);
+ codegen->MarkGCCard(temp1, temp2, ref, valreg, /* emit_null_check= */ false);
DCHECK_EQ(valreg, out.AsRegister<CpuRegister>());
if (kPoisonHeapReferences) {
diff --git a/compiler/optimizing/intrinsics_x86_64.h b/compiler/optimizing/intrinsics_x86_64.h
index 199cfed..59fe815 100644
--- a/compiler/optimizing/intrinsics_x86_64.h
+++ b/compiler/optimizing/intrinsics_x86_64.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
+#include "base/macros.h"
#include "intrinsics.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class HInvokeStaticOrDirect;
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index 0edb23b..0c791b6 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -18,7 +18,7 @@
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
static bool IsPhiOf(HInstruction* instruction, HBasicBlock* block) {
return instruction->IsPhi() && instruction->GetBlock() == block;
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
index 9cafddb..1a86b6e 100644
--- a/compiler/optimizing/licm.h
+++ b/compiler/optimizing/licm.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_LICM_H_
#define ART_COMPILER_OPTIMIZING_LICM_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index adc3cab..f848109 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -17,12 +17,13 @@
#include "licm.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the LICM tests.
diff --git a/compiler/optimizing/linear_order.cc b/compiler/optimizing/linear_order.cc
index 58e00a8..25ca866 100644
--- a/compiler/optimizing/linear_order.cc
+++ b/compiler/optimizing/linear_order.cc
@@ -19,7 +19,7 @@
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
-namespace art {
+namespace art HIDDEN {
static bool InSameLoop(HLoopInformation* first_loop, HLoopInformation* second_loop) {
return first_loop == second_loop;
diff --git a/compiler/optimizing/linear_order.h b/compiler/optimizing/linear_order.h
index 151db00..75e7504 100644
--- a/compiler/optimizing/linear_order.h
+++ b/compiler/optimizing/linear_order.h
@@ -19,9 +19,10 @@
#include <type_traits>
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void LinearizeGraphInternal(const HGraph* graph, ArrayRef<HBasicBlock*> linear_order);
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index d56ae11..b39bac5 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -17,6 +17,7 @@
#include <fstream>
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "dex/dex_file.h"
@@ -28,7 +29,7 @@
#include "pretty_printer.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
class LinearizeTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/live_interval_test.cc b/compiler/optimizing/live_interval_test.cc
index c60386d..b5d1336 100644
--- a/compiler/optimizing/live_interval_test.cc
+++ b/compiler/optimizing/live_interval_test.cc
@@ -15,12 +15,13 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "optimizing_unit_test.h"
#include "ssa_liveness_analysis.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
TEST(LiveInterval, GetStart) {
ArenaPoolAndAllocator pool;
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index bb8a4dc..4f52fdb 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "dex/dex_file.h"
@@ -25,7 +26,7 @@
#include "prepare_for_register_allocation.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
class LiveRangesTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index ba3787e..a276373 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "dex/dex_file.h"
@@ -25,7 +26,7 @@
#include "prepare_for_register_allocation.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
class LivenessTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc
index 3fe42af..f1c50ac 100644
--- a/compiler/optimizing/load_store_analysis.cc
+++ b/compiler/optimizing/load_store_analysis.cc
@@ -19,7 +19,7 @@
#include "base/scoped_arena_allocator.h"
#include "optimizing/escape.h"
-namespace art {
+namespace art HIDDEN {
// A cap for the number of heap locations to prevent pathological time/space consumption.
// The number of heap locations for most of the methods stays below this threshold.
@@ -283,14 +283,6 @@
heap_location_collector_.CleanUp();
return false;
}
- if (heap_location_collector_.HasVolatile() || heap_location_collector_.HasMonitorOps()) {
- // Don't do load/store elimination if the method has volatile field accesses or
- // monitor operations, for now.
- // TODO: do it right.
- heap_location_collector_.CleanUp();
- return false;
- }
-
heap_location_collector_.BuildAliasingMatrix();
heap_location_collector_.DumpReferenceStats(stats_);
return true;
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index 4975bae..c0799db 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -20,6 +20,7 @@
#include "base/arena_allocator.h"
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
@@ -28,7 +29,7 @@
#include "nodes.h"
#include "optimizing/optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
enum class LoadStoreAnalysisType {
kBasic,
@@ -253,8 +254,6 @@
heap_locations_(allocator->Adapter(kArenaAllocLSA)),
aliasing_matrix_(allocator, kInitialAliasingMatrixBitVectorSize, true, kArenaAllocLSA),
has_heap_stores_(false),
- has_volatile_(false),
- has_monitor_operations_(false),
lse_type_(lse_type) {
aliasing_matrix_.ClearAllBits();
}
@@ -350,14 +349,6 @@
return has_heap_stores_;
}
- bool HasVolatile() const {
- return has_volatile_;
- }
-
- bool HasMonitorOps() const {
- return has_monitor_operations_;
- }
-
// Find and return the heap location index in heap_locations_.
// NOTE: When heap locations are created, potentially aliasing/overlapping
// accesses are given different indexes. This find function also
@@ -540,9 +531,6 @@
}
void VisitFieldAccess(HInstruction* ref, const FieldInfo& field_info) {
- if (field_info.IsVolatile()) {
- has_volatile_ = true;
- }
DataType::Type type = field_info.GetFieldType();
const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex();
const size_t offset = field_info.GetFieldOffset().SizeValue();
@@ -637,18 +625,12 @@
CreateReferenceInfoForReferenceType(instruction);
}
- void VisitMonitorOperation(HMonitorOperation* monitor ATTRIBUTE_UNUSED) override {
- has_monitor_operations_ = true;
- }
-
ScopedArenaAllocator* allocator_;
ScopedArenaVector<ReferenceInfo*> ref_info_array_; // All references used for heap accesses.
ScopedArenaVector<HeapLocation*> heap_locations_; // All heap locations.
ArenaBitVector aliasing_matrix_; // aliasing info between each pair of locations.
bool has_heap_stores_; // If there is no heap stores, LSE acts as GVN with better
// alias analysis and won't be as effective.
- bool has_volatile_; // If there are volatile field accesses.
- bool has_monitor_operations_; // If there are monitor operations.
LoadStoreAnalysisType lse_type_;
DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector);
diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc
index 3c26c8d..7930c0d 100644
--- a/compiler/optimizing/load_store_analysis_test.cc
+++ b/compiler/optimizing/load_store_analysis_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "load_store_analysis.h"
#include <array>
@@ -36,7 +37,7 @@
#include "optimizing_unit_test.h"
#include "scoped_thread_state_change.h"
-namespace art {
+namespace art HIDDEN {
class LoadStoreAnalysisTest : public CommonCompilerTest, public OptimizingUnitTestHelper {
public:
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 9b8f07e..b385e03 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -319,7 +319,7 @@
* a hash map to the HeapLocationCollector.
*/
-namespace art {
+namespace art HIDDEN {
#define LSE_VLOG \
if (::art::LoadStoreElimination::kVerboseLoggingMode && VLOG_IS_ON(compiler)) LOG(INFO)
@@ -855,25 +855,6 @@
}
}
- // `instruction` is being removed. Try to see if the null check on it
- // can be removed. This can happen if the same value is set in two branches
- // but not in dominators. Such as:
- // int[] a = foo();
- // if () {
- // a[0] = 2;
- // } else {
- // a[0] = 2;
- // }
- // // a[0] can now be replaced with constant 2, and the null check on it can be removed.
- void TryRemovingNullCheck(HInstruction* instruction) {
- HInstruction* prev = instruction->GetPrevious();
- if ((prev != nullptr) && prev->IsNullCheck() && (prev == instruction->InputAt(0))) {
- // Previous instruction is a null check for this instruction. Remove the null check.
- prev->ReplaceWith(prev->InputAt(0));
- prev->GetBlock()->RemoveInstruction(prev);
- }
- }
-
HInstruction* GetDefaultValue(DataType::Type type) {
switch (type) {
case DataType::Type::kReference:
@@ -993,13 +974,63 @@
<< " but LSE should be the only source of predicated-ifield-gets!";
}
+ void HandleAcquireLoad(HInstruction* instruction) {
+ DCHECK((instruction->IsInstanceFieldGet() && instruction->AsInstanceFieldGet()->IsVolatile()) ||
+ (instruction->IsStaticFieldGet() && instruction->AsStaticFieldGet()->IsVolatile()) ||
+ (instruction->IsMonitorOperation() && instruction->AsMonitorOperation()->IsEnter()))
+ << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName();
+
+ // Acquire operations e.g. MONITOR_ENTER change the thread's view of the memory, so we must
+ // invalidate all current values.
+ ScopedArenaVector<ValueRecord>& heap_values =
+ heap_values_for_[instruction->GetBlock()->GetBlockId()];
+ for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
+ KeepStores(heap_values[i].stored_by);
+ heap_values[i].stored_by = Value::PureUnknown();
+ heap_values[i].value = Value::PartialUnknown(heap_values[i].value);
+ }
+
+ // Note that there's no need to record the load as subsequent acquire loads shouldn't be
+ // eliminated either.
+ }
+
+ void HandleReleaseStore(HInstruction* instruction) {
+ DCHECK((instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->IsVolatile()) ||
+ (instruction->IsStaticFieldSet() && instruction->AsStaticFieldSet()->IsVolatile()) ||
+ (instruction->IsMonitorOperation() && !instruction->AsMonitorOperation()->IsEnter()))
+ << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName();
+
+ // Release operations e.g. MONITOR_EXIT do not affect this thread's view of the memory, but
+ // they will push the modifications for other threads to see. Therefore, we must keep the
+ // stores but there's no need to clobber the value.
+ ScopedArenaVector<ValueRecord>& heap_values =
+ heap_values_for_[instruction->GetBlock()->GetBlockId()];
+ for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
+ KeepStores(heap_values[i].stored_by);
+ heap_values[i].stored_by = Value::PureUnknown();
+ }
+
+ // Note that there's no need to record the store as subsequent release store shouldn't be
+ // eliminated either.
+ }
+
void VisitInstanceFieldGet(HInstanceFieldGet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleAcquireLoad(instruction);
+ return;
+ }
+
HInstruction* object = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field));
}
void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleReleaseStore(instruction);
+ return;
+ }
+
HInstruction* object = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
HInstruction* value = instruction->InputAt(1);
@@ -1008,12 +1039,22 @@
}
void VisitStaticFieldGet(HStaticFieldGet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleAcquireLoad(instruction);
+ return;
+ }
+
HInstruction* cls = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field));
}
void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleReleaseStore(instruction);
+ return;
+ }
+
HInstruction* cls = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
HInstruction* value = instruction->InputAt(1);
@@ -1021,6 +1062,14 @@
VisitSetLocation(instruction, idx, value);
}
+ void VisitMonitorOperation(HMonitorOperation* monitor_op) override {
+ if (monitor_op->IsEnter()) {
+ HandleAcquireLoad(monitor_op);
+ } else {
+ HandleReleaseStore(monitor_op);
+ }
+ }
+
void VisitArrayGet(HArrayGet* instruction) override {
VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction));
}
@@ -1040,8 +1089,8 @@
}
void VisitDeoptimize(HDeoptimize* instruction) override {
- // If we are in a try catch, even singletons are observable.
- const bool in_try_catch = instruction->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool inside_a_try = instruction->GetBlock()->IsTryBlock();
HBasicBlock* block = instruction->GetBlock();
ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
@@ -1053,7 +1102,7 @@
// for singletons that don't escape in the deoptimization environment.
bool observable = true;
ReferenceInfo* info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
- if (!in_try_catch && info->IsSingleton()) {
+ if (!inside_a_try && info->IsSingleton()) {
HInstruction* reference = info->GetReference();
// Finalizable objects always escape.
const bool finalizable_object =
@@ -1099,10 +1148,8 @@
void HandleThrowingInstruction(HInstruction* instruction) {
DCHECK(instruction->CanThrow());
- // If we are inside of a try catch, singletons can become visible since we may not exit the
- // method.
- HandleExit(instruction->GetBlock(),
- instruction->GetBlock()->GetTryCatchInformation() != nullptr);
+ // If we are inside of a try, singletons can become visible since we may not exit the method.
+ HandleExit(instruction->GetBlock(), instruction->GetBlock()->IsTryBlock());
}
void VisitMethodEntryHook(HMethodEntryHook* method_entry) override {
@@ -1137,6 +1184,14 @@
}
}
+ void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) override {
+ HandleThrowingInstruction(load_method_handle);
+ }
+
+ void VisitLoadMethodType(HLoadMethodType* load_method_type) override {
+ HandleThrowingInstruction(load_method_type);
+ }
+
void VisitStringBuilderAppend(HStringBuilderAppend* sb_append) override {
HandleThrowingInstruction(sb_append);
}
@@ -1149,18 +1204,11 @@
HandleThrowingInstruction(check_cast);
}
- void VisitMonitorOperation(HMonitorOperation* monitor_op) override {
- if (monitor_op->CanThrow()) {
- HandleThrowingInstruction(monitor_op);
- }
- }
-
void HandleInvoke(HInstruction* instruction) {
// If `instruction` can throw we have to presume all stores are visible.
const bool can_throw = instruction->CanThrow();
- // If we are in a try catch, even singletons are observable.
- const bool can_throw_in_try_catch =
- can_throw && instruction->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool can_throw_inside_a_try = can_throw && instruction->GetBlock()->IsTryBlock();
SideEffects side_effects = instruction->GetSideEffects();
ScopedArenaVector<ValueRecord>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
@@ -1186,7 +1234,7 @@
return cohort.PrecedesBlock(blk);
});
};
- if (!can_throw_in_try_catch &&
+ if (!can_throw_inside_a_try &&
(ref_info->IsSingleton() ||
// partial and we aren't currently escaping and we haven't escaped yet.
(ref_info->IsPartialSingleton() && partial_singleton_did_not_escape(ref_info, blk)))) {
@@ -1235,8 +1283,8 @@
}
void VisitNewInstance(HNewInstance* new_instance) override {
- // If we are in a try catch, even singletons are observable.
- const bool in_try_catch = new_instance->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool inside_a_try = new_instance->GetBlock()->IsTryBlock();
ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_instance);
if (ref_info == nullptr) {
// new_instance isn't used for field accesses. No need to process it.
@@ -1265,7 +1313,7 @@
heap_values[i].value = Value::ForInstruction(new_instance->GetLoadClass());
heap_values[i].stored_by = Value::PureUnknown();
}
- } else if (in_try_catch || IsEscapingObject(info, block, i)) {
+ } else if (inside_a_try || IsEscapingObject(info, block, i)) {
// Since NewInstance can throw, we presume all previous stores could be visible.
KeepStores(heap_values[i].stored_by);
heap_values[i].stored_by = Value::PureUnknown();
@@ -1274,8 +1322,8 @@
}
void VisitNewArray(HNewArray* new_array) override {
- // If we are in a try catch, even singletons are observable.
- const bool in_try_catch = new_array->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool inside_a_try = new_array->GetBlock()->IsTryBlock();
ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_array);
if (ref_info == nullptr) {
// new_array isn't used for array accesses. No need to process it.
@@ -1300,7 +1348,7 @@
// Array elements are set to default heap values.
heap_values[i].value = Value::Default();
heap_values[i].stored_by = Value::PureUnknown();
- } else if (in_try_catch || IsEscapingObject(info, block, i)) {
+ } else if (inside_a_try || IsEscapingObject(info, block, i)) {
// Since NewArray can throw, we presume all previous stores could be visible.
KeepStores(heap_values[i].stored_by);
heap_values[i].stored_by = Value::PureUnknown();
@@ -1704,8 +1752,7 @@
ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
DCHECK(heap_values.empty());
size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
- if (block->GetPredecessors().empty() || (block->GetTryCatchInformation() != nullptr &&
- block->GetTryCatchInformation()->IsCatchBlock())) {
+ if (block->GetPredecessors().empty() || block->IsCatchBlock()) {
DCHECK_IMPLIES(block->GetPredecessors().empty(), block->IsEntryBlock());
heap_values.resize(num_heap_locations,
{/*value=*/Value::PureUnknown(), /*stored_by=*/Value::PureUnknown()});
@@ -1764,7 +1811,6 @@
if (type == DataType::Type::kReference) {
// Update reference type information. Pass invalid handles, these are not used for Phis.
ReferenceTypePropagation rtp_fixup(block->GetGraph(),
- Handle<mirror::ClassLoader>(),
Handle<mirror::DexCache>(),
/* is_first_run= */ false);
rtp_fixup.Visit(phi);
@@ -1877,7 +1923,6 @@
}
HInstruction* heap_value = FindSubstitute(record.value.GetInstruction());
AddRemovedLoad(instruction, heap_value);
- TryRemovingNullCheck(instruction);
}
}
@@ -2352,7 +2397,6 @@
}
// Update reference type information. Pass invalid handles, these are not used for Phis.
ReferenceTypePropagation rtp_fixup(GetGraph(),
- Handle<mirror::ClassLoader>(),
Handle<mirror::DexCache>(),
/* is_first_run= */ false);
rtp_fixup.Visit(ArrayRef<HInstruction* const>(phis));
@@ -2639,7 +2683,6 @@
record.value = local_heap_values[idx];
HInstruction* heap_value = local_heap_values[idx].GetInstruction();
AddRemovedLoad(load_or_store, heap_value);
- TryRemovingNullCheck(load_or_store);
}
}
}
@@ -2698,7 +2741,6 @@
record.value = Replacement(record.value);
HInstruction* heap_value = record.value.GetInstruction();
AddRemovedLoad(load, heap_value);
- TryRemovingNullCheck(load);
}
}
}
@@ -3013,7 +3055,6 @@
return;
}
ReferenceTypePropagation rtp_fixup(GetGraph(),
- Handle<mirror::ClassLoader>(),
Handle<mirror::DexCache>(),
/* is_first_run= */ false);
rtp_fixup.Visit(ArrayRef<HInstruction* const>(new_ref_phis_));
diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h
index 6ad2eb2..42de803 100644
--- a/compiler/optimizing/load_store_elimination.h
+++ b/compiler/optimizing/load_store_elimination.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_LOAD_STORE_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_LOAD_STORE_ELIMINATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
index 02dc939..f0c2541 100644
--- a/compiler/optimizing/load_store_elimination_test.cc
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -36,7 +36,9 @@
#include "optimizing_unit_test.h"
#include "scoped_thread_state_change.h"
-namespace art {
+namespace art HIDDEN {
+
+static constexpr bool kDebugLseTests = false;
#define CHECK_SUBROUTINE_FAILURE() \
do { \
@@ -54,12 +56,16 @@
void SetUp() override {
SuperTest::SetUp();
- gLogVerbosity.compiler = true;
+ if (kDebugLseTests) {
+ gLogVerbosity.compiler = true;
+ }
}
void TearDown() override {
SuperTest::TearDown();
- gLogVerbosity.compiler = false;
+ if (kDebugLseTests) {
+ gLogVerbosity.compiler = false;
+ }
}
void PerformLSE(bool with_partial = true) {
@@ -70,12 +76,37 @@
EXPECT_TRUE(CheckGraphSkipRefTypeInfoChecks(oss)) << oss.str();
}
- void PerformLSEWithPartial() {
- PerformLSE(true);
+ void PerformLSEWithPartial(const AdjacencyListGraph& blks) {
+ // PerformLSE expects this to be empty.
+ graph_->ClearDominanceInformation();
+ if (kDebugLseTests) {
+ LOG(INFO) << "Pre LSE " << blks;
+ }
+ PerformLSE(/*with_partial=*/ true);
+ if (kDebugLseTests) {
+ LOG(INFO) << "Post LSE " << blks;
+ }
}
- void PerformLSENoPartial() {
- PerformLSE(false);
+ void PerformLSENoPartial(const AdjacencyListGraph& blks) {
+ // PerformLSE expects this to be empty.
+ graph_->ClearDominanceInformation();
+ if (kDebugLseTests) {
+ LOG(INFO) << "Pre LSE " << blks;
+ }
+ PerformLSE(/*with_partial=*/ false);
+ if (kDebugLseTests) {
+ LOG(INFO) << "Post LSE " << blks;
+ }
+ }
+
+ void PerformSimplifications(const AdjacencyListGraph& blks) {
+ InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
+ simp.Run();
+
+ if (kDebugLseTests) {
+ LOG(INFO) << "Post simplification " << blks;
+ }
}
// Create instructions shared among tests.
@@ -542,6 +573,7 @@
AddVecStore(entry_block_, array_, j_);
HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vstore));
@@ -557,6 +589,7 @@
AddVecStore(entry_block_, array_, i_add1_);
HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vstore));
@@ -601,6 +634,7 @@
AddArraySet(entry_block_, array_, i_, c1);
HInstruction* vload5 = AddVecLoad(entry_block_, array_, i_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(load1));
@@ -634,6 +668,7 @@
// a[j] = 1;
HInstruction* array_set = AddArraySet(return_block_, array_, j_, c1);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(array_set));
@@ -671,6 +706,7 @@
// a[j] = 0;
HInstruction* a_set = AddArraySet(return_block_, array_, j_, c0);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(vload));
@@ -709,6 +745,7 @@
// x = a[j];
HInstruction* load = AddArrayGet(return_block_, array_, j_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(vload));
@@ -749,6 +786,7 @@
// down: a[i,... i + 3] = [1,...1]
HInstruction* vstore4 = AddVecStore(down, array_, i_, vdata);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(vstore2));
@@ -839,6 +877,7 @@
HInstruction* vstore2 = AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
HInstruction* vstore3 = AddVecStore(loop_, array_a, phi_, vstore1->InputAt(2));
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vstore1));
@@ -894,7 +933,7 @@
// loop:
// array2[i] = array[i]
// array[0] = 2
- HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
+ HInstruction* store1 = AddArraySet(pre_header_, array_, c0, c2);
HInstruction* load = AddArrayGet(loop_, array_, phi_);
HInstruction* store2 = AddArraySet(loop_, array2, phi_, load);
@@ -926,6 +965,7 @@
HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -949,6 +989,7 @@
HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -1025,6 +1066,7 @@
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
HInstruction* store = AddArraySet(return_block_, array_, c0, load);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -1055,6 +1097,7 @@
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
HInstruction* store = AddArraySet(return_block_, array_, c0, load);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -1086,6 +1129,7 @@
HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload1));
@@ -1116,6 +1160,7 @@
HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload1));
@@ -2024,10 +2069,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSENoPartial();
+ PerformLSENoPartial(blks);
EXPECT_INS_RETAINED(read_bottom);
EXPECT_INS_RETAINED(write_c1);
@@ -2174,9 +2216,8 @@
HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
exit->AddInstruction(read_bottom);
exit->AddInstruction(return_exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- PerformLSENoPartial();
+
+ PerformLSENoPartial(blks);
EXPECT_INS_RETAINED(read_bottom) << *read_bottom;
EXPECT_INS_RETAINED(write_right) << *write_right;
@@ -2266,9 +2307,8 @@
HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom);
exit->AddInstruction(read_bottom);
exit->AddInstruction(return_exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- PerformLSENoPartial();
+
+ PerformLSENoPartial(blks);
EXPECT_INS_RETAINED(read_bottom);
EXPECT_INS_RETAINED(write_right_first);
@@ -2499,11 +2539,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HPredicatedInstanceFieldGet* pred_get =
FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_);
@@ -2656,11 +2692,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_RETAINED(call_left_left);
EXPECT_INS_REMOVED(read1);
@@ -2814,11 +2846,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HNewInstance* moved_new_inst1;
HInstanceFieldSet* moved_set1;
@@ -2954,11 +2982,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(write_entry1);
EXPECT_INS_REMOVED(write_entry2);
@@ -3115,11 +3139,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(new_inst1);
EXPECT_INS_REMOVED(new_inst2);
@@ -3205,11 +3225,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HNewInstance* moved_new_inst = nullptr;
HInstanceFieldSet* moved_set = nullptr;
@@ -3320,11 +3336,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HNewInstance* moved_new_inst = nullptr;
HInstanceFieldSet* moved_set = nullptr;
@@ -3497,11 +3509,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HNewInstance* moved_new_inst = nullptr;
HInstanceFieldSet* moved_set = nullptr;
@@ -3639,11 +3647,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HNewInstance* moved_new_inst;
HInstanceFieldSet* moved_set;
@@ -3746,11 +3750,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
// Each escaping switch path gets its own materialization block.
// Blocks:
@@ -3877,11 +3877,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_early);
EXPECT_EQ(return_early->InputAt(0), c0);
@@ -4013,11 +4009,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
// Normal LSE can get rid of these two.
EXPECT_INS_REMOVED(store_one);
@@ -4504,9 +4496,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- PerformLSENoPartial();
+ PerformLSENoPartial(blks);
EXPECT_INS_RETAINED(write_left_pre) << *write_left_pre;
EXPECT_INS_RETAINED(read_return) << *read_return;
@@ -4612,9 +4602,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- PerformLSENoPartial();
+ PerformLSENoPartial(blks);
EXPECT_INS_RETAINED(read_return);
EXPECT_INS_RETAINED(write_right);
@@ -4700,9 +4688,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- PerformLSENoPartial();
+ PerformLSENoPartial(blks);
EXPECT_INS_RETAINED(read_bottom);
EXPECT_INS_RETAINED(write_right);
@@ -4785,12 +4771,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
-
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSENoPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSENoPartial(blks);
EXPECT_INS_REMOVED(read_bottom);
EXPECT_INS_REMOVED(write_right);
@@ -4829,8 +4810,9 @@
CreateGraph(/*handles=*/&vshs);
AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
"exit",
- {{"entry", "critical_break"},
- {"entry", "partial"},
+ {{"entry", "first_block"},
+ {"first_block", "critical_break"},
+ {"first_block", "partial"},
{"partial", "merge"},
{"critical_break", "merge"},
{"merge", "left"},
@@ -4839,7 +4821,7 @@
{"right", "breturn"},
{"breturn", "exit"}}));
#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
- GET_BLOCK(entry);
+ GET_BLOCK(first_block);
GET_BLOCK(merge);
GET_BLOCK(partial);
GET_BLOCK(critical_break);
@@ -4858,12 +4840,12 @@
HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
HInstruction* if_inst = new (GetAllocator()) HIf(cmp_instructions.cmp_);
- entry->AddInstruction(cls);
- entry->AddInstruction(new_inst);
- entry->AddInstruction(write_entry);
- cmp_instructions.AddSetup(entry);
- entry->AddInstruction(cmp_instructions.cmp_);
- entry->AddInstruction(if_inst);
+ first_block->AddInstruction(cls);
+ first_block->AddInstruction(new_inst);
+ first_block->AddInstruction(write_entry);
+ cmp_instructions.AddSetup(first_block);
+ first_block->AddInstruction(cmp_instructions.cmp_);
+ first_block->AddInstruction(if_inst);
ManuallyBuildEnvFor(cls, {});
cmp_instructions.AddEnvironment(cls->GetEnvironment());
new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
@@ -4897,12 +4879,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
-
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
std::vector<HPhi*> merges;
HPredicatedInstanceFieldGet* pred_get;
@@ -5026,11 +5003,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
std::vector<HPhi*> merges;
HInstanceFieldSet* init_set =
@@ -5157,11 +5130,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
std::vector<HPhi*> merges;
HInstanceFieldSet* init_set =
@@ -5290,12 +5259,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
-
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
std::vector<HPhi*> merges;
std::vector<HInstanceFieldSet*> sets;
@@ -5424,12 +5388,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
-
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_RETAINED(write_bottom);
EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet());
@@ -5539,11 +5498,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_RETAINED(write_bottom);
EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_bottom;
@@ -5627,11 +5582,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom);
EXPECT_INS_REMOVED(write_right);
@@ -5748,11 +5699,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom1);
EXPECT_INS_REMOVED(read_bottom2);
@@ -5901,11 +5848,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom1);
EXPECT_INS_REMOVED(read_bottom2);
@@ -6078,11 +6021,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(early_exit_left_read);
EXPECT_INS_REMOVED(early_exit_right_read);
@@ -6212,11 +6151,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom);
EXPECT_INS_REMOVED(read_right);
@@ -6334,11 +6269,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom);
EXPECT_INS_REMOVED(read_early_return);
@@ -6447,11 +6378,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom);
EXPECT_INS_REMOVED(write_right);
@@ -6585,11 +6512,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom);
EXPECT_INS_REMOVED(write_right);
@@ -6688,11 +6611,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_REMOVED(read_bottom);
EXPECT_INS_RETAINED(write_left);
@@ -6861,11 +6780,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HPredicatedInstanceFieldGet* pred_get =
FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
@@ -7045,11 +6960,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HPredicatedInstanceFieldGet* pred_get =
FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
@@ -7196,11 +7107,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HPredicatedInstanceFieldGet* pred_get =
FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
@@ -7344,11 +7251,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HPredicatedInstanceFieldGet* pred_get =
FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
@@ -7492,11 +7395,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HPredicatedInstanceFieldGet* pred_get =
FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
@@ -7657,11 +7556,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSEWithPartial();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
HPredicatedInstanceFieldGet* pred_get =
FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn);
@@ -7757,17 +7652,10 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
+ PerformLSEWithPartial(blks);
// Run the code-simplifier too
- LOG(INFO) << "Pre simplification " << blks;
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post LSE " << blks;
+ PerformSimplifications(blks);
EXPECT_INS_REMOVED(write_right);
EXPECT_INS_REMOVED(write_start);
@@ -7851,17 +7739,10 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
+ PerformLSEWithPartial(blks);
// Run the code-simplifier too
- LOG(INFO) << "Pre simplification " << blks;
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post LSE " << blks;
+ PerformSimplifications(blks);
EXPECT_INS_REMOVED(write_right);
EXPECT_INS_REMOVED(write_start);
@@ -7961,17 +7842,10 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
+ PerformLSEWithPartial(blks);
// Run the code-simplifier too
- LOG(INFO) << "Pre simplification " << blks;
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post LSE " << blks;
+ PerformSimplifications(blks);
EXPECT_INS_REMOVED(write_case2);
EXPECT_INS_REMOVED(write_case3);
@@ -8069,17 +7943,10 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
+ PerformLSEWithPartial(blks);
// Run the code-simplifier too
- LOG(INFO) << "Pre simplification " << blks;
- InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
- simp.Run();
-
- LOG(INFO) << "Post LSE " << blks;
+ PerformSimplifications(blks);
EXPECT_INS_REMOVED(write_case2);
EXPECT_INS_REMOVED(write_case3);
@@ -8225,11 +8092,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_TRUE(loop_header->IsLoopHeader());
EXPECT_TRUE(loop_header->GetLoopInformation()->IsIrreducible());
@@ -8382,11 +8245,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_RETAINED(cls);
EXPECT_INS_REMOVED(new_inst);
@@ -8544,11 +8403,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_RETAINED(cls);
EXPECT_INS_REMOVED(new_inst);
@@ -8752,11 +8607,7 @@
SetupExit(exit);
- // PerformLSE expects this to be empty.
- graph_->ClearDominanceInformation();
- LOG(INFO) << "Pre LSE " << blks;
- PerformLSE();
- LOG(INFO) << "Post LSE " << blks;
+ PerformLSEWithPartial(blks);
EXPECT_INS_RETAINED(cls);
EXPECT_INS_REMOVED(new_inst);
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 5879c6f..595064f 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -21,7 +21,7 @@
#include "code_generator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Verify that Location is trivially copyable.
static_assert(std::is_trivially_copyable<Location>::value, "Location should be trivially copyable");
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index acaea71..dc87284 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -22,9 +22,10 @@
#include "base/bit_field.h"
#include "base/bit_utils.h"
#include "base/bit_vector.h"
+#include "base/macros.h"
#include "base/value_object.h"
-namespace art {
+namespace art HIDDEN {
class HConstant;
class HInstruction;
diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc
index 76bd849..95e8153 100644
--- a/compiler/optimizing/loop_analysis.cc
+++ b/compiler/optimizing/loop_analysis.cc
@@ -20,7 +20,7 @@
#include "code_generator.h"
#include "induction_var_range.h"
-namespace art {
+namespace art HIDDEN {
void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info,
LoopAnalysisInfo* analysis_results,
diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h
index fbf1516..cec00fe 100644
--- a/compiler/optimizing/loop_analysis.h
+++ b/compiler/optimizing/loop_analysis.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
#define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class InductionVarRange;
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 2d7c208..7a52502 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -27,7 +27,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
@@ -507,9 +507,8 @@
graph_->SetHasLoops(false); // no more loops
}
- // Detach.
+ // Detach allocator.
loop_allocator_ = nullptr;
- last_loop_ = top_loop_ = nullptr;
return did_loop_opt;
}
@@ -530,11 +529,7 @@
AddLoop(block->GetLoopInformation());
}
}
-
- // TODO(solanes): How can `top_loop_` be null if `graph_->HasLoops()` is true?
- if (top_loop_ == nullptr) {
- return false;
- }
+ DCHECK(top_loop_ != nullptr);
// Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use
// temporary data structures using the phase-local allocator. All new HIR
@@ -681,6 +676,50 @@
}
//
+// This optimization applies to loops with plain simple operations
+// (I.e. no calls to java code or runtime) with a known small trip_count * instr_count
+// value.
+//
+bool HLoopOptimization::TryToRemoveSuspendCheckFromLoopHeader(LoopAnalysisInfo* analysis_info,
+ bool generate_code) {
+ if (!graph_->SuspendChecksAreAllowedToNoOp()) {
+ return false;
+ }
+
+ int64_t trip_count = analysis_info->GetTripCount();
+
+ if (trip_count == LoopAnalysisInfo::kUnknownTripCount) {
+ return false;
+ }
+
+ int64_t instruction_count = analysis_info->GetNumberOfInstructions();
+ int64_t total_instruction_count = trip_count * instruction_count;
+
+ // The inclusion of the HasInstructionsPreventingScalarOpts() prevents this
+ // optimization from being applied to loops that have calls.
+ bool can_optimize =
+ total_instruction_count <= HLoopOptimization::kMaxTotalInstRemoveSuspendCheck &&
+ !analysis_info->HasInstructionsPreventingScalarOpts();
+
+ if (!can_optimize) {
+ return false;
+ }
+
+ // If we should do the optimization, disable codegen for the SuspendCheck.
+ if (generate_code) {
+ HLoopInformation* loop_info = analysis_info->GetLoopInfo();
+ HBasicBlock* header = loop_info->GetHeader();
+ HSuspendCheck* instruction = header->GetLoopInformation()->GetSuspendCheck();
+ // As other optimizations depend on SuspendCheck
+ // (e.g: CHAGuardVisitor::HoistGuard), disable its codegen instead of
+ // removing the SuspendCheck instruction.
+ instruction->SetIsNoOp(true);
+ }
+
+ return true;
+}
+
+//
// Optimization.
//
@@ -824,7 +863,7 @@
}
bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
- return TryOptimizeInnerLoopFinite(node) || TryPeelingAndUnrolling(node);
+ return TryOptimizeInnerLoopFinite(node) || TryLoopScalarOpts(node);
}
//
@@ -928,7 +967,7 @@
return true;
}
-bool HLoopOptimization::TryPeelingAndUnrolling(LoopNode* node) {
+bool HLoopOptimization::TryLoopScalarOpts(LoopNode* node) {
HLoopInformation* loop_info = node->loop_info;
int64_t trip_count = LoopAnalysis::GetLoopTripCount(loop_info, &induction_range_);
LoopAnalysisInfo analysis_info(loop_info);
@@ -941,10 +980,16 @@
if (!TryFullUnrolling(&analysis_info, /*generate_code*/ false) &&
!TryPeelingForLoopInvariantExitsElimination(&analysis_info, /*generate_code*/ false) &&
- !TryUnrollingForBranchPenaltyReduction(&analysis_info, /*generate_code*/ false)) {
+ !TryUnrollingForBranchPenaltyReduction(&analysis_info, /*generate_code*/ false) &&
+ !TryToRemoveSuspendCheckFromLoopHeader(&analysis_info, /*generate_code*/ false)) {
return false;
}
+ // Try the suspend check removal even for non-clonable loops. Also this
+ // optimization doesn't interfere with other scalar loop optimizations so it can
+ // be done prior to them.
+ bool removed_suspend_check = TryToRemoveSuspendCheckFromLoopHeader(&analysis_info);
+
// Run 'IsLoopClonable' the last as it might be time-consuming.
if (!LoopClonerHelper::IsLoopClonable(loop_info)) {
return false;
@@ -952,7 +997,7 @@
return TryFullUnrolling(&analysis_info) ||
TryPeelingForLoopInvariantExitsElimination(&analysis_info) ||
- TryUnrollingForBranchPenaltyReduction(&analysis_info);
+ TryUnrollingForBranchPenaltyReduction(&analysis_info) || removed_suspend_check;
}
//
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index b178616..6dd778b 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_LOOP_OPTIMIZATION_H_
#define ART_COMPILER_OPTIMIZING_LOOP_OPTIMIZATION_H_
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "induction_var_range.h"
@@ -25,7 +26,7 @@
#include "optimization.h"
#include "superblock_cloner.h"
-namespace art {
+namespace art HIDDEN {
class CompilerOptions;
class ArchNoOptsLoopHelper;
@@ -47,6 +48,11 @@
static constexpr const char* kLoopOptimizationPassName = "loop_optimization";
+ // The maximum number of total instructions (trip_count * instruction_count),
+ // where the optimization of removing SuspendChecks from the loop header could
+ // be performed.
+ static constexpr int64_t kMaxTotalInstRemoveSuspendCheck = 128;
+
private:
/**
* A single loop inside the loop hierarchy representation.
@@ -179,8 +185,19 @@
// should be actually applied.
bool TryFullUnrolling(LoopAnalysisInfo* analysis_info, bool generate_code = true);
- // Tries to apply scalar loop peeling and unrolling.
- bool TryPeelingAndUnrolling(LoopNode* node);
+ // Tries to remove SuspendCheck for plain loops with a low trip count. The
+ // SuspendCheck in the codegen makes sure that the thread can be interrupted
+ // during execution for GC. Not being able to do so might decrease the
+ // responsiveness of GC when a very long loop or a long recursion is being
+ // executed. However, for plain loops with a small trip count, the removal of
+ // SuspendCheck should not affect the GC's responsiveness by a large margin.
+ // Consequently, since the thread won't be interrupted for plain loops, it is
+ // assumed that the performance might increase by removing SuspendCheck.
+ bool TryToRemoveSuspendCheckFromLoopHeader(LoopAnalysisInfo* analysis_info,
+ bool generate_code = true);
+
+ // Tries to apply scalar loop optimizations.
+ bool TryLoopScalarOpts(LoopNode* node);
//
// Vectorization analysis and synthesis.
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index bda2528..7f694fb 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -14,12 +14,13 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "code_generator.h"
#include "driver/compiler_options.h"
#include "loop_optimization.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the loop optimization tests. These unit tests focus
@@ -94,10 +95,7 @@
void PerformAnalysis() {
graph_->BuildDominatorTree();
iva_->Run();
- // Do not release the loop hierarchy.
- ScopedArenaAllocator loop_allocator(GetArenaStack());
- loop_opt_->loop_allocator_ = &loop_allocator;
- loop_opt_->LocalRun();
+ loop_opt_->Run();
}
/** Constructs string representation of computed loop hierarchy. */
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index d35ed1c..0876ce0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -40,7 +40,7 @@
#include "scoped_thread_state_change-inl.h"
#include "ssa_builder.h"
-namespace art {
+namespace art HIDDEN {
// Enable floating-point static evaluation during constant folding
// only if all floating-point operations and constants evaluate in the
@@ -150,30 +150,54 @@
RemoveEnvironmentUses(instruction);
}
-void HGraph::RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const {
+void HGraph::RemoveDeadBlocksInstructionsAsUsersAndDisconnect(const ArenaBitVector& visited) const {
for (size_t i = 0; i < blocks_.size(); ++i) {
if (!visited.IsBitSet(i)) {
HBasicBlock* block = blocks_[i];
if (block == nullptr) continue;
+
+ // Remove as user.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
RemoveAsUser(it.Current());
}
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
RemoveAsUser(it.Current());
}
+
+ // Remove non-catch phi uses, and disconnect the block.
+ block->DisconnectFromSuccessors(&visited);
+ }
+ }
+}
+
+// This method assumes `insn` has been removed from all users with the exception of catch
+// phis because of missing exceptional edges in the graph. It removes the
+// instruction from catch phi uses, together with inputs of other catch phis in
+// the catch block at the same index, as these must be dead too.
+static void RemoveCatchPhiUsesOfDeadInstruction(HInstruction* insn) {
+ DCHECK(!insn->HasEnvironmentUses());
+ while (insn->HasNonEnvironmentUses()) {
+ const HUseListNode<HInstruction*>& use = insn->GetUses().front();
+ size_t use_index = use.GetIndex();
+ HBasicBlock* user_block = use.GetUser()->GetBlock();
+ DCHECK(use.GetUser()->IsPhi());
+ DCHECK(user_block->IsCatchBlock());
+ for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
}
}
}
void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) {
+ DCHECK(reverse_post_order_.empty()) << "We shouldn't have dominance information.";
for (size_t i = 0; i < blocks_.size(); ++i) {
if (!visited.IsBitSet(i)) {
HBasicBlock* block = blocks_[i];
if (block == nullptr) continue;
- // We only need to update the successor, which might be live.
- for (HBasicBlock* successor : block->GetSuccessors()) {
- successor->RemovePredecessor(block);
- }
+
+ // Remove all remaining uses (which should be only catch phi uses), and the instructions.
+ block->RemoveCatchPhiUsesAndInstruction(/* building_dominator_tree = */ true);
+
// Remove the block from the list of blocks, so that further analyses
// never see it.
blocks_[i] = nullptr;
@@ -200,7 +224,8 @@
// (2) Remove instructions and phis from blocks not visited during
// the initial DFS as users from other instructions, so that
// users can be safely removed before uses later.
- RemoveInstructionsAsUsersFromDeadBlocks(visited);
+ // Also disconnect the block from its successors, updating the successor's phis if needed.
+ RemoveDeadBlocksInstructionsAsUsersAndDisconnect(visited);
// (3) Remove blocks not visited during the initial DFS.
// Step (5) requires dead blocks to be removed from the
@@ -237,6 +262,7 @@
}
void HGraph::ClearLoopInformation() {
+ SetHasLoops(false);
SetHasIrreducibleLoops(false);
for (HBasicBlock* block : GetActiveBlocks()) {
block->SetLoopInformation(nullptr);
@@ -544,6 +570,15 @@
}
}
+HBasicBlock* HGraph::SplitEdgeAndUpdateRPO(HBasicBlock* block, HBasicBlock* successor) {
+ HBasicBlock* new_block = SplitEdge(block, successor);
+ // In the RPO we have {... , block, ... , successor}. We want to insert `new_block` right after
+ // `block` to have a consistent RPO without recomputing the whole graph's RPO.
+ reverse_post_order_.insert(
+ reverse_post_order_.begin() + IndexOfElement(reverse_post_order_, block) + 1, new_block);
+ return new_block;
+}
+
// Reorder phi inputs to match reordering of the block's predecessors.
static void FixPhisAfterPredecessorsReodering(HBasicBlock* block, size_t first, size_t second) {
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
@@ -708,6 +743,8 @@
void HGraph::ComputeTryBlockInformation() {
// Iterate in reverse post order to propagate try membership information from
// predecessors to their successors.
+ bool graph_has_try_catch = false;
+
for (HBasicBlock* block : GetReversePostOrder()) {
if (block->IsEntryBlock() || block->IsCatchBlock()) {
// Catch blocks after simplification have only exceptional predecessors
@@ -722,6 +759,7 @@
DCHECK_IMPLIES(block->IsLoopHeader(),
!block->GetLoopInformation()->IsBackEdge(*first_predecessor));
const HTryBoundary* try_entry = first_predecessor->ComputeTryEntryOfSuccessors();
+ graph_has_try_catch |= try_entry != nullptr;
if (try_entry != nullptr &&
(block->GetTryCatchInformation() == nullptr ||
try_entry != &block->GetTryCatchInformation()->GetTryEntry())) {
@@ -730,6 +768,8 @@
block->SetTryCatchInformation(new (allocator_) TryCatchInformation(*try_entry));
}
}
+
+ SetHasTryCatch(graph_has_try_catch);
}
void HGraph::SimplifyCFG() {
@@ -1459,6 +1499,10 @@
UNREACHABLE();
}
+bool HInstruction::Dominates(HInstruction* other_instruction) const {
+ return other_instruction == this || StrictlyDominates(other_instruction);
+}
+
bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const {
if (other_instruction == this) {
// An instruction does not strictly dominate itself.
@@ -1518,14 +1562,19 @@
DCHECK(env_uses_.empty());
}
-void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement) {
+void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator,
+ HInstruction* replacement,
+ bool strictly_dominated) {
const HUseList<HInstruction*>& uses = GetUses();
for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
HInstruction* user = it->GetUser();
size_t index = it->GetIndex();
// Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
++it;
- if (dominator->StrictlyDominates(user)) {
+ const bool dominated =
+ strictly_dominated ? dominator->StrictlyDominates(user) : dominator->Dominates(user);
+
+ if (dominated) {
user->ReplaceInput(replacement, index);
} else if (user->IsPhi() && !user->AsPhi()->IsCatchPhi()) {
// If the input flows from a block dominated by `dominator`, we can replace it.
@@ -2108,8 +2157,9 @@
MoveBefore(insert_pos);
}
-HBasicBlock* HBasicBlock::SplitBefore(HInstruction* cursor) {
- DCHECK(!graph_->IsInSsaForm()) << "Support for SSA form not implemented.";
+HBasicBlock* HBasicBlock::SplitBefore(HInstruction* cursor, bool require_graph_not_in_ssa_form) {
+ DCHECK_IMPLIES(require_graph_not_in_ssa_form, !graph_->IsInSsaForm())
+ << "Support for SSA form not implemented.";
DCHECK_EQ(cursor->GetBlock(), this);
HBasicBlock* new_block =
@@ -2376,24 +2426,6 @@
}
}
-// Should be called on instructions in a dead block in post order. This method
-// assumes `insn` has been removed from all users with the exception of catch
-// phis because of missing exceptional edges in the graph. It removes the
-// instruction from catch phi uses, together with inputs of other catch phis in
-// the catch block at the same index, as these must be dead too.
-static void RemoveUsesOfDeadInstruction(HInstruction* insn) {
- DCHECK(!insn->HasEnvironmentUses());
- while (insn->HasNonEnvironmentUses()) {
- const HUseListNode<HInstruction*>& use = insn->GetUses().front();
- size_t use_index = use.GetIndex();
- HBasicBlock* user_block = use.GetUser()->GetBlock();
- DCHECK(use.GetUser()->IsPhi() && user_block->IsCatchBlock());
- for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
- }
- }
-}
-
void HBasicBlock::DisconnectAndDelete() {
// Dominators must be removed after all the blocks they dominate. This way
// a loop header is removed last, a requirement for correct loop information
@@ -2418,52 +2450,14 @@
}
// (2) Disconnect the block from its successors and update their phis.
- for (HBasicBlock* successor : successors_) {
- // Delete this block from the list of predecessors.
- size_t this_index = successor->GetPredecessorIndexOf(this);
- successor->predecessors_.erase(successor->predecessors_.begin() + this_index);
-
- // Check that `successor` has other predecessors, otherwise `this` is the
- // dominator of `successor` which violates the order DCHECKed at the top.
- DCHECK(!successor->predecessors_.empty());
-
- // Remove this block's entries in the successor's phis. Skip exceptional
- // successors because catch phi inputs do not correspond to predecessor
- // blocks but throwing instructions. The inputs of the catch phis will be
- // updated in step (3).
- if (!successor->IsCatchBlock()) {
- if (successor->predecessors_.size() == 1u) {
- // The successor has just one predecessor left. Replace phis with the only
- // remaining input.
- for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- HPhi* phi = phi_it.Current()->AsPhi();
- phi->ReplaceWith(phi->InputAt(1 - this_index));
- successor->RemovePhi(phi);
- }
- } else {
- for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- phi_it.Current()->AsPhi()->RemoveInputAt(this_index);
- }
- }
- }
- }
- successors_.clear();
+ DisconnectFromSuccessors();
// (3) Remove instructions and phis. Instructions should have no remaining uses
// except in catch phis. If an instruction is used by a catch phi at `index`,
// remove `index`-th input of all phis in the catch block since they are
// guaranteed dead. Note that we may miss dead inputs this way but the
// graph will always remain consistent.
- for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* insn = it.Current();
- RemoveUsesOfDeadInstruction(insn);
- RemoveInstruction(insn);
- }
- for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
- HPhi* insn = it.Current()->AsPhi();
- RemoveUsesOfDeadInstruction(insn);
- RemovePhi(insn);
- }
+ RemoveCatchPhiUsesAndInstruction(/* building_dominator_tree = */ false);
// (4) Disconnect the block from its predecessors and update their
// control-flow instructions.
@@ -2537,6 +2531,70 @@
SetGraph(nullptr);
}
+void HBasicBlock::DisconnectFromSuccessors(const ArenaBitVector* visited) {
+ for (HBasicBlock* successor : successors_) {
+ // Delete this block from the list of predecessors.
+ size_t this_index = successor->GetPredecessorIndexOf(this);
+ successor->predecessors_.erase(successor->predecessors_.begin() + this_index);
+
+ if (visited != nullptr && !visited->IsBitSet(successor->GetBlockId())) {
+ // `successor` itself is dead. Therefore, there is no need to update its phis.
+ continue;
+ }
+
+ DCHECK(!successor->predecessors_.empty());
+
+ // Remove this block's entries in the successor's phis. Skips exceptional
+ // successors because catch phi inputs do not correspond to predecessor
+ // blocks but throwing instructions. They are removed in `RemoveCatchPhiUses`.
+ if (!successor->IsCatchBlock()) {
+ if (successor->predecessors_.size() == 1u) {
+ // The successor has just one predecessor left. Replace phis with the only
+ // remaining input.
+ for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ HPhi* phi = phi_it.Current()->AsPhi();
+ phi->ReplaceWith(phi->InputAt(1 - this_index));
+ successor->RemovePhi(phi);
+ }
+ } else {
+ for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ phi_it.Current()->AsPhi()->RemoveInputAt(this_index);
+ }
+ }
+ }
+ }
+ successors_.clear();
+}
+
+void HBasicBlock::RemoveCatchPhiUsesAndInstruction(bool building_dominator_tree) {
+ for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* insn = it.Current();
+ RemoveCatchPhiUsesOfDeadInstruction(insn);
+
+ // If we are building the dominator tree, we removed all input records previously.
+ // `RemoveInstruction` will try to remove them again but that's not something we support and we
+ // will crash. We check here since we won't be checking that in RemoveInstruction.
+ if (building_dominator_tree) {
+ DCHECK(insn->GetUses().empty());
+ DCHECK(insn->GetEnvUses().empty());
+ }
+ RemoveInstruction(insn, /* ensure_safety= */ !building_dominator_tree);
+ }
+ for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* insn = it.Current()->AsPhi();
+ RemoveCatchPhiUsesOfDeadInstruction(insn);
+
+ // If we are building the dominator tree, we removed all input records previously.
+ // `RemovePhi` will try to remove them again but that's not something we support and we
+ // will crash. We check here since we won't be checking that in RemovePhi.
+ if (building_dominator_tree) {
+ DCHECK(insn->GetUses().empty());
+ DCHECK(insn->GetEnvUses().empty());
+ }
+ RemovePhi(insn, /* ensure_safety= */ !building_dominator_tree);
+ }
+}
+
void HBasicBlock::MergeInstructionsWith(HBasicBlock* other) {
DCHECK(EndsWithControlFlowInstruction());
RemoveInstruction(GetLastInstruction());
@@ -2660,7 +2718,8 @@
void HGraph::UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block,
HBasicBlock* reference,
- bool replace_if_back_edge) {
+ bool replace_if_back_edge,
+ bool has_more_specific_try_catch_info) {
if (block->IsLoopHeader()) {
// Clear the information of which blocks are contained in that loop. Since the
// information is stored as a bit vector based on block ids, we have to update
@@ -2687,11 +2746,16 @@
}
}
- // Copy TryCatchInformation if `reference` is a try block, not if it is a catch block.
- TryCatchInformation* try_catch_info = reference->IsTryBlock()
- ? reference->GetTryCatchInformation()
- : nullptr;
- block->SetTryCatchInformation(try_catch_info);
+ DCHECK_IMPLIES(has_more_specific_try_catch_info, !reference->IsTryBlock())
+ << "We don't allow to inline try catches inside of other try blocks.";
+
+ // Update the TryCatchInformation, if we are not inlining a try catch.
+ if (!has_more_specific_try_catch_info) {
+ // Copy TryCatchInformation if `reference` is a try block, not if it is a catch block.
+ TryCatchInformation* try_catch_info =
+ reference->IsTryBlock() ? reference->GetTryCatchInformation() : nullptr;
+ block->SetTryCatchInformation(try_catch_info);
+ }
}
HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
@@ -2730,9 +2794,15 @@
if (HasTryCatch()) {
outer_graph->SetHasTryCatch(true);
}
+ if (HasMonitorOperations()) {
+ outer_graph->SetHasMonitorOperations(true);
+ }
if (HasSIMD()) {
outer_graph->SetHasSIMD(true);
}
+ if (HasAlwaysThrowingInvokes()) {
+ outer_graph->SetHasAlwaysThrowingInvokes(true);
+ }
HInstruction* return_value = nullptr;
if (GetBlocks().size() == 3) {
@@ -2771,6 +2841,7 @@
HBasicBlock* first = entry_block_->GetSuccessors()[0];
DCHECK(!first->IsInLoop());
+ DCHECK(first->GetTryCatchInformation() == nullptr);
at->MergeWithInlined(first);
exit_block_->ReplaceWith(to);
@@ -2801,12 +2872,14 @@
// and (4) to the blocks that apply.
for (HBasicBlock* current : GetReversePostOrder()) {
if (current != exit_block_ && current != entry_block_ && current != first) {
- DCHECK(current->GetTryCatchInformation() == nullptr);
DCHECK(current->GetGraph() == this);
current->SetGraph(outer_graph);
outer_graph->AddBlock(current);
outer_graph->reverse_post_order_[++index_of_at] = current;
- UpdateLoopAndTryInformationOfNewBlock(current, at, /* replace_if_back_edge= */ false);
+ UpdateLoopAndTryInformationOfNewBlock(current,
+ at,
+ /* replace_if_back_edge= */ false,
+ current->GetTryCatchInformation() != nullptr);
}
}
@@ -2820,25 +2893,62 @@
// Update all predecessors of the exit block (now the `to` block)
// to not `HReturn` but `HGoto` instead. Special case throwing blocks
- // to now get the outer graph exit block as successor. Note that the inliner
- // currently doesn't support inlining methods with try/catch.
+ // to now get the outer graph exit block as successor.
HPhi* return_value_phi = nullptr;
bool rerun_dominance = false;
bool rerun_loop_analysis = false;
for (size_t pred = 0; pred < to->GetPredecessors().size(); ++pred) {
HBasicBlock* predecessor = to->GetPredecessors()[pred];
HInstruction* last = predecessor->GetLastInstruction();
+
+ // At this point we might either have:
+ // A) Return/ReturnVoid/Throw as the last instruction, or
+ // B) `Return/ReturnVoid/Throw->TryBoundary` as the last instruction chain
+
+ const bool saw_try_boundary = last->IsTryBoundary();
+ if (saw_try_boundary) {
+ DCHECK(predecessor->IsSingleTryBoundary());
+ DCHECK(!last->AsTryBoundary()->IsEntry());
+ predecessor = predecessor->GetSinglePredecessor();
+ last = predecessor->GetLastInstruction();
+ }
+
if (last->IsThrow()) {
- DCHECK(!at->IsTryBlock());
- predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock());
+ if (at->IsTryBlock()) {
+ DCHECK(!saw_try_boundary) << "We don't support inlining of try blocks into try blocks.";
+ // Create a TryBoundary of kind:exit and point it to the Exit block.
+ HBasicBlock* new_block = outer_graph->SplitEdge(predecessor, to);
+ new_block->AddInstruction(
+ new (allocator) HTryBoundary(HTryBoundary::BoundaryKind::kExit, last->GetDexPc()));
+ new_block->ReplaceSuccessor(to, outer_graph->GetExitBlock());
+
+ // Copy information from the predecessor.
+ new_block->SetLoopInformation(predecessor->GetLoopInformation());
+ TryCatchInformation* try_catch_info = predecessor->GetTryCatchInformation();
+ new_block->SetTryCatchInformation(try_catch_info);
+ for (HBasicBlock* xhandler :
+ try_catch_info->GetTryEntry().GetBlock()->GetExceptionalSuccessors()) {
+ new_block->AddSuccessor(xhandler);
+ }
+ DCHECK(try_catch_info->GetTryEntry().HasSameExceptionHandlersAs(
+ *new_block->GetLastInstruction()->AsTryBoundary()));
+ } else {
+ // We either have `Throw->TryBoundary` or `Throw`. We want to point the whole chain to the
+ // exit, so we recompute `predecessor`
+ predecessor = to->GetPredecessors()[pred];
+ predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock());
+ }
+
--pred;
// We need to re-run dominance information, as the exit block now has
- // a new dominator.
+ // a new predecessor and potential new dominator.
+ // TODO(solanes): See if it's worth it to hand-modify the domination chain instead of
+ // rerunning the dominance for the whole graph.
rerun_dominance = true;
if (predecessor->GetLoopInformation() != nullptr) {
- // The exit block and blocks post dominated by the exit block do not belong
- // to any loop. Because we do not compute the post dominators, we need to re-run
- // loop analysis to get the loop information correct.
+ // The loop information might have changed e.g. `predecessor` might not be in a loop
+ // anymore. We only do this if `predecessor` has loop information as it is impossible for
+ // predecessor to end up in a loop if it wasn't in one before.
rerun_loop_analysis = true;
}
} else {
@@ -2863,6 +2973,19 @@
}
predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
predecessor->RemoveInstruction(last);
+
+ if (saw_try_boundary) {
+ predecessor = to->GetPredecessors()[pred];
+ DCHECK(predecessor->EndsWithTryBoundary());
+ DCHECK_EQ(predecessor->GetNormalSuccessors().size(), 1u);
+ if (predecessor->GetSuccessors()[0]->GetPredecessors().size() > 1) {
+ outer_graph->SplitCriticalEdge(predecessor, to);
+ rerun_dominance = true;
+ if (predecessor->GetLoopInformation() != nullptr) {
+ rerun_loop_analysis = true;
+ }
+ }
+ }
}
}
if (rerun_loop_analysis) {
@@ -3047,6 +3170,7 @@
HSuspendCheck* suspend_check = new (allocator_) HSuspendCheck(header->GetDexPc());
new_header->AddInstruction(suspend_check);
new_body->AddInstruction(new (allocator_) HGoto());
+ DCHECK(loop->GetSuspendCheck() != nullptr);
suspend_check->CopyEnvironmentFromWithLoopPhiAdjustment(
loop->GetSuspendCheck()->GetEnvironment(), header);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 7a0059f..58ce9ce 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -29,6 +29,7 @@
#include "base/array_ref.h"
#include "base/intrusive_forward_list.h"
#include "base/iteration_range.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/quasi_atomic.h"
#include "base/stl_util.h"
@@ -51,7 +52,7 @@
#include "mirror/method_type.h"
#include "offsets.h"
-namespace art {
+namespace art HIDDEN {
class ArenaStack;
class CodeGenerator;
@@ -406,6 +407,7 @@
has_loops_(false),
has_irreducible_loops_(false),
has_direct_critical_native_call_(false),
+ has_always_throwing_invokes_(false),
dead_reference_safe_(dead_reference_safe),
debuggable_(debuggable),
current_instruction_id_(start_instruction_id),
@@ -485,9 +487,11 @@
// Update the loop and try membership of `block`, which was spawned from `reference`.
// In case `reference` is a back edge, `replace_if_back_edge` notifies whether `block`
// should be the new back edge.
+ // `has_more_specific_try_catch_info` will be set to true when inlining a try catch.
void UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block,
HBasicBlock* reference,
- bool replace_if_back_edge);
+ bool replace_if_back_edge,
+ bool has_more_specific_try_catch_info = false);
// Need to add a couple of blocks to test if the loop body is entered and
// put deoptimization instructions, etc.
@@ -510,6 +514,11 @@
HBasicBlock* SplitEdge(HBasicBlock* block, HBasicBlock* successor);
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
+
+ // Splits the edge between `block` and `successor` and then updates the graph's RPO to keep
+ // consistency without recomputing the whole graph.
+ HBasicBlock* SplitEdgeAndUpdateRPO(HBasicBlock* block, HBasicBlock* successor);
+
void OrderLoopHeaderPredecessors(HBasicBlock* header);
// Transform a loop into a format with a single preheader.
@@ -678,6 +687,13 @@
return cha_single_implementation_list_;
}
+ // In case of OSR we intend to use SuspendChecks as an entry point to the
+ // function; for debuggable graphs we might deoptimize to interpreter from
+ // SuspendChecks. In these cases we should always generate code for them.
+ bool SuspendChecksAreAllowedToNoOp() const {
+ return !IsDebuggable() && !IsCompilingOsr();
+ }
+
void AddCHASingleImplementationDependency(ArtMethod* method) {
cha_single_implementation_list_.insert(method);
}
@@ -704,6 +720,9 @@
bool HasDirectCriticalNativeCall() const { return has_direct_critical_native_call_; }
void SetHasDirectCriticalNativeCall(bool value) { has_direct_critical_native_call_ = value; }
+ bool HasAlwaysThrowingInvokes() const { return has_always_throwing_invokes_; }
+ void SetHasAlwaysThrowingInvokes(bool value) { has_always_throwing_invokes_ = value; }
+
ArtMethod* GetArtMethod() const { return art_method_; }
void SetArtMethod(ArtMethod* method) { art_method_ = method; }
@@ -719,12 +738,12 @@
return ReferenceTypeInfo::Create(handle_cache_.GetObjectClassHandle(), /* is_exact= */ false);
}
- uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; }
+ uint32_t GetNumberOfCHAGuards() const { return number_of_cha_guards_; }
void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; }
void IncrementNumberOfCHAGuards() { number_of_cha_guards_++; }
private:
- void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
+ void RemoveDeadBlocksInstructionsAsUsersAndDisconnect(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited);
template <class InstructionType, typename ValueType>
@@ -792,14 +811,11 @@
size_t temporaries_vreg_slots_;
// Flag whether there are bounds checks in the graph. We can skip
- // BCE if it's false. It's only best effort to keep it up to date in
- // the presence of code elimination so there might be false positives.
+ // BCE if it's false.
bool has_bounds_checks_;
// Flag whether there are try/catch blocks in the graph. We will skip
- // try/catch-related passes if it's false. It's only best effort to keep
- // it up to date in the presence of code elimination so there might be
- // false positives.
+ // try/catch-related passes if it's false.
bool has_try_catch_;
// Flag whether there are any HMonitorOperation in the graph. If yes this will mandate
@@ -812,20 +828,19 @@
bool has_simd_;
// Flag whether there are any loops in the graph. We can skip loop
- // optimization if it's false. It's only best effort to keep it up
- // to date in the presence of code elimination so there might be false
- // positives.
+ // optimization if it's false.
bool has_loops_;
- // Flag whether there are any irreducible loops in the graph. It's only
- // best effort to keep it up to date in the presence of code elimination
- // so there might be false positives.
+ // Flag whether there are any irreducible loops in the graph.
bool has_irreducible_loops_;
// Flag whether there are any direct calls to native code registered
// for @CriticalNative methods.
bool has_direct_critical_native_call_;
+ // Flag whether the graph contains invokes that always throw.
+ bool has_always_throwing_invokes_;
+
// Is the code known to be robust against eliminating dead references
// and the effects of early finalization? If false, dead reference variables
// are kept if they might be visible to the garbage collector.
@@ -1291,7 +1306,7 @@
// graph, create a Goto at the end of the former block and will create an edge
// between the blocks. It will not, however, update the reverse post order or
// loop and try/catch information.
- HBasicBlock* SplitBefore(HInstruction* cursor);
+ HBasicBlock* SplitBefore(HInstruction* cursor, bool require_graph_not_in_ssa_form = true);
// Split the block into two blocks just before `cursor`. Returns the newly
// created block. Note that this method just updates raw block information,
@@ -1332,6 +1347,20 @@
// are safely updated.
void DisconnectAndDelete();
+ // Disconnects `this` from all its successors and updates their phis, if the successors have them.
+ // If `visited` is provided, it will use the information to know if a successor is reachable and
+ // skip updating those phis.
+ void DisconnectFromSuccessors(const ArenaBitVector* visited = nullptr);
+
+ // Removes the catch phi uses of the instructions in `this`, and then remove the instruction
+ // itself. If `building_dominator_tree` is true, it will not remove the instruction as user, since
+ // we do it in a previous step. This is a special case for building up the dominator tree: we want
+ // to eliminate uses before inputs but we don't have domination information, so we remove all
+ // connections from input/uses first before removing any instruction.
+ // This method assumes the instructions have been removed from all users with the exception of
+ // catch phis because of missing exceptional edges in the graph.
+ void RemoveCatchPhiUsesAndInstruction(bool building_dominator_tree);
+
void AddInstruction(HInstruction* instruction);
// Insert `instruction` before/after an existing instruction `cursor`.
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
@@ -1540,10 +1569,10 @@
M(Min, BinaryOperation) \
M(MonitorOperation, Instruction) \
M(Mul, BinaryOperation) \
- M(NativeDebugInfo, Instruction) \
M(Neg, UnaryOperation) \
M(NewArray, Instruction) \
M(NewInstance, Instruction) \
+ M(Nop, Instruction) \
M(Not, UnaryOperation) \
M(NotEqual, Condition) \
M(NullConstant, Instruction) \
@@ -2408,7 +2437,7 @@
!CanThrow() &&
!IsSuspendCheck() &&
!IsControlFlow() &&
- !IsNativeDebugInfo() &&
+ !IsNop() &&
!IsParameterValue() &&
// If we added an explicit barrier then we should keep it.
!IsMemoryBarrier() &&
@@ -2419,9 +2448,12 @@
return IsRemovable() && !HasUses();
}
- // Does this instruction strictly dominate `other_instruction`?
- // Returns false if this instruction and `other_instruction` are the same.
- // Aborts if this instruction and `other_instruction` are both phis.
+ // Does this instruction dominate `other_instruction`?
+ // Aborts if this instruction and `other_instruction` are different phis.
+ bool Dominates(HInstruction* other_instruction) const;
+
+ // Same but with `strictly dominates` i.e. returns false if this instruction and
+ // `other_instruction` are the same.
bool StrictlyDominates(HInstruction* other_instruction) const;
int GetId() const { return id_; }
@@ -2486,7 +2518,9 @@
void SetLocations(LocationSummary* locations) { locations_ = locations; }
void ReplaceWith(HInstruction* instruction);
- void ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement);
+ void ReplaceUsesDominatedBy(HInstruction* dominator,
+ HInstruction* replacement,
+ bool strictly_dominated = true);
void ReplaceEnvUsesDominatedBy(HInstruction* dominator, HInstruction* replacement);
void ReplaceInput(HInstruction* replacement, size_t index);
@@ -4700,7 +4734,7 @@
void SetAlwaysThrows(bool always_throws) { SetPackedFlag<kFlagAlwaysThrows>(always_throws); }
- bool AlwaysThrows() const override { return GetPackedFlag<kFlagAlwaysThrows>(); }
+ bool AlwaysThrows() const override final { return GetPackedFlag<kFlagAlwaysThrows>(); }
bool CanBeMoved() const override { return IsIntrinsic() && !DoesAnyWrite(); }
@@ -6362,6 +6396,27 @@
const FieldInfo field_info_;
};
+enum class WriteBarrierKind {
+ // Emit the write barrier, with a runtime optimization which checks if the value that it is being
+ // set is null.
+ kEmitWithNullCheck,
+ // Emit the write barrier, without the runtime null check optimization. This could be set because:
+ // A) It is a write barrier for an ArraySet (which does the optimization with the type check, so
+ // it never does the optimization at the write barrier stage)
+ // B) We know that the input can't be null
+ // C) This write barrier is actually several write barriers coalesced into one. Potentially we
+ // could ask if every value is null for a runtime optimization at the cost of compile time / code
+ // size. At the time of writing it was deemed not worth the effort.
+ kEmitNoNullCheck,
+ // Skip emitting the write barrier. This could be set because:
+ // A) The write barrier is not needed (e.g. it is not a reference, or the value is the null
+ // constant)
+ // B) This write barrier was coalesced into another one so there's no need to emit it.
+ kDontEmit,
+ kLast = kDontEmit
+};
+std::ostream& operator<<(std::ostream& os, WriteBarrierKind rhs);
+
class HInstanceFieldSet final : public HExpression<2> {
public:
HInstanceFieldSet(HInstruction* object,
@@ -6386,6 +6441,7 @@
dex_file) {
SetPackedFlag<kFlagValueCanBeNull>(true);
SetPackedFlag<kFlagIsPredicatedSet>(false);
+ SetPackedField<WriteBarrierKindField>(WriteBarrierKind::kEmitWithNullCheck);
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
@@ -6406,6 +6462,12 @@
void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
bool GetIsPredicatedSet() const { return GetPackedFlag<kFlagIsPredicatedSet>(); }
void SetIsPredicatedSet(bool value = true) { SetPackedFlag<kFlagIsPredicatedSet>(value); }
+ WriteBarrierKind GetWriteBarrierKind() { return GetPackedField<WriteBarrierKindField>(); }
+ void SetWriteBarrierKind(WriteBarrierKind kind) {
+ DCHECK(kind != WriteBarrierKind::kEmitWithNullCheck)
+ << "We shouldn't go back to the original value.";
+ SetPackedField<WriteBarrierKindField>(kind);
+ }
DECLARE_INSTRUCTION(InstanceFieldSet);
@@ -6415,11 +6477,17 @@
private:
static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
static constexpr size_t kFlagIsPredicatedSet = kFlagValueCanBeNull + 1;
- static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagIsPredicatedSet + 1;
+ static constexpr size_t kWriteBarrierKind = kFlagIsPredicatedSet + 1;
+ static constexpr size_t kWriteBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(WriteBarrierKind::kLast));
+ static constexpr size_t kNumberOfInstanceFieldSetPackedBits =
+ kWriteBarrierKind + kWriteBarrierKindSize;
static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
const FieldInfo field_info_;
+ using WriteBarrierKindField =
+ BitField<WriteBarrierKind, kWriteBarrierKind, kWriteBarrierKindSize>;
};
class HArrayGet final : public HExpression<2> {
@@ -6540,6 +6608,8 @@
SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == DataType::Type::kReference);
SetPackedFlag<kFlagValueCanBeNull>(true);
SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(false);
+ // ArraySets never do the null check optimization at the write barrier stage.
+ SetPackedField<WriteBarrierKindField>(WriteBarrierKind::kEmitNoNullCheck);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -6560,8 +6630,10 @@
return false;
}
- void ClearNeedsTypeCheck() {
+ void ClearTypeCheck() {
SetPackedFlag<kFlagNeedsTypeCheck>(false);
+ // Clear the `CanTriggerGC` flag too as we can only trigger a GC when doing a type check.
+ SetSideEffects(GetSideEffects().Exclusion(SideEffects::CanTriggerGC()));
}
void ClearValueCanBeNull() {
@@ -6610,6 +6682,16 @@
: SideEffects::None();
}
+ WriteBarrierKind GetWriteBarrierKind() { return GetPackedField<WriteBarrierKindField>(); }
+
+ void SetWriteBarrierKind(WriteBarrierKind kind) {
+ DCHECK(kind != WriteBarrierKind::kEmitNoNullCheck)
+ << "We shouldn't go back to the original value.";
+ DCHECK(kind != WriteBarrierKind::kEmitWithNullCheck)
+ << "We never do the null check optimization for ArraySets.";
+ SetPackedField<WriteBarrierKindField>(kind);
+ }
+
DECLARE_INSTRUCTION(ArraySet);
protected:
@@ -6625,11 +6707,16 @@
// Cached information for the reference_type_info_ so that codegen
// does not need to inspect the static type.
static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1;
- static constexpr size_t kNumberOfArraySetPackedBits =
- kFlagStaticTypeOfArrayIsObjectArray + 1;
+ static constexpr size_t kWriteBarrierKind = kFlagStaticTypeOfArrayIsObjectArray + 1;
+ static constexpr size_t kWriteBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(WriteBarrierKind::kLast));
+ static constexpr size_t kNumberOfArraySetPackedBits = kWriteBarrierKind + kWriteBarrierKindSize;
static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using ExpectedComponentTypeField =
BitField<DataType::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>;
+
+ using WriteBarrierKindField =
+ BitField<WriteBarrierKind, kWriteBarrierKind, kWriteBarrierKindSize>;
};
class HArrayLength final : public HExpression<1> {
@@ -6714,9 +6801,10 @@
class HSuspendCheck final : public HExpression<0> {
public:
- explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
+ explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc, bool is_no_op = false)
: HExpression(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc),
slow_path_(nullptr) {
+ SetPackedFlag<kFlagIsNoOp>(is_no_op);
}
bool IsClonable() const override { return true; }
@@ -6725,6 +6813,10 @@
return true;
}
+ void SetIsNoOp(bool is_no_op) { SetPackedFlag<kFlagIsNoOp>(is_no_op); }
+ bool IsNoOp() const { return GetPackedFlag<kFlagIsNoOp>(); }
+
+
void SetSlowPath(SlowPathCode* slow_path) { slow_path_ = slow_path; }
SlowPathCode* GetSlowPath() const { return slow_path_; }
@@ -6733,28 +6825,42 @@
protected:
DEFAULT_COPY_CONSTRUCTOR(SuspendCheck);
+ // True if the HSuspendCheck should not emit any code during codegen. It is
+ // not possible to simply remove this instruction to disable codegen, as
+ // other optimizations (e.g: CHAGuardVisitor::HoistGuard) depend on
+ // HSuspendCheck being present in every loop.
+ static constexpr size_t kFlagIsNoOp = kNumberOfGenericPackedBits;
+ static constexpr size_t kNumberOfSuspendCheckPackedBits = kFlagIsNoOp + 1;
+ static_assert(kNumberOfSuspendCheckPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
private:
// Only used for code generation, in order to share the same slow path between back edges
// of a same loop.
SlowPathCode* slow_path_;
};
-// Pseudo-instruction which provides the native debugger with mapping information.
-// It ensures that we can generate line number and local variables at this point.
-class HNativeDebugInfo : public HExpression<0> {
+// Pseudo-instruction which doesn't generate any code.
+// If `emit_environment` is true, it can be used to generate an environment. It is used, for
+// example, to provide the native debugger with mapping information. It ensures that we can generate
+// line number and local variables at this point.
+class HNop : public HExpression<0> {
public:
- explicit HNativeDebugInfo(uint32_t dex_pc)
- : HExpression<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) {
+ explicit HNop(uint32_t dex_pc, bool needs_environment)
+ : HExpression<0>(kNop, SideEffects::None(), dex_pc), needs_environment_(needs_environment) {
}
bool NeedsEnvironment() const override {
- return true;
+ return needs_environment_;
}
- DECLARE_INSTRUCTION(NativeDebugInfo);
+ DECLARE_INSTRUCTION(Nop);
protected:
- DEFAULT_COPY_CONSTRUCTOR(NativeDebugInfo);
+ DEFAULT_COPY_CONSTRUCTOR(Nop);
+
+ private:
+ bool needs_environment_;
};
/**
@@ -7222,6 +7328,10 @@
return SideEffects::CanTriggerGC();
}
+ bool CanThrow() const override { return true; }
+
+ bool NeedsEnvironment() const override { return true; }
+
DECLARE_INSTRUCTION(LoadMethodHandle);
protected:
@@ -7266,6 +7376,10 @@
return SideEffects::CanTriggerGC();
}
+ bool CanThrow() const override { return true; }
+
+ bool NeedsEnvironment() const override { return true; }
+
DECLARE_INSTRUCTION(LoadMethodType);
protected:
@@ -7400,6 +7514,7 @@
declaring_class_def_index,
dex_file) {
SetPackedFlag<kFlagValueCanBeNull>(true);
+ SetPackedField<WriteBarrierKindField>(WriteBarrierKind::kEmitWithNullCheck);
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
}
@@ -7415,6 +7530,13 @@
bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
+ WriteBarrierKind GetWriteBarrierKind() { return GetPackedField<WriteBarrierKindField>(); }
+ void SetWriteBarrierKind(WriteBarrierKind kind) {
+ DCHECK(kind != WriteBarrierKind::kEmitWithNullCheck)
+ << "We shouldn't go back to the original value.";
+ SetPackedField<WriteBarrierKindField>(kind);
+ }
+
DECLARE_INSTRUCTION(StaticFieldSet);
protected:
@@ -7422,25 +7544,34 @@
private:
static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
- static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1;
+ static constexpr size_t kWriteBarrierKind = kFlagValueCanBeNull + 1;
+ static constexpr size_t kWriteBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(WriteBarrierKind::kLast));
+ static constexpr size_t kNumberOfStaticFieldSetPackedBits =
+ kWriteBarrierKind + kWriteBarrierKindSize;
static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
const FieldInfo field_info_;
+ using WriteBarrierKindField =
+ BitField<WriteBarrierKind, kWriteBarrierKind, kWriteBarrierKindSize>;
};
class HStringBuilderAppend final : public HVariableInputSizeInstruction {
public:
HStringBuilderAppend(HIntConstant* format,
uint32_t number_of_arguments,
+ bool has_fp_args,
ArenaAllocator* allocator,
uint32_t dex_pc)
: HVariableInputSizeInstruction(
kStringBuilderAppend,
DataType::Type::kReference,
- // The runtime call may read memory from inputs. It never writes outside
- // of the newly allocated result object (or newly allocated helper objects).
- SideEffects::AllReads().Union(SideEffects::CanTriggerGC()),
+ SideEffects::CanTriggerGC().Union(
+ // The runtime call may read memory from inputs. It never writes outside
+ // of the newly allocated result object or newly allocated helper objects,
+ // except for float/double arguments where we reuse thread-local helper objects.
+ has_fp_args ? SideEffects::AllWritesAndReads() : SideEffects::AllReads()),
dex_pc,
allocator,
number_of_arguments + /* format */ 1u,
@@ -8393,7 +8524,7 @@
#include "nodes_x86.h"
#endif
-namespace art {
+namespace art HIDDEN {
class OptimizingCompilerStats;
diff --git a/compiler/optimizing/nodes_shared.cc b/compiler/optimizing/nodes_shared.cc
index eca97d7..b3a7ad9 100644
--- a/compiler/optimizing/nodes_shared.cc
+++ b/compiler/optimizing/nodes_shared.cc
@@ -23,7 +23,7 @@
#include "instruction_simplifier_shared.h"
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index 7dcac17..27e6103 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -22,7 +22,7 @@
// (defining `HInstruction` and co).
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class HMultiplyAccumulate final : public HExpression<3> {
public:
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 34f0e9b..29210fe 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -17,11 +17,12 @@
#include "nodes.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "optimizing_unit_test.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class NodeTest : public OptimizingUnitTest {};
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index a2cd86d..73f6c40 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -21,7 +21,7 @@
// is included in the header file nodes.h itself. However it gives editing tools better context.
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Memory alignment, represented as an offset relative to a base, where 0 <= offset < base,
// and base is a power of two. For example, the value Alignment(16, 0) means memory is
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index b0a665d..e0a48db 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -15,10 +15,11 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for testing vector nodes.
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 8e8fbc1..e246390 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -17,7 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_NODES_X86_H_
#define ART_COMPILER_OPTIMIZING_NODES_X86_H_
-namespace art {
+namespace art HIDDEN {
// Compute the address of the method for X86 Constant area support.
class HX86ComputeBaseMethodAddress final : public HExpression<0> {
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 2cac38b..e1b97b4 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -55,10 +55,11 @@
#include "select_generator.h"
#include "sharpening.h"
#include "side_effects_analysis.h"
+#include "write_barrier_elimination.h"
// Decide between default or alternative pass name.
-namespace art {
+namespace art HIDDEN {
const char* OptimizationPassName(OptimizationPass pass) {
switch (pass) {
@@ -95,6 +96,8 @@
return ConstructorFenceRedundancyElimination::kCFREPassName;
case OptimizationPass::kScheduling:
return HInstructionScheduling::kInstructionSchedulingPassName;
+ case OptimizationPass::kWriteBarrierElimination:
+ return WriteBarrierElimination::kWBEPassName;
#ifdef ART_ENABLE_CODEGEN_arm
case OptimizationPass::kInstructionSimplifierArm:
return arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName;
@@ -194,7 +197,8 @@
opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, pass_name);
break;
case OptimizationPass::kInductionVarAnalysis:
- opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, pass_name);
+ opt = most_recent_induction =
+ new (allocator) HInductionVarAnalysis(graph, stats, pass_name);
break;
//
// Passes that need prior analysis.
@@ -221,7 +225,7 @@
// Regular passes.
//
case OptimizationPass::kConstantFolding:
- opt = new (allocator) HConstantFolding(graph, pass_name);
+ opt = new (allocator) HConstantFolding(graph, stats, pass_name);
break;
case OptimizationPass::kDeadCodeElimination:
opt = new (allocator) HDeadCodeElimination(graph, stats, pass_name);
@@ -239,6 +243,7 @@
/* total_number_of_instructions= */ 0,
/* parent= */ nullptr,
/* depth= */ 0,
+ /* try_catch_inlining_allowed= */ true,
pass_name);
break;
}
@@ -267,6 +272,9 @@
case OptimizationPass::kLoadStoreElimination:
opt = new (allocator) LoadStoreElimination(graph, stats, pass_name);
break;
+ case OptimizationPass::kWriteBarrierElimination:
+ opt = new (allocator) WriteBarrierElimination(graph, stats, pass_name);
+ break;
case OptimizationPass::kScheduling:
opt = new (allocator) HInstructionScheduling(
graph, codegen->GetCompilerOptions().GetInstructionSet(), codegen, pass_name);
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index 2113df0..c3ba175 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -18,10 +18,11 @@
#define ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_
#include "base/arena_object.h"
+#include "base/macros.h"
#include "nodes.h"
#include "optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
@@ -83,6 +84,7 @@
kScheduling,
kSelectGenerator,
kSideEffectsAnalysis,
+ kWriteBarrierElimination,
#ifdef ART_ENABLE_CODEGEN_arm
kInstructionSimplifierArm,
kCriticalNativeAbiFixupArm,
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index 73e1fbe..f12e748 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -18,6 +18,7 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/macros.h"
#include "base/runtime_debug.h"
#include "cfi_test.h"
#include "driver/compiler_options.h"
@@ -32,7 +33,7 @@
namespace vixl32 = vixl::aarch32;
-namespace art {
+namespace art HIDDEN {
// Run the tests only on host.
#ifndef ART_TARGET_ANDROID
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 6eb3d01..dbf247c 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -33,12 +33,11 @@
#include "base/timing_logger.h"
#include "builder.h"
#include "code_generator.h"
-#include "compiled_method.h"
#include "compiler.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "dex/dex_file_types.h"
-#include "driver/compiled_method_storage.h"
+#include "driver/compiled_code_storage.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
#include "graph_checker.h"
@@ -52,6 +51,7 @@
#include "linker/linker_patch.h"
#include "nodes.h"
#include "oat_quick_method_header.h"
+#include "optimizing/write_barrier_elimination.h"
#include "prepare_for_register_allocation.h"
#include "reference_type_propagation.h"
#include "register_allocator_linear_scan.h"
@@ -62,7 +62,7 @@
#include "stack_map_stream.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kArenaAllocatorMemoryReportThreshold = 8 * MB;
@@ -269,7 +269,7 @@
class OptimizingCompiler final : public Compiler {
public:
explicit OptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage);
+ CompiledCodeStorage* storage);
~OptimizingCompiler() override;
bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file) const override;
@@ -359,11 +359,11 @@
const DexCompilationUnit& dex_compilation_unit,
PassObserver* pass_observer) const;
- private:
// Create a 'CompiledMethod' for an optimized graph.
CompiledMethod* Emit(ArenaAllocator* allocator,
CodeVectorAllocator* code_allocator,
CodeGenerator* codegen,
+ bool is_intrinsic,
const dex::CodeItem* item) const;
// Try compiling a method and return the code generator used for
@@ -413,7 +413,7 @@
static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */
OptimizingCompiler::OptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage)
+ CompiledCodeStorage* storage)
: Compiler(compiler_options, storage, kMaximumCompilationTimeBeforeWarning) {
// Enable C1visualizer output.
const std::string& cfg_file_name = compiler_options.GetDumpCfgFileName();
@@ -671,17 +671,25 @@
"constant_folding$after_bce"),
OptDef(OptimizationPass::kAggressiveInstructionSimplifier,
"instruction_simplifier$after_bce"),
+ OptDef(OptimizationPass::kDeadCodeElimination,
+ "dead_code_elimination$after_bce"),
// Other high-level optimizations.
OptDef(OptimizationPass::kLoadStoreElimination),
- OptDef(OptimizationPass::kCHAGuardOptimization),
OptDef(OptimizationPass::kDeadCodeElimination,
- "dead_code_elimination$final"),
+ "dead_code_elimination$after_lse",
+ OptimizationPass::kLoadStoreElimination),
+ OptDef(OptimizationPass::kCHAGuardOptimization),
OptDef(OptimizationPass::kCodeSinking),
// The codegen has a few assumptions that only the instruction simplifier
// can satisfy. For example, the code generator does not expect to see a
// HTypeConversion from a type to the same type.
OptDef(OptimizationPass::kAggressiveInstructionSimplifier,
"instruction_simplifier$before_codegen"),
+ // Simplification may result in dead code that should be removed prior to
+ // code generation.
+ OptDef(OptimizationPass::kDeadCodeElimination,
+ "dead_code_elimination$before_codegen",
+ OptimizationPass::kAggressiveInstructionSimplifier),
// Eliminate constructor fences after code sinking to avoid
// complicated sinking logic to split a fence with many inputs.
OptDef(OptimizationPass::kConstructorFenceRedundancyElimination)
@@ -711,18 +719,19 @@
CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator,
CodeVectorAllocator* code_allocator,
CodeGenerator* codegen,
+ bool is_intrinsic,
const dex::CodeItem* code_item_for_osr_check) const {
ArenaVector<linker::LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen);
ScopedArenaVector<uint8_t> stack_map = codegen->BuildStackMaps(code_item_for_osr_check);
- CompiledMethodStorage* storage = GetCompiledMethodStorage();
- CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
- storage,
+ CompiledCodeStorage* storage = GetCompiledCodeStorage();
+ CompiledMethod* compiled_method = storage->CreateCompiledMethod(
codegen->GetInstructionSet(),
code_allocator->GetMemory(),
ArrayRef<const uint8_t>(stack_map),
ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()),
- ArrayRef<const linker::LinkerPatch>(linker_patches));
+ ArrayRef<const linker::LinkerPatch>(linker_patches),
+ is_intrinsic);
for (const linker::LinkerPatch& patch : linker_patches) {
if (codegen->NeedsThunkCode(patch) && storage->GetThunkCode(patch).empty()) {
@@ -891,6 +900,8 @@
RunBaselineOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
} else {
RunOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
+ PassScope scope(WriteBarrierElimination::kWBEPassName, &pass_observer);
+ WriteBarrierElimination(graph, compilation_stats_.get()).Run();
}
RegisterAllocator::Strategy regalloc_strategy =
@@ -984,6 +995,10 @@
optimizations);
RunArchOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
+ {
+ PassScope scope(WriteBarrierElimination::kWBEPassName, &pass_observer);
+ WriteBarrierElimination(graph, compilation_stats_.get()).Run();
+ }
AllocateRegisters(graph,
codegen.get(),
@@ -1079,10 +1094,8 @@
compiled_method = Emit(&allocator,
&code_allocator,
codegen.get(),
+ compiled_intrinsic,
compiled_intrinsic ? nullptr : code_item);
- if (compiled_intrinsic) {
- compiled_method->MarkAsIntrinsic();
- }
if (kArenaAllocatorCountAllocations) {
codegen.reset(); // Release codegen's ScopedArenaAllocator for memory accounting.
@@ -1115,17 +1128,18 @@
static ScopedArenaVector<uint8_t> CreateJniStackMap(ScopedArenaAllocator* allocator,
const JniCompiledMethod& jni_compiled_method,
- size_t code_size) {
+ size_t code_size,
+ bool debuggable) {
// StackMapStream is quite large, so allocate it using the ScopedArenaAllocator
// to stay clear of the frame size limit.
std::unique_ptr<StackMapStream> stack_map_stream(
new (allocator) StackMapStream(allocator, jni_compiled_method.GetInstructionSet()));
- stack_map_stream->BeginMethod(
- jni_compiled_method.GetFrameSize(),
- jni_compiled_method.GetCoreSpillMask(),
- jni_compiled_method.GetFpSpillMask(),
- /* num_dex_registers= */ 0,
- /* baseline= */ false);
+ stack_map_stream->BeginMethod(jni_compiled_method.GetFrameSize(),
+ jni_compiled_method.GetCoreSpillMask(),
+ jni_compiled_method.GetFpSpillMask(),
+ /* num_dex_registers= */ 0,
+ /* baseline= */ false,
+ debuggable);
stack_map_stream->EndMethod(code_size);
return stack_map_stream->Encode();
}
@@ -1172,12 +1186,11 @@
method,
&handles));
if (codegen != nullptr) {
- CompiledMethod* compiled_method = Emit(&allocator,
- &code_allocator,
- codegen.get(),
- /* item= */ nullptr);
- compiled_method->MarkAsIntrinsic();
- return compiled_method;
+ return Emit(&allocator,
+ &code_allocator,
+ codegen.get(),
+ /*is_intrinsic=*/ true,
+ /*item=*/ nullptr);
}
}
}
@@ -1187,19 +1200,22 @@
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub);
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
- &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
- return CompiledMethod::SwapAllocCompiledMethod(
- GetCompiledMethodStorage(),
+ ScopedArenaVector<uint8_t> stack_map =
+ CreateJniStackMap(&stack_map_allocator,
+ jni_compiled_method,
+ jni_compiled_method.GetCode().size(),
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler());
+ return GetCompiledCodeStorage()->CreateCompiledMethod(
jni_compiled_method.GetInstructionSet(),
jni_compiled_method.GetCode(),
ArrayRef<const uint8_t>(stack_map),
jni_compiled_method.GetCfi(),
- /* patches= */ ArrayRef<const linker::LinkerPatch>());
+ /*patches=*/ ArrayRef<const linker::LinkerPatch>(),
+ /*is_intrinsic=*/ false);
}
Compiler* CreateOptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage) {
+ CompiledCodeStorage* storage) {
return new OptimizingCompiler(compiler_options, storage);
}
@@ -1233,6 +1249,14 @@
ArenaAllocator allocator(runtime->GetJitArenaPool());
if (UNLIKELY(method->IsNative())) {
+ // Use GenericJniTrampoline for critical native methods in debuggable runtimes. We don't
+ // support calling method entry / exit hooks for critical native methods yet.
+ // TODO(mythria): Add support for calling method entry / exit hooks in JITed stubs for critical
+ // native methods too.
+ if (runtime->IsJavaDebuggable() && method->IsCriticalNative()) {
+ DCHECK(compiler_options.IsJitCompiler());
+ return false;
+ }
JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod(
compiler_options, access_flags, method_idx, *dex_file, &allocator);
std::vector<Handle<mirror::Object>> roots;
@@ -1241,8 +1265,11 @@
ArenaStack arena_stack(runtime->GetJitArenaPool());
// StackMapStream is large and it does not fit into this frame, so we need helper method.
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
- &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
+ ScopedArenaVector<uint8_t> stack_map =
+ CreateJniStackMap(&stack_map_allocator,
+ jni_compiled_method,
+ jni_compiled_method.GetCode().size(),
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler());
ArrayRef<const uint8_t> reserved_code;
ArrayRef<const uint8_t> reserved_data;
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index cd6d684..737ffd0 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -18,18 +18,19 @@
#define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
#include "base/globals.h"
+#include "base/macros.h"
#include "base/mutex.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
+class CompiledCodeStorage;
class Compiler;
-class CompiledMethodStorage;
class CompilerOptions;
class DexFile;
Compiler* CreateOptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage);
+ CompiledCodeStorage* storage);
bool EncodeArtMethodInInlineInfo(ArtMethod* method);
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index d458e42..a1d0a5a 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -26,8 +26,9 @@
#include "base/atomic.h"
#include "base/globals.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
enum class MethodCompilationStat {
kAttemptBytecodeCompilation = 0,
@@ -46,6 +47,7 @@
kUnresolvedFieldNotAFastAccess,
kRemovedCheckedCast,
kRemovedDeadInstruction,
+ kRemovedTry,
kRemovedNullCheck,
kNotCompiledSkipped,
kNotCompiledInvalidBytecode,
@@ -59,6 +61,7 @@
kNotCompiledSpaceFilter,
kNotCompiledUnhandledInstruction,
kNotCompiledUnsupportedIsa,
+ kNotCompiledInliningIrreducibleLoop,
kNotCompiledIrreducibleLoopAndStringInit,
kNotCompiledPhiEquivalentInOsr,
kInlinedMonomorphicCall,
@@ -73,11 +76,13 @@
kLoopVectorizedIdiom,
kSelectGenerated,
kRemovedInstanceOf,
+ kPropagatedIfValue,
kInlinedInvokeVirtualOrInterface,
kInlinedLastInvokeVirtualOrInterface,
kImplicitNullCheckGenerated,
kExplicitNullCheckGenerated,
kSimplifyIf,
+ kSimplifyIfAddedPhi,
kSimplifyThrowingInvoke,
kInstructionSunk,
kNotInlinedUnresolvedEntrypoint,
@@ -88,16 +93,19 @@
kNotInlinedEnvironmentBudget,
kNotInlinedInstructionBudget,
kNotInlinedLoopWithoutExit,
- kNotInlinedIrreducibleLoop,
+ kNotInlinedIrreducibleLoopCallee,
+ kNotInlinedIrreducibleLoopCaller,
kNotInlinedAlwaysThrows,
kNotInlinedInfiniteLoop,
- kNotInlinedTryCatchCaller,
kNotInlinedTryCatchCallee,
+ kNotInlinedTryCatchDisabled,
kNotInlinedRegisterAllocator,
kNotInlinedCannotBuild,
+ kNotInlinedNeverInlineAnnotation,
kNotInlinedNotCompilable,
kNotInlinedNotVerified,
kNotInlinedCodeItem,
+ kNotInlinedEndsWithThrow,
kNotInlinedWont,
kNotInlinedRecursiveBudget,
kNotInlinedPolymorphicRecursiveBudget,
@@ -105,12 +113,15 @@
kNotInlinedUnresolved,
kNotInlinedPolymorphic,
kNotInlinedCustom,
+ kNotVarAnalyzedPathological,
kTryInline,
kConstructorFenceGeneratedNew,
kConstructorFenceGeneratedFinal,
kConstructorFenceRemovedLSE,
kConstructorFenceRemovedPFRA,
kConstructorFenceRemovedCFRE,
+ kPossibleWriteBarrier,
+ kRemovedWriteBarrier,
kBitstringTypeCheck,
kJitOutOfMemoryForCommit,
kFullLSEAllocationRemoved,
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index e836880..e62ccf0 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -25,6 +25,7 @@
#include <vector>
#include <variant>
+#include "base/macros.h"
#include "base/indenter.h"
#include "base/malloc_arena_pool.h"
#include "base/scoped_arena_allocator.h"
@@ -46,7 +47,7 @@
#include "ssa_builder.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
#define NUM_INSTRUCTIONS(...) \
(sizeof((uint16_t[]) {__VA_ARGS__}) /sizeof(uint16_t))
@@ -278,7 +279,7 @@
/* class_linker= */ nullptr,
graph->GetDexFile(),
code_item,
- /* class_def_index= */ DexFile::kDexNoIndex16,
+ /* class_def_idx= */ DexFile::kDexNoIndex16,
/* method_idx= */ dex::kDexNoIndex,
/* access_flags= */ 0u,
/* verified_method= */ nullptr,
@@ -559,7 +560,7 @@
class OptimizingUnitTest : public CommonArtTest, public OptimizingUnitTestHelper {};
// Naive string diff data type.
-typedef std::list<std::pair<std::string, std::string>> diff_t;
+using diff_t = std::list<std::pair<std::string, std::string>>;
// An alias for the empty string used to make it clear that a line is
// removed in a diff.
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 2036b4a..9fc4cc8 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -19,7 +19,7 @@
#include "base/stl_util.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void ParallelMoveResolver::BuildInitialMoveList(HParallelMove* parallel_move) {
// Perform a linear sweep of the moves to add them to the initial list of
diff --git a/compiler/optimizing/parallel_move_resolver.h b/compiler/optimizing/parallel_move_resolver.h
index 5fadcab..17d5122 100644
--- a/compiler/optimizing/parallel_move_resolver.h
+++ b/compiler/optimizing/parallel_move_resolver.h
@@ -18,11 +18,12 @@
#define ART_COMPILER_OPTIMIZING_PARALLEL_MOVE_RESOLVER_H_
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "base/value_object.h"
#include "data_type.h"
#include "locations.h"
-namespace art {
+namespace art HIDDEN {
class HParallelMove;
class MoveOperands;
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index a8ab6cd..a1c05e9 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest-typed-test.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
constexpr int kScratchRegisterStartIndexForTest = 100;
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index 17f37f0..b7e773f 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -18,7 +18,7 @@
#include "code_generator_x86.h"
#include "intrinsics_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
/**
diff --git a/compiler/optimizing/pc_relative_fixups_x86.h b/compiler/optimizing/pc_relative_fixups_x86.h
index 3b470a6..45578d8 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.h
+++ b/compiler/optimizing/pc_relative_fixups_x86.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_PC_RELATIVE_FIXUPS_X86_H_
#define ART_COMPILER_OPTIMIZING_PC_RELATIVE_FIXUPS_X86_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index c2f3d0e..707783e 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -22,7 +22,7 @@
#include "optimizing_compiler_stats.h"
#include "well_known_classes.h"
-namespace art {
+namespace art HIDDEN {
void PrepareForRegisterAllocation::Run() {
// Order does not matter.
@@ -83,7 +83,7 @@
if (check->IsStringCharAt()) {
// Add a fake environment for String.charAt() inline info as we want the exception
// to appear as being thrown from there. Skip if we're compiling String.charAt() itself.
- ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+ ArtMethod* char_at_method = WellKnownClasses::java_lang_String_charAt;
if (GetGraph()->GetArtMethod() != char_at_method) {
ArenaAllocator* allocator = GetGraph()->GetAllocator();
HEnvironment* environment = new (allocator) HEnvironment(allocator,
@@ -109,7 +109,7 @@
if (value->IsNullConstant()) {
DCHECK_EQ(value->GetType(), DataType::Type::kReference);
if (instruction->NeedsTypeCheck()) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
}
}
}
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index e0bb76e..0426f84 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_PREPARE_FOR_REGISTER_ALLOCATION_H_
#define ART_COMPILER_OPTIMIZING_PREPARE_FOR_REGISTER_ALLOCATION_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CompilerOptions;
class OptimizingCompilerStats;
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 8ef9ce4..77ddb97 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -19,9 +19,10 @@
#include "android-base/stringprintf.h"
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class HPrettyPrinter : public HGraphVisitor {
public:
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 6ef386b..5ed3944 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -17,6 +17,7 @@
#include "pretty_printer.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction.h"
@@ -25,7 +26,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class PrettyPrinterTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index e6024b0..2b0a118 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -29,7 +29,7 @@
#include "mirror/dex_cache.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
static inline ObjPtr<mirror::DexCache> FindDexCacheWithHint(
Thread* self, const DexFile& dex_file, Handle<mirror::DexCache> hint_dex_cache)
@@ -43,16 +43,12 @@
class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
public:
- RTPVisitor(HGraph* graph,
- Handle<mirror::ClassLoader> class_loader,
- Handle<mirror::DexCache> hint_dex_cache,
- bool is_first_run)
- : HGraphDelegateVisitor(graph),
- class_loader_(class_loader),
- hint_dex_cache_(hint_dex_cache),
- allocator_(graph->GetArenaStack()),
- worklist_(allocator_.Adapter(kArenaAllocReferenceTypePropagation)),
- is_first_run_(is_first_run) {
+ RTPVisitor(HGraph* graph, Handle<mirror::DexCache> hint_dex_cache, bool is_first_run)
+ : HGraphDelegateVisitor(graph),
+ hint_dex_cache_(hint_dex_cache),
+ allocator_(graph->GetArenaStack()),
+ worklist_(allocator_.Adapter(kArenaAllocReferenceTypePropagation)),
+ is_first_run_(is_first_run) {
worklist_.reserve(kDefaultWorklistSize);
}
@@ -110,7 +106,6 @@
static constexpr size_t kDefaultWorklistSize = 8;
- Handle<mirror::ClassLoader> class_loader_;
Handle<mirror::DexCache> hint_dex_cache_;
// Use local allocator for allocating memory.
@@ -122,15 +117,10 @@
};
ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
- Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> hint_dex_cache,
bool is_first_run,
const char* name)
- : HOptimization(graph, name),
- class_loader_(class_loader),
- hint_dex_cache_(hint_dex_cache),
- is_first_run_(is_first_run) {
-}
+ : HOptimization(graph, name), hint_dex_cache_(hint_dex_cache), is_first_run_(is_first_run) {}
void ReferenceTypePropagation::ValidateTypes() {
// TODO: move this to the graph checker. Note: There may be no Thread for gtests.
@@ -167,18 +157,12 @@
}
void ReferenceTypePropagation::Visit(HInstruction* instruction) {
- RTPVisitor visitor(graph_,
- class_loader_,
- hint_dex_cache_,
- is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, is_first_run_);
instruction->Accept(&visitor);
}
void ReferenceTypePropagation::Visit(ArrayRef<HInstruction* const> instructions) {
- RTPVisitor visitor(graph_,
- class_loader_,
- hint_dex_cache_,
- is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, is_first_run_);
for (HInstruction* instruction : instructions) {
if (instruction->IsPhi()) {
// Need to force phis to recalculate null-ness.
@@ -349,7 +333,7 @@
}
bool ReferenceTypePropagation::Run() {
- RTPVisitor visitor(graph_, class_loader_, hint_dex_cache_, is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, is_first_run_);
// To properly propagate type info we need to visit in the dominator-based order.
// Reverse post order guarantees a node's dominators are visited first.
@@ -446,10 +430,13 @@
if (rhs->AsIntConstant()->IsTrue()) {
// Case (1a)
*trueBranch = ifInstruction->IfTrueSuccessor();
- } else {
+ } else if (rhs->AsIntConstant()->IsFalse()) {
// Case (2a)
- DCHECK(rhs->AsIntConstant()->IsFalse()) << rhs->AsIntConstant()->GetValue();
*trueBranch = ifInstruction->IfFalseSuccessor();
+ } else {
+ // Sometimes we see a comparison of instance-of with a constant which is neither 0 nor 1.
+ // In those cases, we cannot do the match if+instance-of.
+ return false;
}
*instanceOf = lhs->AsInstanceOf();
return true;
@@ -463,10 +450,13 @@
if (rhs->AsIntConstant()->IsFalse()) {
// Case (1b)
*trueBranch = ifInstruction->IfTrueSuccessor();
- } else {
+ } else if (rhs->AsIntConstant()->IsTrue()) {
// Case (2b)
- DCHECK(rhs->AsIntConstant()->IsTrue()) << rhs->AsIntConstant()->GetValue();
*trueBranch = ifInstruction->IfFalseSuccessor();
+ } else {
+ // Sometimes we see a comparison of instance-of with a constant which is neither 0 nor 1.
+ // In those cases, we cannot do the match if+instance-of.
+ return false;
}
*instanceOf = lhs->AsInstanceOf();
return true;
@@ -583,7 +573,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_);
ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType(
- type_idx, dex_cache, class_loader_.Get());
+ type_idx, dex_cache, dex_cache->GetClassLoader());
SetClassAsTypeInfo(instr, klass, is_exact);
}
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 889a846..d696e28 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -18,12 +18,13 @@
#define ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "mirror/class-inl.h"
#include "nodes.h"
#include "obj_ptr.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Propagates reference types to instructions.
@@ -31,7 +32,6 @@
class ReferenceTypePropagation : public HOptimization {
public:
ReferenceTypePropagation(HGraph* graph,
- Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> hint_dex_cache,
bool is_first_run,
const char* name = kReferenceTypePropagationPassName);
@@ -73,8 +73,6 @@
void ValidateTypes();
- Handle<mirror::ClassLoader> class_loader_;
-
// Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with
// graph_->GetDexFile(). Since we may look up also in other dex files, it's used only
// as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache().
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index d1bcab0..2b012fc 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -19,6 +19,7 @@
#include <random>
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "base/transform_array_ref.h"
#include "base/transform_iterator.h"
#include "builder.h"
@@ -26,7 +27,7 @@
#include "object_lock.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
// TODO It would be good to use the following but there is a miniscule amount of
// chance for flakiness so we'll just use a set seed instead.
@@ -47,11 +48,8 @@
void SetupPropagation(VariableSizedHandleScope* handles) {
graph_ = CreateGraph(handles);
- propagation_ = new (GetAllocator()) ReferenceTypePropagation(graph_,
- Handle<mirror::ClassLoader>(),
- Handle<mirror::DexCache>(),
- true,
- "test_prop");
+ propagation_ = new (GetAllocator())
+ ReferenceTypePropagation(graph_, Handle<mirror::DexCache>(), true, "test_prop");
}
// Relay method to merge type in reference type propagation.
diff --git a/compiler/optimizing/register_allocation_resolver.cc b/compiler/optimizing/register_allocation_resolver.cc
index 875c633..53e11f2 100644
--- a/compiler/optimizing/register_allocation_resolver.cc
+++ b/compiler/optimizing/register_allocation_resolver.cc
@@ -21,7 +21,7 @@
#include "linear_order.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
RegisterAllocationResolver::RegisterAllocationResolver(CodeGenerator* codegen,
const SsaLivenessAnalysis& liveness)
diff --git a/compiler/optimizing/register_allocation_resolver.h b/compiler/optimizing/register_allocation_resolver.h
index 2783717..f4782eb 100644
--- a/compiler/optimizing/register_allocation_resolver.h
+++ b/compiler/optimizing/register_allocation_resolver.h
@@ -18,10 +18,11 @@
#define ART_COMPILER_OPTIMIZING_REGISTER_ALLOCATION_RESOLVER_H_
#include "base/array_ref.h"
+#include "base/macros.h"
#include "base/value_object.h"
#include "data_type.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class CodeGenerator;
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index a9c217f..e4c2d74 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -27,7 +27,7 @@
#include "register_allocator_linear_scan.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
RegisterAllocator::RegisterAllocator(ScopedArenaAllocator* allocator,
CodeGenerator* codegen,
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index 4d22687..453e339 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -22,7 +22,7 @@
#include "base/arena_object.h"
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class HBasicBlock;
diff --git a/compiler/optimizing/register_allocator_graph_color.cc b/compiler/optimizing/register_allocator_graph_color.cc
index 684aaf5..a7c891d 100644
--- a/compiler/optimizing/register_allocator_graph_color.cc
+++ b/compiler/optimizing/register_allocator_graph_color.cc
@@ -22,7 +22,7 @@
#include "ssa_liveness_analysis.h"
#include "thread-current-inl.h"
-namespace art {
+namespace art HIDDEN {
// Highest number of registers that we support for any platform. This can be used for std::bitset,
// for example, which needs to know its size at compile time.
diff --git a/compiler/optimizing/register_allocator_graph_color.h b/compiler/optimizing/register_allocator_graph_color.h
index e5b86ea..0e10152 100644
--- a/compiler/optimizing/register_allocator_graph_color.h
+++ b/compiler/optimizing/register_allocator_graph_color.h
@@ -24,7 +24,7 @@
#include "base/scoped_arena_containers.h"
#include "register_allocator.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class HBasicBlock;
diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc
index 833c24d..fcdaa2d 100644
--- a/compiler/optimizing/register_allocator_linear_scan.cc
+++ b/compiler/optimizing/register_allocator_linear_scan.cc
@@ -26,7 +26,7 @@
#include "register_allocation_resolver.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kMaxLifetimePosition = -1;
static constexpr size_t kDefaultNumberOfSpillSlots = 4;
diff --git a/compiler/optimizing/register_allocator_linear_scan.h b/compiler/optimizing/register_allocator_linear_scan.h
index 9a1e0d7..c71a9e9 100644
--- a/compiler/optimizing/register_allocator_linear_scan.h
+++ b/compiler/optimizing/register_allocator_linear_scan.h
@@ -22,7 +22,7 @@
#include "base/scoped_arena_containers.h"
#include "register_allocator.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class HBasicBlock;
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 6823155..dabd4a3 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -18,6 +18,7 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "code_generator_x86.h"
@@ -31,7 +32,7 @@
#include "ssa_liveness_analysis.h"
#include "ssa_phi_elimination.h"
-namespace art {
+namespace art HIDDEN {
using Strategy = RegisterAllocator::Strategy;
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index 8f18ccf..116f526 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -32,7 +32,7 @@
#include "scheduler_arm.h"
#endif
-namespace art {
+namespace art HIDDEN {
void SchedulingGraph::AddDependency(SchedulingNode* node,
SchedulingNode* dependency,
@@ -718,9 +718,10 @@
// HLoadException
// HMemoryBarrier
// HMonitorOperation
- // HNativeDebugInfo
+ // HNop
// HThrow
// HTryBoundary
+ // All volatile field access e.g. HInstanceFieldGet
// TODO: Some of the instructions above may be safe to schedule (maybe as
// scheduling barriers).
return instruction->IsArrayGet() ||
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index f7180a0..299fbc9 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -19,6 +19,7 @@
#include <fstream>
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
@@ -28,7 +29,7 @@
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
// General description of instruction scheduling.
//
diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc
index 25dd104..3f931c4 100644
--- a/compiler/optimizing/scheduler_arm.cc
+++ b/compiler/optimizing/scheduler_arm.cc
@@ -23,7 +23,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
using helpers::Int32ConstantFrom;
diff --git a/compiler/optimizing/scheduler_arm.h b/compiler/optimizing/scheduler_arm.h
index d11222d..73103df 100644
--- a/compiler/optimizing/scheduler_arm.h
+++ b/compiler/optimizing/scheduler_arm.h
@@ -17,14 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_SCHEDULER_ARM_H_
#define ART_COMPILER_OPTIMIZING_SCHEDULER_ARM_H_
+#include "base/macros.h"
#include "code_generator_arm_vixl.h"
#include "scheduler.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
-// TODO: Replace CodeGeneratorARMType with CodeGeneratorARMVIXL everywhere?
-typedef CodeGeneratorARMVIXL CodeGeneratorARMType;
-
// AArch32 instruction latencies.
// We currently assume that all ARM CPUs share the same instruction latency list.
// The following latencies were tuned based on performance experiments and
@@ -52,7 +50,7 @@
class SchedulingLatencyVisitorARM : public SchedulingLatencyVisitor {
public:
explicit SchedulingLatencyVisitorARM(CodeGenerator* codegen)
- : codegen_(down_cast<CodeGeneratorARMType*>(codegen)) {}
+ : codegen_(down_cast<CodeGeneratorARMVIXL*>(codegen)) {}
// Default visitor for instructions not handled specifically below.
void VisitInstruction(HInstruction* ATTRIBUTE_UNUSED) override {
@@ -133,7 +131,7 @@
// The latency setting for each HInstruction depends on how CodeGenerator may generate code,
// latency visitors may query CodeGenerator for such information for accurate latency settings.
- CodeGeneratorARMType* codegen_;
+ CodeGeneratorARMVIXL* codegen_;
};
class HSchedulerARM : public HScheduler {
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index 4f504c2..3071afd 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -20,7 +20,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
void SchedulingLatencyVisitorARM64::VisitBinaryOperation(HBinaryOperation* instr) {
diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h
index ba5a743..420f558 100644
--- a/compiler/optimizing/scheduler_arm64.h
+++ b/compiler/optimizing/scheduler_arm64.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_SCHEDULER_ARM64_H_
#define ART_COMPILER_OPTIMIZING_SCHEDULER_ARM64_H_
+#include "base/macros.h"
#include "scheduler.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
static constexpr uint32_t kArm64MemoryLoadLatency = 5;
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index a1cc202..6eea87f 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -17,6 +17,7 @@
#include "scheduler.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "codegen_test_utils.h"
#include "common_compiler_test.h"
@@ -34,7 +35,7 @@
#include "scheduler_arm.h"
#endif
-namespace art {
+namespace art HIDDEN {
// Return all combinations of ISA and code generator that are executable on
// hardware, or on simulator, and that we'd like to test.
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index 5405382..761e7b5 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -16,10 +16,10 @@
#include "select_generator.h"
-#include "base/scoped_arena_containers.h"
+#include "optimizing/nodes.h"
#include "reference_type_propagation.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kMaxInstructionsInBranch = 1u;
@@ -69,156 +69,277 @@
return block1->GetSingleSuccessor() == block2->GetSingleSuccessor();
}
-// Returns nullptr if `block` has either no phis or there is more than one phi
-// with different inputs at `index1` and `index2`. Otherwise returns that phi.
-static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index2) {
+// Returns nullptr if `block` has either no phis or there is more than one phi. Otherwise returns
+// that phi.
+static HPhi* GetSinglePhi(HBasicBlock* block, size_t index1, size_t index2) {
DCHECK_NE(index1, index2);
HPhi* select_phi = nullptr;
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
- if (phi->InputAt(index1) != phi->InputAt(index2)) {
- if (select_phi == nullptr) {
- // First phi with different inputs for the two indices found.
- select_phi = phi;
- } else {
- // More than one phis has different inputs for the two indices.
- return nullptr;
- }
+ if (select_phi == nullptr) {
+ // First phi found.
+ select_phi = phi;
+ } else {
+ // More than one phi found, return null.
+ return nullptr;
}
}
return select_phi;
}
+bool HSelectGenerator::TryGenerateSelectSimpleDiamondPattern(
+ HBasicBlock* block, ScopedArenaSafeMap<HInstruction*, HSelect*>* cache) {
+ DCHECK(block->GetLastInstruction()->IsIf());
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
+ HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
+ DCHECK_NE(true_block, false_block);
+
+ if (!IsSimpleBlock(true_block) ||
+ !IsSimpleBlock(false_block) ||
+ !BlocksMergeTogether(true_block, false_block)) {
+ return false;
+ }
+ HBasicBlock* merge_block = true_block->GetSingleSuccessor();
+
+ // If the branches are not empty, move instructions in front of the If.
+ // TODO(dbrazdil): This puts an instruction between If and its condition.
+ // Implement moving of conditions to first users if possible.
+ while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
+ HInstruction* instr = true_block->GetFirstInstruction();
+ DCHECK(!instr->CanThrow());
+ instr->MoveBefore(if_instruction);
+ }
+ while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
+ HInstruction* instr = false_block->GetFirstInstruction();
+ DCHECK(!instr->CanThrow());
+ instr->MoveBefore(if_instruction);
+ }
+ DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn());
+ DCHECK(false_block->IsSingleGoto() || false_block->IsSingleReturn());
+
+ // Find the resulting true/false values.
+ size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block);
+ size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block);
+ DCHECK_NE(predecessor_index_true, predecessor_index_false);
+
+ bool both_successors_return = true_block->IsSingleReturn() && false_block->IsSingleReturn();
+ // TODO(solanes): Extend to support multiple phis? e.g.
+ // int a, b;
+ // if (bool) {
+ // a = 0; b = 1;
+ // } else {
+ // a = 1; b = 2;
+ // }
+ // // use a and b
+ HPhi* phi = GetSinglePhi(merge_block, predecessor_index_true, predecessor_index_false);
+
+ HInstruction* true_value = nullptr;
+ HInstruction* false_value = nullptr;
+ if (both_successors_return) {
+ true_value = true_block->GetFirstInstruction()->InputAt(0);
+ false_value = false_block->GetFirstInstruction()->InputAt(0);
+ } else if (phi != nullptr) {
+ true_value = phi->InputAt(predecessor_index_true);
+ false_value = phi->InputAt(predecessor_index_false);
+ } else {
+ return false;
+ }
+ DCHECK(both_successors_return || phi != nullptr);
+
+ // Create the Select instruction and insert it in front of the If.
+ HInstruction* condition = if_instruction->InputAt(0);
+ HSelect* select = new (graph_->GetAllocator()) HSelect(condition,
+ true_value,
+ false_value,
+ if_instruction->GetDexPc());
+ if (both_successors_return) {
+ if (true_value->GetType() == DataType::Type::kReference) {
+ DCHECK(false_value->GetType() == DataType::Type::kReference);
+ ReferenceTypePropagation::FixUpInstructionType(select, graph_->GetHandleCache());
+ }
+ } else if (phi->GetType() == DataType::Type::kReference) {
+ select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo());
+ }
+ block->InsertInstructionBefore(select, if_instruction);
+
+ // Remove the true branch which removes the corresponding Phi
+ // input if needed. If left only with the false branch, the Phi is
+ // automatically removed.
+ if (both_successors_return) {
+ false_block->GetFirstInstruction()->ReplaceInput(select, 0);
+ } else {
+ phi->ReplaceInput(select, predecessor_index_false);
+ }
+
+ bool only_two_predecessors = (merge_block->GetPredecessors().size() == 2u);
+ true_block->DisconnectAndDelete();
+
+ // Merge remaining blocks which are now connected with Goto.
+ DCHECK_EQ(block->GetSingleSuccessor(), false_block);
+ block->MergeWith(false_block);
+ if (!both_successors_return && only_two_predecessors) {
+ DCHECK_EQ(only_two_predecessors, phi->GetBlock() == nullptr);
+ DCHECK_EQ(block->GetSingleSuccessor(), merge_block);
+ block->MergeWith(merge_block);
+ }
+
+ MaybeRecordStat(stats_, MethodCompilationStat::kSelectGenerated);
+
+ // Very simple way of finding common subexpressions in the generated HSelect statements
+ // (since this runs after GVN). Lookup by condition, and reuse latest one if possible
+ // (due to post order, latest select is most likely replacement). If needed, we could
+ // improve this by e.g. using the operands in the map as well.
+ auto it = cache->find(condition);
+ if (it == cache->end()) {
+ cache->Put(condition, select);
+ } else {
+ // Found cached value. See if latest can replace cached in the HIR.
+ HSelect* cached_select = it->second;
+ DCHECK_EQ(cached_select->GetCondition(), select->GetCondition());
+ if (cached_select->GetTrueValue() == select->GetTrueValue() &&
+ cached_select->GetFalseValue() == select->GetFalseValue() &&
+ select->StrictlyDominates(cached_select)) {
+ cached_select->ReplaceWith(select);
+ cached_select->GetBlock()->RemoveInstruction(cached_select);
+ }
+ it->second = select; // always cache latest
+ }
+
+ // No need to update dominance information, as we are simplifying
+ // a simple diamond shape, where the join block is merged with the
+ // entry block. Any following blocks would have had the join block
+ // as a dominator, and `MergeWith` handles changing that to the
+ // entry block
+ return true;
+}
+
+HBasicBlock* HSelectGenerator::TryFixupDoubleDiamondPattern(HBasicBlock* block) {
+ DCHECK(block->GetLastInstruction()->IsIf());
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
+ HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
+ DCHECK_NE(true_block, false_block);
+
+ // One branch must be a single goto, and the other one the inner if.
+ if (true_block->IsSingleGoto() == false_block->IsSingleGoto()) {
+ return nullptr;
+ }
+
+ HBasicBlock* single_goto = true_block->IsSingleGoto() ? true_block : false_block;
+ HBasicBlock* inner_if_block = true_block->IsSingleGoto() ? false_block : true_block;
+
+ // The innner if branch has to be a block with just a comparison and an if.
+ if (!inner_if_block->EndsWithIf() ||
+ inner_if_block->GetLastInstruction()->AsIf()->InputAt(0) !=
+ inner_if_block->GetFirstInstruction() ||
+ inner_if_block->GetLastInstruction()->GetPrevious() !=
+ inner_if_block->GetFirstInstruction() ||
+ !inner_if_block->GetFirstInstruction()->IsCondition()) {
+ return nullptr;
+ }
+
+ HIf* inner_if_instruction = inner_if_block->GetLastInstruction()->AsIf();
+ HBasicBlock* inner_if_true_block = inner_if_instruction->IfTrueSuccessor();
+ HBasicBlock* inner_if_false_block = inner_if_instruction->IfFalseSuccessor();
+ if (!inner_if_true_block->IsSingleGoto() || !inner_if_false_block->IsSingleGoto()) {
+ return nullptr;
+ }
+
+ // One must merge into the outer condition and the other must not.
+ if (BlocksMergeTogether(single_goto, inner_if_true_block) ==
+ BlocksMergeTogether(single_goto, inner_if_false_block)) {
+ return nullptr;
+ }
+
+ // First merge merges the outer if with one of the inner if branches. The block must be a Phi and
+ // a Goto.
+ HBasicBlock* first_merge = single_goto->GetSingleSuccessor();
+ if (first_merge->GetNumberOfPredecessors() != 2 ||
+ first_merge->GetPhis().CountSize() != 1 ||
+ !first_merge->GetLastInstruction()->IsGoto() ||
+ first_merge->GetFirstInstruction() != first_merge->GetLastInstruction()) {
+ return nullptr;
+ }
+
+ HPhi* first_phi = first_merge->GetFirstPhi()->AsPhi();
+
+ // Second merge is first_merge and the remainder branch merging. It must be phi + goto, or phi +
+ // return. Depending on the first merge, we define the second merge.
+ HBasicBlock* merges_into_second_merge =
+ BlocksMergeTogether(single_goto, inner_if_true_block)
+ ? inner_if_false_block
+ : inner_if_true_block;
+ if (!BlocksMergeTogether(first_merge, merges_into_second_merge)) {
+ return nullptr;
+ }
+
+ HBasicBlock* second_merge = merges_into_second_merge->GetSingleSuccessor();
+ if (second_merge->GetNumberOfPredecessors() != 2 ||
+ second_merge->GetPhis().CountSize() != 1 ||
+ !(second_merge->GetLastInstruction()->IsGoto() ||
+ second_merge->GetLastInstruction()->IsReturn()) ||
+ second_merge->GetFirstInstruction() != second_merge->GetLastInstruction()) {
+ return nullptr;
+ }
+
+ size_t index = second_merge->GetPredecessorIndexOf(merges_into_second_merge);
+ HPhi* second_phi = second_merge->GetFirstPhi()->AsPhi();
+
+ // Merge the phis.
+ first_phi->AddInput(second_phi->InputAt(index));
+ merges_into_second_merge->ReplaceSuccessor(second_merge, first_merge);
+ second_phi->ReplaceWith(first_phi);
+ second_merge->RemovePhi(second_phi);
+
+ // Sort out the new domination before merging the blocks
+ DCHECK_EQ(second_merge->GetSinglePredecessor(), first_merge);
+ second_merge->GetDominator()->RemoveDominatedBlock(second_merge);
+ second_merge->SetDominator(first_merge);
+ first_merge->AddDominatedBlock(second_merge);
+ first_merge->MergeWith(second_merge);
+
+ // No need to update dominance information. There's a chance that `merges_into_second_merge`
+ // doesn't come before `first_merge` but we don't need to fix it since `merges_into_second_merge`
+ // will disappear from the graph altogether when doing the follow-up
+ // TryGenerateSelectSimpleDiamondPattern.
+
+ return inner_if_block;
+}
+
bool HSelectGenerator::Run() {
- bool didSelect = false;
+ bool did_select = false;
// Select cache with local allocator.
ScopedArenaAllocator allocator(graph_->GetArenaStack());
- ScopedArenaSafeMap<HInstruction*, HSelect*> cache(
- std::less<HInstruction*>(), allocator.Adapter(kArenaAllocSelectGenerator));
+ ScopedArenaSafeMap<HInstruction*, HSelect*> cache(std::less<HInstruction*>(),
+ allocator.Adapter(kArenaAllocSelectGenerator));
// Iterate in post order in the unlikely case that removing one occurrence of
// the selection pattern empties a branch block of another occurrence.
for (HBasicBlock* block : graph_->GetPostOrder()) {
- if (!block->EndsWithIf()) continue;
-
- // Find elements of the diamond pattern.
- HIf* if_instruction = block->GetLastInstruction()->AsIf();
- HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
- HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
- DCHECK_NE(true_block, false_block);
-
- if (!IsSimpleBlock(true_block) ||
- !IsSimpleBlock(false_block) ||
- !BlocksMergeTogether(true_block, false_block)) {
+ if (!block->EndsWithIf()) {
continue;
}
- HBasicBlock* merge_block = true_block->GetSingleSuccessor();
- // If the branches are not empty, move instructions in front of the If.
- // TODO(dbrazdil): This puts an instruction between If and its condition.
- // Implement moving of conditions to first users if possible.
- while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
- HInstruction* instr = true_block->GetFirstInstruction();
- DCHECK(!instr->CanThrow());
- instr->MoveBefore(if_instruction);
- }
- while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
- HInstruction* instr = false_block->GetFirstInstruction();
- DCHECK(!instr->CanThrow());
- instr->MoveBefore(if_instruction);
- }
- DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn());
- DCHECK(false_block->IsSingleGoto() || false_block->IsSingleReturn());
-
- // Find the resulting true/false values.
- size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block);
- size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block);
- DCHECK_NE(predecessor_index_true, predecessor_index_false);
-
- bool both_successors_return = true_block->IsSingleReturn() && false_block->IsSingleReturn();
- HPhi* phi = GetSingleChangedPhi(merge_block, predecessor_index_true, predecessor_index_false);
-
- HInstruction* true_value = nullptr;
- HInstruction* false_value = nullptr;
- if (both_successors_return) {
- true_value = true_block->GetFirstInstruction()->InputAt(0);
- false_value = false_block->GetFirstInstruction()->InputAt(0);
- } else if (phi != nullptr) {
- true_value = phi->InputAt(predecessor_index_true);
- false_value = phi->InputAt(predecessor_index_false);
+ if (TryGenerateSelectSimpleDiamondPattern(block, &cache)) {
+ did_select = true;
} else {
- continue;
- }
- DCHECK(both_successors_return || phi != nullptr);
-
- // Create the Select instruction and insert it in front of the If.
- HInstruction* condition = if_instruction->InputAt(0);
- HSelect* select = new (graph_->GetAllocator()) HSelect(condition,
- true_value,
- false_value,
- if_instruction->GetDexPc());
- if (both_successors_return) {
- if (true_value->GetType() == DataType::Type::kReference) {
- DCHECK(false_value->GetType() == DataType::Type::kReference);
- ReferenceTypePropagation::FixUpInstructionType(select, graph_->GetHandleCache());
+ // Try to fix up the odd version of the double diamond pattern. If we could do it, it means
+ // that we can generate two selects.
+ HBasicBlock* inner_if_block = TryFixupDoubleDiamondPattern(block);
+ if (inner_if_block != nullptr) {
+ // Generate the selects now since `inner_if_block` should be after `block` in PostOrder.
+ bool result = TryGenerateSelectSimpleDiamondPattern(inner_if_block, &cache);
+ DCHECK(result);
+ result = TryGenerateSelectSimpleDiamondPattern(block, &cache);
+ DCHECK(result);
+ did_select = true;
}
- } else if (phi->GetType() == DataType::Type::kReference) {
- select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo());
}
- block->InsertInstructionBefore(select, if_instruction);
-
- // Remove the true branch which removes the corresponding Phi
- // input if needed. If left only with the false branch, the Phi is
- // automatically removed.
- if (both_successors_return) {
- false_block->GetFirstInstruction()->ReplaceInput(select, 0);
- } else {
- phi->ReplaceInput(select, predecessor_index_false);
- }
-
- bool only_two_predecessors = (merge_block->GetPredecessors().size() == 2u);
- true_block->DisconnectAndDelete();
-
- // Merge remaining blocks which are now connected with Goto.
- DCHECK_EQ(block->GetSingleSuccessor(), false_block);
- block->MergeWith(false_block);
- if (!both_successors_return && only_two_predecessors) {
- DCHECK_EQ(only_two_predecessors, phi->GetBlock() == nullptr);
- DCHECK_EQ(block->GetSingleSuccessor(), merge_block);
- block->MergeWith(merge_block);
- }
-
- MaybeRecordStat(stats_, MethodCompilationStat::kSelectGenerated);
-
- // Very simple way of finding common subexpressions in the generated HSelect statements
- // (since this runs after GVN). Lookup by condition, and reuse latest one if possible
- // (due to post order, latest select is most likely replacement). If needed, we could
- // improve this by e.g. using the operands in the map as well.
- auto it = cache.find(condition);
- if (it == cache.end()) {
- cache.Put(condition, select);
- } else {
- // Found cached value. See if latest can replace cached in the HIR.
- HSelect* cached = it->second;
- DCHECK_EQ(cached->GetCondition(), select->GetCondition());
- if (cached->GetTrueValue() == select->GetTrueValue() &&
- cached->GetFalseValue() == select->GetFalseValue() &&
- select->StrictlyDominates(cached)) {
- cached->ReplaceWith(select);
- cached->GetBlock()->RemoveInstruction(cached);
- }
- it->second = select; // always cache latest
- }
-
- // No need to update dominance information, as we are simplifying
- // a simple diamond shape, where the join block is merged with the
- // entry block. Any following blocks would have had the join block
- // as a dominator, and `MergeWith` handles changing that to the
- // entry block.
- didSelect = true;
}
- return didSelect;
+
+ return did_select;
}
} // namespace art
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
index 30ac8a8..7aa0803 100644
--- a/compiler/optimizing/select_generator.h
+++ b/compiler/optimizing/select_generator.h
@@ -57,9 +57,12 @@
#ifndef ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
#define ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
+#include "base/macros.h"
+#include "base/scoped_arena_containers.h"
#include "optimization.h"
+#include "optimizing/nodes.h"
-namespace art {
+namespace art HIDDEN {
class HSelectGenerator : public HOptimization {
public:
@@ -72,6 +75,43 @@
static constexpr const char* kSelectGeneratorPassName = "select_generator";
private:
+ bool TryGenerateSelectSimpleDiamondPattern(HBasicBlock* block,
+ ScopedArenaSafeMap<HInstruction*, HSelect*>* cache);
+
+ // When generating code for nested ternary operators (e.g. `return (x > 100) ? 100 : ((x < -100) ?
+ // -100 : x);`), a dexer can generate a double diamond pattern but it is not a clear cut one due
+ // to the merging of the blocks. `TryFixupDoubleDiamondPattern` recognizes that pattern and fixes
+ // up the graph to have a clean double diamond that `TryGenerateSelectSimpleDiamondPattern` can
+ // use to generate selects.
+ //
+ // In ASCII, it turns:
+ //
+ // 1 (outer if)
+ // / \
+ // 2 3 (inner if)
+ // | / \
+ // | 4 5
+ // \/ |
+ // 6 |
+ // \ |
+ // 7
+ // |
+ // 8
+ // into:
+ // 1 (outer if)
+ // / \
+ // 2 3 (inner if)
+ // | / \
+ // | 4 5
+ // \/ /
+ // 6
+ // |
+ // 8
+ //
+ // In short, block 7 disappears and we merge 6 and 7. Now we have a diamond with {3,4,5,6}, and
+ // when that gets resolved we get another one with the outer if.
+ HBasicBlock* TryFixupDoubleDiamondPattern(HBasicBlock* block);
+
DISALLOW_COPY_AND_ASSIGN(HSelectGenerator);
};
diff --git a/compiler/optimizing/select_generator_test.cc b/compiler/optimizing/select_generator_test.cc
index b18d41a..fc9e150 100644
--- a/compiler/optimizing/select_generator_test.cc
+++ b/compiler/optimizing/select_generator_test.cc
@@ -17,12 +17,13 @@
#include "select_generator.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
class SelectGeneratorTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 17cf3d3..277edff 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -34,7 +34,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
static bool IsInBootImage(ArtMethod* method) {
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -63,9 +63,9 @@
bool for_interface_call,
CodeGenerator* codegen) {
if (kIsDebugBuild) {
- ScopedObjectAccess soa(Thread::Current()); // Required for GetDeclaringClass below.
+ ScopedObjectAccess soa(Thread::Current()); // Required for `IsStringConstructor()` below.
DCHECK(callee != nullptr);
- DCHECK(!(callee->IsConstructor() && callee->GetDeclaringClass()->IsStringClass()));
+ DCHECK(!callee->IsStringConstructor());
}
MethodLoadKind method_load_kind;
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 9753669..6dfe904 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_SHARPENING_H_
#define ART_COMPILER_OPTIMIZING_SHARPENING_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index ba97b43..56719b1 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -16,7 +16,7 @@
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
bool SideEffectsAnalysis::Run() {
// Inlining might have created more blocks, so we need to increase the size
diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h
index 56a01e6..47fcdc5 100644
--- a/compiler/optimizing/side_effects_analysis.h
+++ b/compiler/optimizing/side_effects_analysis.h
@@ -18,10 +18,11 @@
#define ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis : public HOptimization {
public:
diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc
index 268798c..f2b781d 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -16,10 +16,11 @@
#include <gtest/gtest.h>
+#include "base/macros.h"
#include "data_type.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Only runtime types other than void are allowed.
static const DataType::Type kTestTypes[] = {
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 67ee83c..a658252 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -27,7 +27,7 @@
#include "scoped_thread_state_change-inl.h"
#include "ssa_phi_elimination.h"
-namespace art {
+namespace art HIDDEN {
void SsaBuilder::FixNullConstantType() {
// The order doesn't matter here.
@@ -538,7 +538,6 @@
// Compute type of reference type instructions. The pass assumes that
// NullConstant has been fixed up.
ReferenceTypePropagation(graph_,
- class_loader_,
dex_cache_,
/* is_first_run= */ true).Run();
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index a7d4e0e..99a5469 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_
#define ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Transforms a graph into SSA form. The liveness guarantees of
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 18942a1..317e099 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -21,7 +21,7 @@
#include "linear_order.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void SsaLivenessAnalysis::Analyze() {
// Compute the linear order directly in the graph's data structure
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 7f31585..cc2b49c 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -21,11 +21,12 @@
#include "base/intrusive_forward_list.h"
#include "base/iteration_range.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class SsaLivenessAnalysis;
diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc
index a477893..2df0f34 100644
--- a/compiler/optimizing/ssa_liveness_analysis_test.cc
+++ b/compiler/optimizing/ssa_liveness_analysis_test.cc
@@ -20,12 +20,13 @@
#include "arch/instruction_set_features.h"
#include "base/arena_allocator.h"
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "code_generator.h"
#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
class SsaLivenessAnalysisTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 8fd6962..ce343df 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -21,7 +21,7 @@
#include "base/scoped_arena_containers.h"
#include "base/bit_vector-inl.h"
-namespace art {
+namespace art HIDDEN {
bool SsaDeadPhiElimination::Run() {
MarkDeadPhis();
diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h
index c5cc752..f606f92 100644
--- a/compiler/optimizing/ssa_phi_elimination.h
+++ b/compiler/optimizing/ssa_phi_elimination.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization phase that removes dead phis from the graph. Dead phis are unused
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index e679893..008e435 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -17,6 +17,7 @@
#include "android-base/stringprintf.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction.h"
@@ -27,7 +28,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class SsaTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index f55bbee..1a368ed 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -20,6 +20,7 @@
#include <vector>
#include "art_method-inl.h"
+#include "base/globals.h"
#include "base/stl_util.h"
#include "class_linker.h"
#include "dex/dex_file.h"
@@ -32,7 +33,7 @@
#include "scoped_thread_state_change-inl.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
constexpr static bool kVerifyStackMaps = kIsDebugBuild;
@@ -49,7 +50,8 @@
size_t core_spill_mask,
size_t fp_spill_mask,
uint32_t num_dex_registers,
- bool baseline) {
+ bool baseline,
+ bool debuggable) {
DCHECK(!in_method_) << "Mismatched Begin/End calls";
in_method_ = true;
DCHECK_EQ(packed_frame_size_, 0u) << "BeginMethod was already called";
@@ -60,6 +62,7 @@
fp_spill_mask_ = fp_spill_mask;
num_dex_registers_ = num_dex_registers;
baseline_ = baseline;
+ debuggable_ = debuggable;
if (kVerifyStackMaps) {
dchecks_.emplace_back([=](const CodeInfo& code_info) {
@@ -99,16 +102,21 @@
}
}
-void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
- uint32_t native_pc_offset,
- uint32_t register_mask,
- BitVector* stack_mask,
- StackMap::Kind kind,
- bool needs_vreg_info) {
+void StackMapStream::BeginStackMapEntry(
+ uint32_t dex_pc,
+ uint32_t native_pc_offset,
+ uint32_t register_mask,
+ BitVector* stack_mask,
+ StackMap::Kind kind,
+ bool needs_vreg_info,
+ const std::vector<uint32_t>& dex_pc_list_for_catch_verification) {
DCHECK(in_method_) << "Call BeginMethod first";
DCHECK(!in_stack_map_) << "Mismatched Begin/End calls";
in_stack_map_ = true;
+ DCHECK_IMPLIES(!dex_pc_list_for_catch_verification.empty(), kind == StackMap::Kind::Catch);
+ DCHECK_IMPLIES(!dex_pc_list_for_catch_verification.empty(), kIsDebugBuild);
+
current_stack_map_ = BitTableBuilder<StackMap>::Entry();
current_stack_map_[StackMap::kKind] = static_cast<uint32_t>(kind);
current_stack_map_[StackMap::kPackedNativePc] =
@@ -149,7 +157,8 @@
instruction_set_);
CHECK_EQ(stack_map.Row(), stack_map_index);
} else if (kind == StackMap::Kind::Catch) {
- StackMap stack_map = code_info.GetCatchStackMapForDexPc(dex_pc);
+ StackMap stack_map = code_info.GetCatchStackMapForDexPc(
+ ArrayRef<const uint32_t>(dex_pc_list_for_catch_verification));
CHECK_EQ(stack_map.Row(), stack_map_index);
}
StackMap stack_map = code_info.GetStackMapAt(stack_map_index);
@@ -367,6 +376,7 @@
uint32_t flags = (inline_infos_.size() > 0) ? CodeInfo::kHasInlineInfo : 0;
flags |= baseline_ ? CodeInfo::kIsBaseline : 0;
+ flags |= debuggable_ ? CodeInfo::kIsDebuggable : 0;
DCHECK_LE(flags, kVarintMax); // Ensure flags can be read directly as byte.
uint32_t bit_table_flags = 0;
ForEachBitTable([&bit_table_flags](size_t i, auto bit_table) {
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 27145a1..643af2d 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -21,6 +21,7 @@
#include "base/arena_bit_vector.h"
#include "base/bit_table.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "base/memory_region.h"
#include "base/scoped_arena_containers.h"
#include "base/value_object.h"
@@ -28,7 +29,7 @@
#include "nodes.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
@@ -64,15 +65,19 @@
size_t core_spill_mask,
size_t fp_spill_mask,
uint32_t num_dex_registers,
- bool baseline = false);
+ bool baseline,
+ bool debuggable);
void EndMethod(size_t code_size);
- void BeginStackMapEntry(uint32_t dex_pc,
- uint32_t native_pc_offset,
- uint32_t register_mask = 0,
- BitVector* sp_mask = nullptr,
- StackMap::Kind kind = StackMap::Kind::Default,
- bool needs_vreg_info = true);
+ void BeginStackMapEntry(
+ uint32_t dex_pc,
+ uint32_t native_pc_offset,
+ uint32_t register_mask = 0,
+ BitVector* sp_mask = nullptr,
+ StackMap::Kind kind = StackMap::Kind::Default,
+ bool needs_vreg_info = true,
+ const std::vector<uint32_t>& dex_pc_list_for_catch_verification = std::vector<uint32_t>());
+
void EndStackMapEntry();
void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
@@ -125,6 +130,7 @@
uint32_t fp_spill_mask_ = 0;
uint32_t num_dex_registers_ = 0;
bool baseline_;
+ bool debuggable_;
BitTableBuilder<StackMap> stack_maps_;
BitTableBuilder<RegisterMask> register_masks_;
BitmapTableBuilder stack_masks_;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index f6a739e..a2c30e7 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -18,12 +18,13 @@
#include "art_method.h"
#include "base/arena_bit_vector.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "stack_map_stream.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
// Check that the stack mask of given stack map is identical
// to the given bit vector. Returns true if they are same.
@@ -52,7 +53,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
size_t number_of_dex_registers = 2;
@@ -106,7 +112,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -300,7 +311,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -363,7 +379,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -411,7 +432,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -467,7 +493,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 1);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 1,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask);
@@ -512,7 +543,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -702,7 +738,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 0);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 0,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, true);
sp_mask.SetBit(1);
diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc
index a5f919c..ba56eb8 100644
--- a/compiler/optimizing/superblock_cloner.cc
+++ b/compiler/optimizing/superblock_cloner.cc
@@ -22,7 +22,7 @@
#include <sstream>
-namespace art {
+namespace art HIDDEN {
using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
using HInstructionMap = SuperblockCloner::HInstructionMap;
diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h
index 1f6ee74..421701f 100644
--- a/compiler/optimizing/superblock_cloner.h
+++ b/compiler/optimizing/superblock_cloner.h
@@ -20,9 +20,10 @@
#include "base/arena_bit_vector.h"
#include "base/arena_containers.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class InductionVarRange;
diff --git a/compiler/optimizing/superblock_cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc
index d8d68b7..ea2563e 100644
--- a/compiler/optimizing/superblock_cloner_test.cc
+++ b/compiler/optimizing/superblock_cloner_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "graph_checker.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -21,7 +22,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
using HInstructionMap = SuperblockCloner::HInstructionMap;
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
index 33823e2..93a84b4 100644
--- a/compiler/optimizing/suspend_check_test.cc
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_instruction.h"
#include "nodes.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
/**
* Check that the HGraphBuilder adds suspend checks to backward branches.
diff --git a/compiler/optimizing/write_barrier_elimination.cc b/compiler/optimizing/write_barrier_elimination.cc
new file mode 100644
index 0000000..9023cc2
--- /dev/null
+++ b/compiler/optimizing/write_barrier_elimination.cc
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 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 "write_barrier_elimination.h"
+
+#include "base/arena_allocator.h"
+#include "base/scoped_arena_allocator.h"
+#include "base/scoped_arena_containers.h"
+#include "optimizing/nodes.h"
+
+namespace art HIDDEN {
+
+class WBEVisitor : public HGraphVisitor {
+ public:
+ WBEVisitor(HGraph* graph, OptimizingCompilerStats* stats)
+ : HGraphVisitor(graph),
+ scoped_allocator_(graph->GetArenaStack()),
+ current_write_barriers_(scoped_allocator_.Adapter(kArenaAllocWBE)),
+ stats_(stats) {}
+
+ void VisitBasicBlock(HBasicBlock* block) override {
+ // We clear the map to perform this optimization only in the same block. Doing it across blocks
+ // would entail non-trivial merging of states.
+ current_write_barriers_.clear();
+ HGraphVisitor::VisitBasicBlock(block);
+ }
+
+ void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
+ DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
+
+ if (instruction->GetFieldType() != DataType::Type::kReference ||
+ instruction->GetValue()->IsNullConstant()) {
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ return;
+ }
+
+ MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
+ HInstruction* obj = HuntForOriginalReference(instruction->InputAt(0));
+ auto it = current_write_barriers_.find(obj);
+ if (it != current_write_barriers_.end()) {
+ DCHECK(it->second->IsInstanceFieldSet());
+ DCHECK(it->second->AsInstanceFieldSet()->GetWriteBarrierKind() !=
+ WriteBarrierKind::kDontEmit);
+ DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
+ it->second->AsInstanceFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitNoNullCheck);
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
+ } else {
+ const bool inserted = current_write_barriers_.insert({obj, instruction}).second;
+ DCHECK(inserted);
+ DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ }
+ }
+
+ void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
+ DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
+
+ if (instruction->GetFieldType() != DataType::Type::kReference ||
+ instruction->GetValue()->IsNullConstant()) {
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ return;
+ }
+
+ MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
+ HInstruction* cls = HuntForOriginalReference(instruction->InputAt(0));
+ auto it = current_write_barriers_.find(cls);
+ if (it != current_write_barriers_.end()) {
+ DCHECK(it->second->IsStaticFieldSet());
+ DCHECK(it->second->AsStaticFieldSet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
+ it->second->AsStaticFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitNoNullCheck);
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
+ } else {
+ const bool inserted = current_write_barriers_.insert({cls, instruction}).second;
+ DCHECK(inserted);
+ DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ }
+ }
+
+ void VisitArraySet(HArraySet* instruction) override {
+ if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
+ ClearCurrentValues();
+ }
+
+ if (instruction->GetComponentType() != DataType::Type::kReference ||
+ instruction->GetValue()->IsNullConstant()) {
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ return;
+ }
+
+ HInstruction* arr = HuntForOriginalReference(instruction->InputAt(0));
+ MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
+ auto it = current_write_barriers_.find(arr);
+ if (it != current_write_barriers_.end()) {
+ DCHECK(it->second->IsArraySet());
+ DCHECK(it->second->AsArraySet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
+ // We never skip the null check in ArraySets so that value is already set.
+ DCHECK(it->second->AsArraySet()->GetWriteBarrierKind() == WriteBarrierKind::kEmitNoNullCheck);
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
+ } else {
+ const bool inserted = current_write_barriers_.insert({arr, instruction}).second;
+ DCHECK(inserted);
+ DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ }
+ }
+
+ void VisitInstruction(HInstruction* instruction) override {
+ if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
+ ClearCurrentValues();
+ }
+ }
+
+ private:
+ void ClearCurrentValues() { current_write_barriers_.clear(); }
+
+ HInstruction* HuntForOriginalReference(HInstruction* ref) const {
+ // An original reference can be transformed by instructions like:
+ // i0 NewArray
+ // i1 HInstruction(i0) <-- NullCheck, BoundType, IntermediateAddress.
+ // i2 ArraySet(i1, index, value)
+ DCHECK(ref != nullptr);
+ while (ref->IsNullCheck() || ref->IsBoundType() || ref->IsIntermediateAddress()) {
+ ref = ref->InputAt(0);
+ }
+ return ref;
+ }
+
+ ScopedArenaAllocator scoped_allocator_;
+
+ // Stores a map of <Receiver, InstructionWhereTheWriteBarrierIs>.
+ // `InstructionWhereTheWriteBarrierIs` is used for DCHECKs only.
+ ScopedArenaHashMap<HInstruction*, HInstruction*> current_write_barriers_;
+
+ OptimizingCompilerStats* const stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(WBEVisitor);
+};
+
+bool WriteBarrierElimination::Run() {
+ WBEVisitor wbe_visitor(graph_, stats_);
+ wbe_visitor.VisitReversePostOrder();
+ return true;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/write_barrier_elimination.h b/compiler/optimizing/write_barrier_elimination.h
new file mode 100644
index 0000000..a3769e7
--- /dev/null
+++ b/compiler/optimizing/write_barrier_elimination.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_WRITE_BARRIER_ELIMINATION_H_
+#define ART_COMPILER_OPTIMIZING_WRITE_BARRIER_ELIMINATION_H_
+
+#include "base/macros.h"
+#include "optimization.h"
+
+namespace art HIDDEN {
+
+// Eliminates unnecessary write barriers from InstanceFieldSet, StaticFieldSet, and ArraySet.
+//
+// We can eliminate redundant write barriers as we don't need several for the same receiver. For
+// example:
+// MyObject o;
+// o.inner_obj = io;
+// o.inner_obj2 = io2;
+// o.inner_obj3 = io3;
+// We can keep the write barrier for `inner_obj` and remove the other two.
+//
+// In order to do this, we set the WriteBarrierKind of the instruction. The instruction's kind are
+// set to kEmitNoNullCheck (if this write barrier coalesced other write barriers, we don't want to
+// perform the null check optimization), or to kDontEmit (if the write barrier as a whole is not
+// needed).
+class WriteBarrierElimination : public HOptimization {
+ public:
+ WriteBarrierElimination(HGraph* graph,
+ OptimizingCompilerStats* stats,
+ const char* name = kWBEPassName)
+ : HOptimization(graph, name, stats) {}
+
+ bool Run() override;
+
+ static constexpr const char* kWBEPassName = "write_barrier_elimination";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WriteBarrierElimination);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_WRITE_BARRIER_ELIMINATION_H_
diff --git a/compiler/optimizing/x86_memory_gen.cc b/compiler/optimizing/x86_memory_gen.cc
index b1abcf6..8265214 100644
--- a/compiler/optimizing/x86_memory_gen.cc
+++ b/compiler/optimizing/x86_memory_gen.cc
@@ -18,7 +18,7 @@
#include "code_generator.h"
#include "driver/compiler_options.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
/**
diff --git a/compiler/optimizing/x86_memory_gen.h b/compiler/optimizing/x86_memory_gen.h
index 3f4178d..1cae1a5 100644
--- a/compiler/optimizing/x86_memory_gen.h
+++ b/compiler/optimizing/x86_memory_gen.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_X86_MEMORY_GEN_H_
#define ART_COMPILER_OPTIMIZING_X86_MEMORY_GEN_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
namespace x86 {
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index 0aaeaa5..5e7ac83 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -38,7 +38,7 @@
#define __ assembler.
-namespace art {
+namespace art HIDDEN {
#ifdef ART_ENABLE_CODEGEN_arm
namespace arm {
diff --git a/compiler/trampolines/trampoline_compiler.h b/compiler/trampolines/trampoline_compiler.h
index f0086b5..32e35ae 100644
--- a/compiler/trampolines/trampoline_compiler.h
+++ b/compiler/trampolines/trampoline_compiler.h
@@ -22,9 +22,10 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/macros.h"
#include "offsets.h"
-namespace art {
+namespace art HIDDEN {
enum EntryPointCallingConvention {
// ABI of invocations to a method's interpreter entry point.
@@ -36,12 +37,10 @@
};
// Create code that will invoke the function held in thread local storage.
-std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline32(InstructionSet isa,
- EntryPointCallingConvention abi,
- ThreadOffset32 entry_point_offset);
-std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline64(InstructionSet isa,
- EntryPointCallingConvention abi,
- ThreadOffset64 entry_point_offset);
+EXPORT std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline32(
+ InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset32 entry_point_offset);
+EXPORT std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline64(
+ InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset64 entry_point_offset);
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm_shared.h b/compiler/utils/arm/assembler_arm_shared.h
index 7464052..77118c8 100644
--- a/compiler/utils/arm/assembler_arm_shared.h
+++ b/compiler/utils/arm/assembler_arm_shared.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_SHARED_H_
#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_SHARED_H_
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
namespace arm {
enum LoadOperandType {
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index 2ed065f..c7ca003 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -26,7 +26,7 @@
using namespace vixl::aarch32; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm {
#ifdef ___
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index 5bc8a70..f2c0b76 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -37,7 +37,7 @@
namespace vixl32 = vixl::aarch32;
-namespace art {
+namespace art HIDDEN {
namespace arm {
inline dwarf::Reg DWARFReg(vixl32::Register reg) {
diff --git a/compiler/utils/arm/constants_arm.cc b/compiler/utils/arm/constants_arm.cc
index b02b343..a927fc2 100644
--- a/compiler/utils/arm/constants_arm.cc
+++ b/compiler/utils/arm/constants_arm.cc
@@ -16,7 +16,7 @@
#include "constants_arm.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index f42fd97..ef6d48d 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -26,8 +26,9 @@
#include "arch/arm/registers_arm.h"
#include "base/casts.h"
#include "base/globals.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Defines constants and accessor classes to assemble, disassemble and
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index c0e5638..699df80 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -29,7 +29,7 @@
using vixl::ExactAssemblyScope;
using vixl::CodeBufferCheckScope;
-namespace art {
+namespace art HIDDEN {
namespace arm {
#ifdef ___
@@ -430,8 +430,15 @@
asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value());
}
-void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
- asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
+void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) {
+ if (tag_sp) {
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ vixl32::Register reg = temps.Acquire();
+ ___ Orr(reg, sp, 0x2);
+ asm_.StoreToOffset(kStoreWord, reg, tr, thr_offs.Int32Value());
+ } else {
+ asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
+ }
}
void ArmVIXLJNIMacroAssembler::SignExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,
@@ -871,6 +878,11 @@
}
}
+void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst, size_t value) {
+ ArmManagedRegister dst = mdst.AsArm();
+ ___ Mov(AsVIXLRegister(dst), static_cast<uint32_t>(value));
+}
+
void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest, FrameOffset src, size_t size) {
DCHECK(size == 4 || size == 8) << size;
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
@@ -1215,6 +1227,14 @@
}
}
+void ArmVIXLJNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ vixl32::Register scratch = temps.Acquire();
+ ___ Mov(scratch, static_cast<uint32_t>(address));
+ ___ Ldrb(scratch, MemOperand(scratch, 0));
+ ___ CompareAndBranchIfNonZero(scratch, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+}
+
void ArmVIXLJNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
___ Bind(ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
index ed453ae..df14d04 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
@@ -29,7 +29,7 @@
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class ArmVIXLJNIMacroAssembler final
@@ -70,7 +70,7 @@
void StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset32 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
@@ -100,6 +100,8 @@
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
+
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset32 thr_offs,
@@ -213,6 +215,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/arm/managed_register_arm.cc b/compiler/utils/arm/managed_register_arm.cc
index deff658..07d50da 100644
--- a/compiler/utils/arm/managed_register_arm.cc
+++ b/compiler/utils/arm/managed_register_arm.cc
@@ -18,7 +18,7 @@
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Returns true if this managed-register overlaps the other managed-register.
diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h
index 6d942fa..b3d436c 100644
--- a/compiler/utils/arm/managed_register_arm.h
+++ b/compiler/utils/arm/managed_register_arm.h
@@ -19,10 +19,11 @@
#include <android-base/logging.h>
+#include "base/macros.h"
#include "constants_arm.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Values for register pairs.
diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc
index 6f440a7..60f6090 100644
--- a/compiler/utils/arm/managed_register_arm_test.cc
+++ b/compiler/utils/arm/managed_register_arm_test.cc
@@ -16,9 +16,10 @@
#include "managed_register_arm.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
TEST(ArmManagedRegister, NoRegister) {
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index b71b00b..cc50883 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -24,7 +24,7 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
#ifdef ___
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index b49a13a..62f807f 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -38,7 +38,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
class Arm64InstructionSetFeatures;
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index a09fe7e..27d275f 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -24,7 +24,7 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
#ifdef ___
@@ -218,10 +218,13 @@
___ Str(scratch, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
}
-void Arm64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 tr_offs) {
+void Arm64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 tr_offs, bool tag_sp) {
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
Register scratch = temps.AcquireX();
___ Mov(scratch, reg_x(SP));
+ if (tag_sp) {
+ ___ Orr(scratch, scratch, 0x2);
+ }
___ Str(scratch, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
}
@@ -640,6 +643,12 @@
}
}
+void Arm64JNIMacroAssembler::Move(ManagedRegister m_dst, size_t value) {
+ Arm64ManagedRegister dst = m_dst.AsArm64();
+ DCHECK(dst.IsXRegister());
+ ___ Mov(reg_x(dst.AsXRegister()), value);
+}
+
void Arm64JNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset64 tr_offs) {
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
Register scratch = temps.AcquireX();
@@ -1037,6 +1046,14 @@
}
}
+void Arm64JNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ Register scratch = temps.AcquireX();
+ ___ Mov(scratch, address);
+ ___ Ldrb(scratch.W(), MEM_OP(scratch, 0));
+ ___ Cbnz(scratch.W(), Arm64JNIMacroLabel::Cast(label)->AsArm64());
+}
+
void Arm64JNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
___ Bind(Arm64JNIMacroLabel::Cast(label)->AsArm64());
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h
index 2c04184..3f6a4fb 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.h
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h
@@ -37,7 +37,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
class Arm64JNIMacroAssembler final : public JNIMacroAssemblerFwd<Arm64Assembler, PointerSize::k64> {
@@ -72,7 +72,7 @@
void StoreRawPtr(FrameOffset dest, ManagedRegister src) override;
void StoreImmediateToFrame(FrameOffset dest, uint32_t imm) override;
void StoreStackOffsetToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset64 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
// Load routines.
@@ -92,6 +92,7 @@
ArrayRef<ArgumentLocation> srcs,
ArrayRef<FrameOffset> refs) override;
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset64 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs, ManagedRegister scratch)
override;
@@ -197,6 +198,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc
index 5632265..74a3545 100644
--- a/compiler/utils/arm64/managed_register_arm64.cc
+++ b/compiler/utils/arm64/managed_register_arm64.cc
@@ -17,7 +17,7 @@
#include "managed_register_arm64.h"
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
// TODO: Define convention
diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h
index 8a06f63..7e8c976 100644
--- a/compiler/utils/arm64/managed_register_arm64.h
+++ b/compiler/utils/arm64/managed_register_arm64.h
@@ -20,9 +20,10 @@
#include <android-base/logging.h>
#include "arch/arm64/registers_arm64.h"
+#include "base/macros.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
const int kNumberOfXRegIds = kNumberOfXRegisters;
diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc
index d151ac9..f250360 100644
--- a/compiler/utils/arm64/managed_register_arm64_test.cc
+++ b/compiler/utils/arm64/managed_register_arm64_test.cc
@@ -18,9 +18,10 @@
#include "assembler_arm64.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
TEST(Arm64ManagedRegister, NoRegister) {
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index d1d2a3d..b82f0dc 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -23,7 +23,7 @@
#include "base/globals.h"
#include "base/memory_region.h"
-namespace art {
+namespace art HIDDEN {
AssemblerBuffer::AssemblerBuffer(ArenaAllocator* allocator)
: allocator_(allocator) {
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 4b4fb14..13a5d9f 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -37,7 +37,7 @@
#include "x86/constants_x86.h"
#include "x86_64/constants_x86_64.h"
-namespace art {
+namespace art HIDDEN {
class Assembler;
class AssemblerBuffer;
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index bb22fe5..d03e5a7 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -26,11 +26,12 @@
#include <fstream>
#include <iterator>
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "assembler_test_base.h"
#include "common_runtime_test.h" // For ScratchFile
-namespace art {
+namespace art HIDDEN {
// Helper for a constexpr string length.
constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
@@ -59,7 +60,7 @@
return assembler_.get();
}
- typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler);
+ using TestFn = std::string (*)(AssemblerTest *, Ass *);
void DriverFn(TestFn f, const std::string& test_name) {
DriverWrapper(f(this, assembler_.get()), test_name);
@@ -259,7 +260,7 @@
std::string (AssemblerTest::*GetName1)(const Reg1&),
std::string (AssemblerTest::*GetName2)(const Reg2&),
std::string (AssemblerTest::*GetName3)(const Reg3&),
- std::string fmt,
+ const std::string& fmt,
int bias) {
std::string str;
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index bf73808..a342d9a 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -26,6 +26,7 @@
#include "android-base/strings.h"
+#include "base/macros.h"
#include "base/os.h"
#include "base/utils.h"
#include "common_runtime_test.h" // For ScratchDir.
@@ -34,7 +35,7 @@
#include "exec_utils.h"
#include "stream/file_output_stream.h"
-namespace art {
+namespace art HIDDEN {
// If you want to take a look at the differences between the ART assembler and clang,
// set this flag to true. The disassembled files will then remain in the tmp directory.
@@ -59,7 +60,7 @@
// This is intended to be run as a test.
bool CheckTools() {
- for (auto cmd : { GetAssemblerCommand()[0], GetDisassemblerCommand()[0] }) {
+ for (const std::string& cmd : { GetAssemblerCommand()[0], GetDisassemblerCommand()[0] }) {
if (!OS::FileExists(cmd.c_str())) {
LOG(ERROR) << "Could not find " << cmd;
return false;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index b2d4dcd..7d08426 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -30,10 +30,11 @@
#include "utils/assembler_test_base.h"
#include "base/hex_dump.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "common_runtime_test.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Include results file (generated manually)
@@ -159,7 +160,8 @@
__ StoreRef(FrameOffset(48), scratch_register);
__ StoreSpanning(FrameOffset(48), method_register, FrameOffset(48));
__ StoreStackOffsetToThread(ThreadOffset32(512), FrameOffset(4096));
- __ StoreStackPointerToThread(ThreadOffset32(512));
+ __ StoreStackPointerToThread(ThreadOffset32(512), false);
+ __ StoreStackPointerToThread(ThreadOffset32(512), true);
// Other
__ Call(method_register, FrameOffset(48));
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index b6c6025..79cf029 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -1,258 +1,260 @@
const char* const VixlJniHelpersResults = {
- " 0: 2d e9 e0 4d push.w {r5, r6, r7, r8, r10, r11, lr}\n"
- " 4: 2d ed 10 8a vpush {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
- " 8: 81 b0 sub sp, #4\n"
- " a: 00 90 str r0, [sp]\n"
- " c: 19 91 str r1, [sp, #100]\n"
- " e: 8d ed 1a 0a vstr s0, [sp, #104]\n"
- " 12: 1b 92 str r2, [sp, #108]\n"
- " 14: 1c 93 str r3, [sp, #112]\n"
- " 16: 88 b0 sub sp, #32\n"
- " 18: ad f5 80 5d sub.w sp, sp, #4096\n"
- " 1c: 08 98 ldr r0, [sp, #32]\n"
- " 1e: 1f 98 ldr r0, [sp, #124]\n"
- " 20: 21 98 ldr r0, [sp, #132]\n"
- " 22: ff 98 ldr r0, [sp, #1020]\n"
- " 24: dd f8 00 04 ldr.w r0, [sp, #1024]\n"
- " 28: dd f8 fc cf ldr.w r12, [sp, #4092]\n"
- " 2c: 0d f5 80 5c add.w r12, sp, #4096\n"
- " 30: dc f8 00 c0 ldr.w r12, [r12]\n"
- " 34: d9 f8 00 c2 ldr.w r12, [r9, #512]\n"
- " 38: dc f8 80 00 ldr.w r0, [r12, #128]\n"
- " 3c: 08 90 str r0, [sp, #32]\n"
- " 3e: 1f 90 str r0, [sp, #124]\n"
- " 40: 21 90 str r0, [sp, #132]\n"
- " 42: ff 90 str r0, [sp, #1020]\n"
- " 44: cd f8 00 04 str.w r0, [sp, #1024]\n"
- " 48: cd f8 fc cf str.w r12, [sp, #4092]\n"
- " 4c: 4d f8 04 5d str r5, [sp, #-4]!\n"
- " 50: 0d f5 80 55 add.w r5, sp, #4096\n"
- " 54: c5 f8 04 c0 str.w r12, [r5, #4]\n"
- " 58: 5d f8 04 5b ldr r5, [sp], #4\n"
- " 5c: 4f f0 ff 0c mov.w r12, #255\n"
- " 60: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 64: 6f f0 7f 4c mvn r12, #4278190080\n"
- " 68: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 6c: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 70: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 74: 0c 90 str r0, [sp, #48]\n"
- " 76: dd f8 30 c0 ldr.w r12, [sp, #48]\n"
- " 7a: cd f8 34 c0 str.w r12, [sp, #52]\n"
- " 7e: 0d f5 80 5c add.w r12, sp, #4096\n"
- " 82: c9 f8 00 c2 str.w r12, [r9, #512]\n"
- " 86: c9 f8 00 d2 str.w sp, [r9, #512]\n"
- " 8a: d0 f8 30 e0 ldr.w lr, [r0, #48]\n"
- " 8e: f0 47 blx lr\n"
- " 90: dd f8 2c c0 ldr.w r12, [sp, #44]\n"
- " 94: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 98: d9 f8 00 c2 ldr.w r12, [r9, #512]\n"
- " 9c: cd f8 2c c0 str.w r12, [sp, #44]\n"
- " a0: dd f8 2c c0 ldr.w r12, [sp, #44]\n"
- " a4: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " a8: 48 46 mov r0, r9\n"
- " aa: cd f8 30 90 str.w r9, [sp, #48]\n"
- " ae: 04 46 mov r4, r0\n"
- " b0: 0d f1 30 0c add.w r12, sp, #48\n"
- " b4: bb f1 00 0f cmp.w r11, #0\n"
- " b8: 18 bf it ne\n"
- " ba: e3 46 movne r11, r12\n"
- " bc: 0d f1 30 0b add.w r11, sp, #48\n"
- " c0: 5f ea 0b 00 movs.w r0, r11\n"
- " c4: 18 bf it ne\n"
- " c6: 0c a8 addne r0, sp, #48\n"
- " c8: dd f8 40 c0 ldr.w r12, [sp, #64]\n"
- " cc: bc f1 00 0f cmp.w r12, #0\n"
- " d0: 18 bf it ne\n"
- " d2: 0d f1 40 0c addne.w r12, sp, #64\n"
- " d6: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " da: 5f ea 0b 00 movs.w r0, r11\n"
- " de: 18 bf it ne\n"
- " e0: 00 a8 addne r0, sp, #0\n"
- " e2: 0d f2 04 40 addw r0, sp, #1028\n"
- " e6: bb f1 00 0f cmp.w r11, #0\n"
- " ea: 08 bf it eq\n"
- " ec: 58 46 moveq r0, r11\n"
- " ee: 0d f2 04 4c addw r12, sp, #1028\n"
- " f2: bb f1 00 0f cmp.w r11, #0\n"
- " f6: 18 bf it ne\n"
- " f8: e3 46 movne r11, r12\n"
- " fa: d9 f8 94 c0 ldr.w r12, [r9, #148]\n"
- " fe: bc f1 00 0f cmp.w r12, #0\n"
- " 102: 71 d1 bne 0x1e8 @ imm = #226\n"
- " 104: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 108: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 10c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 110: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 114: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 118: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 11c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 120: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 124: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 128: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 12c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 130: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 134: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 138: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 13c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 140: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 144: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 148: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 14c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 150: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 154: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 158: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 15c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 160: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 164: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 168: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 16c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 170: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 174: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 178: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 17c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 180: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 184: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 188: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 18c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 190: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 194: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 198: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 19c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1a0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1a4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1a8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1ac: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1b0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1b4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1b8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1bc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1c0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1c4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1c8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1cc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1d0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1d4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1d8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1dc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1e0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1e4: 00 f0 02 b8 b.w 0x1ec @ imm = #4\n"
- " 1e8: 00 f0 1b b8 b.w 0x222 @ imm = #54\n"
- " 1ec: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1f0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1f4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1f8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1fc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 200: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 204: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 208: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 20c: 0d f5 80 5d add.w sp, sp, #4096\n"
- " 210: 08 b0 add sp, #32\n"
- " 212: 01 b0 add sp, #4\n"
- " 214: bd ec 10 8a vpop {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
- " 218: bd e8 e0 4d pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
- " 21c: d9 f8 24 80 ldr.w r8, [r9, #36]\n"
- " 220: 70 47 bx lr\n"
- " 222: d9 f8 94 00 ldr.w r0, [r9, #148]\n"
- " 226: d9 f8 c8 e2 ldr.w lr, [r9, #712]\n"
- " 22a: f0 47 blx lr\n"
+ " 0: e92d 4de0 push.w {r5, r6, r7, r8, r10, r11, lr}\n"
+ " 4: ed2d 8a10 vpush {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
+ " 8: b081 sub sp, #4\n"
+ " a: 9000 str r0, [sp]\n"
+ " c: 9119 str r1, [sp, #100]\n"
+ " e: ed8d 0a1a vstr s0, [sp, #104]\n"
+ " 12: 921b str r2, [sp, #108]\n"
+ " 14: 931c str r3, [sp, #112]\n"
+ " 16: b088 sub sp, #32\n"
+ " 18: f5ad 5d80 sub.w sp, sp, #4096\n"
+ " 1c: 9808 ldr r0, [sp, #32]\n"
+ " 1e: 981f ldr r0, [sp, #124]\n"
+ " 20: 9821 ldr r0, [sp, #132]\n"
+ " 22: 98ff ldr r0, [sp, #1020]\n"
+ " 24: f8dd 0400 ldr.w r0, [sp, #1024]\n"
+ " 28: f8dd cffc ldr.w r12, [sp, #4092]\n"
+ " 2c: f50d 5c80 add.w r12, sp, #4096\n"
+ " 30: f8dc c000 ldr.w r12, [r12]\n"
+ " 34: f8d9 c200 ldr.w r12, [r9, #512]\n"
+ " 38: f8dc 0080 ldr.w r0, [r12, #128]\n"
+ " 3c: 9008 str r0, [sp, #32]\n"
+ " 3e: 901f str r0, [sp, #124]\n"
+ " 40: 9021 str r0, [sp, #132]\n"
+ " 42: 90ff str r0, [sp, #1020]\n"
+ " 44: f8cd 0400 str.w r0, [sp, #1024]\n"
+ " 48: f8cd cffc str.w r12, [sp, #4092]\n"
+ " 4c: f84d 5d04 str r5, [sp, #-4]!\n"
+ " 50: f50d 5580 add.w r5, sp, #4096\n"
+ " 54: f8c5 c004 str.w r12, [r5, #4]\n"
+ " 58: f85d 5b04 ldr r5, [sp], #4\n"
+ " 5c: f04f 0cff mov.w r12, #255\n"
+ " 60: f8cd c030 str.w r12, [sp, #48]\n"
+ " 64: f06f 4c7f mvn r12, #4278190080\n"
+ " 68: f8cd c030 str.w r12, [sp, #48]\n"
+ " 6c: f8cd c030 str.w r12, [sp, #48]\n"
+ " 70: f8cd c030 str.w r12, [sp, #48]\n"
+ " 74: 900c str r0, [sp, #48]\n"
+ " 76: f8dd c030 ldr.w r12, [sp, #48]\n"
+ " 7a: f8cd c034 str.w r12, [sp, #52]\n"
+ " 7e: f50d 5c80 add.w r12, sp, #4096\n"
+ " 82: f8c9 c200 str.w r12, [r9, #512]\n"
+ " 86: f8c9 d200 str.w sp, [r9, #512]\n"
+ " 8a: f04d 0c02 orr r12, sp, #2\n"
+ " 8e: f8c9 c200 str.w r12, [r9, #512]\n"
+ " 92: f8d0 e030 ldr.w lr, [r0, #48]\n"
+ " 96: 47f0 blx lr\n"
+ " 98: f8dd c02c ldr.w r12, [sp, #44]\n"
+ " 9c: f8cd c030 str.w r12, [sp, #48]\n"
+ " a0: f8d9 c200 ldr.w r12, [r9, #512]\n"
+ " a4: f8cd c02c str.w r12, [sp, #44]\n"
+ " a8: f8dd c02c ldr.w r12, [sp, #44]\n"
+ " ac: f8cd c030 str.w r12, [sp, #48]\n"
+ " b0: 4648 mov r0, r9\n"
+ " b2: f8cd 9030 str.w r9, [sp, #48]\n"
+ " b6: 4604 mov r4, r0\n"
+ " b8: f10d 0c30 add.w r12, sp, #48\n"
+ " bc: f1bb 0f00 cmp.w r11, #0\n"
+ " c0: bf18 it ne\n"
+ " c2: 46e3 movne r11, r12\n"
+ " c4: f10d 0b30 add.w r11, sp, #48\n"
+ " c8: ea5f 000b movs.w r0, r11\n"
+ " cc: bf18 it ne\n"
+ " ce: a80c addne r0, sp, #48\n"
+ " d0: f8dd c040 ldr.w r12, [sp, #64]\n"
+ " d4: f1bc 0f00 cmp.w r12, #0\n"
+ " d8: bf18 it ne\n"
+ " da: f10d 0c40 addne.w r12, sp, #64\n"
+ " de: f8cd c030 str.w r12, [sp, #48]\n"
+ " e2: ea5f 000b movs.w r0, r11\n"
+ " e6: bf18 it ne\n"
+ " e8: a800 addne r0, sp, #0\n"
+ " ea: f20d 4004 addw r0, sp, #1028\n"
+ " ee: f1bb 0f00 cmp.w r11, #0\n"
+ " f2: bf08 it eq\n"
+ " f4: 4658 moveq r0, r11\n"
+ " f6: f20d 4c04 addw r12, sp, #1028\n"
+ " fa: f1bb 0f00 cmp.w r11, #0\n"
+ " fe: bf18 it ne\n"
+ " 100: 46e3 movne r11, r12\n"
+ " 102: f8d9 c09c ldr.w r12, [r9, #156]\n"
+ " 106: f1bc 0f00 cmp.w r12, #0\n"
+ " 10a: d171 bne 0x1f0 @ imm = #226\n"
+ " 10c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 110: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 114: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 118: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 11c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 120: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 124: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 128: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 12c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 130: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 134: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 138: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 13c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 140: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 144: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 148: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 14c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 150: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 154: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 158: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 15c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 160: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 164: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 168: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 16c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 170: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 174: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 178: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 17c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 180: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 184: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 188: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 18c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 190: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 194: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 198: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 19c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1a0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1a4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1a8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1ac: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1b0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1b4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1b8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1bc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1c0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1c4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1c8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1cc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1d0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1d4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1d8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1dc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1e0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1e4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1e8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1ec: f000 b802 b.w 0x1f4 @ imm = #4\n"
+ " 1f0: f000 b81b b.w 0x22a @ imm = #54\n"
+ " 1f4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1f8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1fc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 200: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 204: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 208: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 20c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 210: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 214: f50d 5d80 add.w sp, sp, #4096\n"
+ " 218: b008 add sp, #32\n"
+ " 21a: b001 add sp, #4\n"
+ " 21c: ecbd 8a10 vpop {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
+ " 220: e8bd 4de0 pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
+ " 224: f8d9 8024 ldr.w r8, [r9, #36]\n"
+ " 228: 4770 bx lr\n"
+ " 22a: f8d9 009c ldr.w r0, [r9, #156]\n"
+ " 22e: f8d9 e2d0 ldr.w lr, [r9, #720]\n"
+ " 232: 47f0 blx lr\n"
};
const char* const VixlLoadFromOffsetResults = {
- " 0: e2 68 ldr r2, [r4, #12]\n"
- " 2: d4 f8 ff 2f ldr.w r2, [r4, #4095]\n"
- " 6: 04 f5 80 52 add.w r2, r4, #4096\n"
- " a: 12 68 ldr r2, [r2]\n"
- " c: 04 f5 80 12 add.w r2, r4, #1048576\n"
- " 10: d2 f8 a4 20 ldr.w r2, [r2, #164]\n"
- " 14: 4f f4 80 52 mov.w r2, #4096\n"
- " 18: c0 f2 10 02 movt r2, #16\n"
- " 1c: 22 44 add r2, r4\n"
- " 1e: 12 68 ldr r2, [r2]\n"
- " 20: 4f f4 80 5c mov.w r12, #4096\n"
- " 24: c0 f2 10 0c movt r12, #16\n"
- " 28: 64 44 add r4, r12\n"
- " 2a: 24 68 ldr r4, [r4]\n"
- " 2c: a2 89 ldrh r2, [r4, #12]\n"
- " 2e: b4 f8 ff 2f ldrh.w r2, [r4, #4095]\n"
- " 32: 04 f5 80 52 add.w r2, r4, #4096\n"
- " 36: 12 88 ldrh r2, [r2]\n"
- " 38: 04 f5 80 12 add.w r2, r4, #1048576\n"
- " 3c: b2 f8 a4 20 ldrh.w r2, [r2, #164]\n"
- " 40: 4f f4 80 52 mov.w r2, #4096\n"
- " 44: c0 f2 10 02 movt r2, #16\n"
- " 48: 22 44 add r2, r4\n"
- " 4a: 12 88 ldrh r2, [r2]\n"
- " 4c: 4f f4 80 5c mov.w r12, #4096\n"
- " 50: c0 f2 10 0c movt r12, #16\n"
- " 54: 64 44 add r4, r12\n"
- " 56: 24 88 ldrh r4, [r4]\n"
- " 58: d4 e9 03 23 ldrd r2, r3, [r4, #12]\n"
- " 5c: d4 e9 ff 23 ldrd r2, r3, [r4, #1020]\n"
- " 60: 04 f5 80 62 add.w r2, r4, #1024\n"
- " 64: d2 e9 00 23 ldrd r2, r3, [r2]\n"
- " 68: 04 f5 80 22 add.w r2, r4, #262144\n"
- " 6c: d2 e9 29 23 ldrd r2, r3, [r2, #164]\n"
- " 70: 4f f4 80 62 mov.w r2, #1024\n"
- " 74: c0 f2 04 02 movt r2, #4\n"
- " 78: 22 44 add r2, r4\n"
- " 7a: d2 e9 00 23 ldrd r2, r3, [r2]\n"
- " 7e: 4f f4 80 6c mov.w r12, #1024\n"
- " 82: c0 f2 04 0c movt r12, #4\n"
- " 86: 64 44 add r4, r12\n"
- " 88: d4 e9 00 45 ldrd r4, r5, [r4]\n"
- " 8c: dc f8 0c 00 ldr.w r0, [r12, #12]\n"
- " 90: a4 f5 80 12 sub.w r2, r4, #1048576\n"
- " 94: d2 f8 a4 20 ldr.w r2, [r2, #164]\n"
- " 98: 94 f9 0c 20 ldrsb.w r2, [r4, #12]\n"
- " 9c: 22 7b ldrb r2, [r4, #12]\n"
- " 9e: b4 f9 0c 20 ldrsh.w r2, [r4, #12]\n"
+ " 0: 68e2 ldr r2, [r4, #12]\n"
+ " 2: f8d4 2fff ldr.w r2, [r4, #4095]\n"
+ " 6: f504 5280 add.w r2, r4, #4096\n"
+ " a: 6812 ldr r2, [r2]\n"
+ " c: f504 1280 add.w r2, r4, #1048576\n"
+ " 10: f8d2 20a4 ldr.w r2, [r2, #164]\n"
+ " 14: f44f 5280 mov.w r2, #4096\n"
+ " 18: f2c0 0210 movt r2, #16\n"
+ " 1c: 4422 add r2, r4\n"
+ " 1e: 6812 ldr r2, [r2]\n"
+ " 20: f44f 5c80 mov.w r12, #4096\n"
+ " 24: f2c0 0c10 movt r12, #16\n"
+ " 28: 4464 add r4, r12\n"
+ " 2a: 6824 ldr r4, [r4]\n"
+ " 2c: 89a2 ldrh r2, [r4, #12]\n"
+ " 2e: f8b4 2fff ldrh.w r2, [r4, #4095]\n"
+ " 32: f504 5280 add.w r2, r4, #4096\n"
+ " 36: 8812 ldrh r2, [r2]\n"
+ " 38: f504 1280 add.w r2, r4, #1048576\n"
+ " 3c: f8b2 20a4 ldrh.w r2, [r2, #164]\n"
+ " 40: f44f 5280 mov.w r2, #4096\n"
+ " 44: f2c0 0210 movt r2, #16\n"
+ " 48: 4422 add r2, r4\n"
+ " 4a: 8812 ldrh r2, [r2]\n"
+ " 4c: f44f 5c80 mov.w r12, #4096\n"
+ " 50: f2c0 0c10 movt r12, #16\n"
+ " 54: 4464 add r4, r12\n"
+ " 56: 8824 ldrh r4, [r4]\n"
+ " 58: e9d4 2303 ldrd r2, r3, [r4, #12]\n"
+ " 5c: e9d4 23ff ldrd r2, r3, [r4, #1020]\n"
+ " 60: f504 6280 add.w r2, r4, #1024\n"
+ " 64: e9d2 2300 ldrd r2, r3, [r2]\n"
+ " 68: f504 2280 add.w r2, r4, #262144\n"
+ " 6c: e9d2 2329 ldrd r2, r3, [r2, #164]\n"
+ " 70: f44f 6280 mov.w r2, #1024\n"
+ " 74: f2c0 0204 movt r2, #4\n"
+ " 78: 4422 add r2, r4\n"
+ " 7a: e9d2 2300 ldrd r2, r3, [r2]\n"
+ " 7e: f44f 6c80 mov.w r12, #1024\n"
+ " 82: f2c0 0c04 movt r12, #4\n"
+ " 86: 4464 add r4, r12\n"
+ " 88: e9d4 4500 ldrd r4, r5, [r4]\n"
+ " 8c: f8dc 000c ldr.w r0, [r12, #12]\n"
+ " 90: f5a4 1280 sub.w r2, r4, #1048576\n"
+ " 94: f8d2 20a4 ldr.w r2, [r2, #164]\n"
+ " 98: f994 200c ldrsb.w r2, [r4, #12]\n"
+ " 9c: 7b22 ldrb r2, [r4, #12]\n"
+ " 9e: f9b4 200c ldrsh.w r2, [r4, #12]\n"
};
const char* const VixlStoreToOffsetResults = {
- " 0: e2 60 str r2, [r4, #12]\n"
- " 2: c4 f8 ff 2f str.w r2, [r4, #4095]\n"
- " 6: 04 f5 80 5c add.w r12, r4, #4096\n"
- " a: cc f8 00 20 str.w r2, [r12]\n"
- " e: 04 f5 80 1c add.w r12, r4, #1048576\n"
- " 12: cc f8 a4 20 str.w r2, [r12, #164]\n"
- " 16: 4f f4 80 5c mov.w r12, #4096\n"
- " 1a: c0 f2 10 0c movt r12, #16\n"
- " 1e: a4 44 add r12, r4\n"
- " 20: cc f8 00 20 str.w r2, [r12]\n"
- " 24: 4f f4 80 5c mov.w r12, #4096\n"
- " 28: c0 f2 10 0c movt r12, #16\n"
- " 2c: a4 44 add r12, r4\n"
- " 2e: cc f8 00 40 str.w r4, [r12]\n"
- " 32: a2 81 strh r2, [r4, #12]\n"
- " 34: a4 f8 ff 2f strh.w r2, [r4, #4095]\n"
- " 38: 04 f5 80 5c add.w r12, r4, #4096\n"
- " 3c: ac f8 00 20 strh.w r2, [r12]\n"
- " 40: 04 f5 80 1c add.w r12, r4, #1048576\n"
- " 44: ac f8 a4 20 strh.w r2, [r12, #164]\n"
- " 48: 4f f4 80 5c mov.w r12, #4096\n"
- " 4c: c0 f2 10 0c movt r12, #16\n"
- " 50: a4 44 add r12, r4\n"
- " 52: ac f8 00 20 strh.w r2, [r12]\n"
- " 56: 4f f4 80 5c mov.w r12, #4096\n"
- " 5a: c0 f2 10 0c movt r12, #16\n"
- " 5e: a4 44 add r12, r4\n"
- " 60: ac f8 00 40 strh.w r4, [r12]\n"
- " 64: c4 e9 03 23 strd r2, r3, [r4, #12]\n"
- " 68: c4 e9 ff 23 strd r2, r3, [r4, #1020]\n"
- " 6c: 04 f5 80 6c add.w r12, r4, #1024\n"
- " 70: cc e9 00 23 strd r2, r3, [r12]\n"
- " 74: 04 f5 80 2c add.w r12, r4, #262144\n"
- " 78: cc e9 29 23 strd r2, r3, [r12, #164]\n"
- " 7c: 4f f4 80 6c mov.w r12, #1024\n"
- " 80: c0 f2 04 0c movt r12, #4\n"
- " 84: a4 44 add r12, r4\n"
- " 86: cc e9 00 23 strd r2, r3, [r12]\n"
- " 8a: 4f f4 80 6c mov.w r12, #1024\n"
- " 8e: c0 f2 04 0c movt r12, #4\n"
- " 92: a4 44 add r12, r4\n"
- " 94: cc e9 00 45 strd r4, r5, [r12]\n"
- " 98: cc f8 0c 00 str.w r0, [r12, #12]\n"
- " 9c: a4 f5 80 1c sub.w r12, r4, #1048576\n"
- " a0: cc f8 a4 20 str.w r2, [r12, #164]\n"
- " a4: 22 73 strb r2, [r4, #12]\n"
+ " 0: 60e2 str r2, [r4, #12]\n"
+ " 2: f8c4 2fff str.w r2, [r4, #4095]\n"
+ " 6: f504 5c80 add.w r12, r4, #4096\n"
+ " a: f8cc 2000 str.w r2, [r12]\n"
+ " e: f504 1c80 add.w r12, r4, #1048576\n"
+ " 12: f8cc 20a4 str.w r2, [r12, #164]\n"
+ " 16: f44f 5c80 mov.w r12, #4096\n"
+ " 1a: f2c0 0c10 movt r12, #16\n"
+ " 1e: 44a4 add r12, r4\n"
+ " 20: f8cc 2000 str.w r2, [r12]\n"
+ " 24: f44f 5c80 mov.w r12, #4096\n"
+ " 28: f2c0 0c10 movt r12, #16\n"
+ " 2c: 44a4 add r12, r4\n"
+ " 2e: f8cc 4000 str.w r4, [r12]\n"
+ " 32: 81a2 strh r2, [r4, #12]\n"
+ " 34: f8a4 2fff strh.w r2, [r4, #4095]\n"
+ " 38: f504 5c80 add.w r12, r4, #4096\n"
+ " 3c: f8ac 2000 strh.w r2, [r12]\n"
+ " 40: f504 1c80 add.w r12, r4, #1048576\n"
+ " 44: f8ac 20a4 strh.w r2, [r12, #164]\n"
+ " 48: f44f 5c80 mov.w r12, #4096\n"
+ " 4c: f2c0 0c10 movt r12, #16\n"
+ " 50: 44a4 add r12, r4\n"
+ " 52: f8ac 2000 strh.w r2, [r12]\n"
+ " 56: f44f 5c80 mov.w r12, #4096\n"
+ " 5a: f2c0 0c10 movt r12, #16\n"
+ " 5e: 44a4 add r12, r4\n"
+ " 60: f8ac 4000 strh.w r4, [r12]\n"
+ " 64: e9c4 2303 strd r2, r3, [r4, #12]\n"
+ " 68: e9c4 23ff strd r2, r3, [r4, #1020]\n"
+ " 6c: f504 6c80 add.w r12, r4, #1024\n"
+ " 70: e9cc 2300 strd r2, r3, [r12]\n"
+ " 74: f504 2c80 add.w r12, r4, #262144\n"
+ " 78: e9cc 2329 strd r2, r3, [r12, #164]\n"
+ " 7c: f44f 6c80 mov.w r12, #1024\n"
+ " 80: f2c0 0c04 movt r12, #4\n"
+ " 84: 44a4 add r12, r4\n"
+ " 86: e9cc 2300 strd r2, r3, [r12]\n"
+ " 8a: f44f 6c80 mov.w r12, #1024\n"
+ " 8e: f2c0 0c04 movt r12, #4\n"
+ " 92: 44a4 add r12, r4\n"
+ " 94: e9cc 4500 strd r4, r5, [r12]\n"
+ " 98: f8cc 000c str.w r0, [r12, #12]\n"
+ " 9c: f5a4 1c80 sub.w r12, r4, #1048576\n"
+ " a0: f8cc 20a4 str.w r2, [r12, #164]\n"
+ " a4: 7322 strb r2, [r4, #12]\n"
};
diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h
index 377b7fe..5f68a7c 100644
--- a/compiler/utils/atomic_dex_ref_map-inl.h
+++ b/compiler/utils/atomic_dex_ref_map-inl.h
@@ -21,12 +21,13 @@
#include <type_traits>
+#include "base/macros.h"
#include "dex/class_reference.h"
#include "dex/dex_file-inl.h"
#include "dex/method_reference.h"
#include "dex/type_reference.h"
-namespace art {
+namespace art HIDDEN {
template <typename DexFileReferenceType, typename Value>
inline size_t AtomicDexRefMap<DexFileReferenceType, Value>::NumberOfDexIndices(
diff --git a/compiler/utils/atomic_dex_ref_map.h b/compiler/utils/atomic_dex_ref_map.h
index a8c285f..b10fef5 100644
--- a/compiler/utils/atomic_dex_ref_map.h
+++ b/compiler/utils/atomic_dex_ref_map.h
@@ -19,10 +19,11 @@
#include "base/atomic.h"
#include "base/dchecked_vector.h"
+#include "base/macros.h"
#include "base/safe_map.h"
#include "dex/dex_file_reference.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
diff --git a/compiler/utils/atomic_dex_ref_map_test.cc b/compiler/utils/atomic_dex_ref_map_test.cc
index 864531e..329735b 100644
--- a/compiler/utils/atomic_dex_ref_map_test.cc
+++ b/compiler/utils/atomic_dex_ref_map_test.cc
@@ -18,12 +18,13 @@
#include <memory>
+#include "base/macros.h"
#include "common_runtime_test.h"
#include "dex/dex_file-inl.h"
#include "dex/method_reference.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
class AtomicDexRefMapTest : public CommonRuntimeTest {};
diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h
index d4a9cc8..db744c5 100644
--- a/compiler/utils/dedupe_set-inl.h
+++ b/compiler/utils/dedupe_set-inl.h
@@ -27,11 +27,12 @@
#include "android-base/stringprintf.h"
#include "base/hash_set.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/stl_util.h"
#include "base/time_utils.h"
-namespace art {
+namespace art HIDDEN {
template <typename InKey,
typename StoreKey,
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index a1ba208..42db8e3 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -23,7 +23,7 @@
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class Thread;
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index b390508..89385e7 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -21,11 +21,12 @@
#include <vector>
#include "base/array_ref.h"
+#include "base/macros.h"
#include "dedupe_set-inl.h"
#include "gtest/gtest.h"
#include "thread-current-inl.h"
-namespace art {
+namespace art HIDDEN {
class DedupeSetTestHashFunc {
public:
diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc
index d6d49f8..f11b48d 100644
--- a/compiler/utils/jni_macro_assembler.cc
+++ b/compiler/utils/jni_macro_assembler.cc
@@ -35,7 +35,7 @@
#include "base/globals.h"
#include "base/memory_region.h"
-namespace art {
+namespace art HIDDEN {
using MacroAsm32UniquePtr = std::unique_ptr<JNIMacroAssembler<PointerSize::k32>>;
diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h
index 7022e3d..79dd987 100644
--- a/compiler/utils/jni_macro_assembler.h
+++ b/compiler/utils/jni_macro_assembler.h
@@ -30,7 +30,7 @@
#include "managed_register.h"
#include "offsets.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class DebugFrameOpCodeWriterForAssembler;
@@ -126,7 +126,11 @@
virtual void StoreStackOffsetToThread(ThreadOffset<kPointerSize> thr_offs,
FrameOffset fr_offs) = 0;
- virtual void StoreStackPointerToThread(ThreadOffset<kPointerSize> thr_offs) = 0;
+ // Stores stack pointer by tagging it if required so we can walk the stack. In debuggable runtimes
+ // we use tag to tell if we are using JITed code or AOT code. In non-debuggable runtimes we never
+ // use JITed code when AOT code is present. So checking for AOT code is sufficient to detect which
+ // code is being executed. We avoid tagging in non-debuggable runtimes to reduce instructions.
+ virtual void StoreStackPointerToThread(ThreadOffset<kPointerSize> thr_offs, bool tag_sp) = 0;
virtual void StoreSpanning(FrameOffset dest,
ManagedRegister src,
@@ -211,6 +215,8 @@
ManagedRegister scratch,
size_t size) = 0;
+ virtual void Move(ManagedRegister dst, size_t value) = 0;
+
virtual void MemoryBarrier(ManagedRegister scratch) = 0;
// Sign extension
@@ -282,6 +288,8 @@
virtual void TestMarkBit(ManagedRegister ref,
JNIMacroLabel* label,
JNIMacroUnaryCondition cond) = 0;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ virtual void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) = 0;
// Code at this offset will serve as the target for the Jump call.
virtual void Bind(JNIMacroLabel* label) = 0;
diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h
index e77177e..ac8e7d3 100644
--- a/compiler/utils/jni_macro_assembler_test.h
+++ b/compiler/utils/jni_macro_assembler_test.h
@@ -20,6 +20,7 @@
#include "jni_macro_assembler.h"
#include "assembler_test_base.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "common_runtime_test.h" // For ScratchFile
@@ -30,7 +31,7 @@
#include <fstream>
#include <iterator>
-namespace art {
+namespace art HIDDEN {
template<typename Ass>
class JNIMacroAssemblerTest : public AssemblerTestBase {
@@ -39,7 +40,7 @@
return assembler_.get();
}
- typedef std::string (*TestFn)(JNIMacroAssemblerTest* assembler_test, Ass* assembler);
+ using TestFn = std::string (*)(JNIMacroAssemblerTest *, Ass *);
void DriverFn(TestFn f, const std::string& test_name) {
DriverWrapper(f(this, assembler_.get()), test_name);
diff --git a/compiler/utils/label.h b/compiler/utils/label.h
index 282500b..0368d90 100644
--- a/compiler/utils/label.h
+++ b/compiler/utils/label.h
@@ -20,7 +20,9 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class Assembler;
class AssemblerBuffer;
diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h
index a3b33ba..72acbd5 100644
--- a/compiler/utils/managed_register.h
+++ b/compiler/utils/managed_register.h
@@ -20,9 +20,10 @@
#include <type_traits>
#include <vector>
+#include "base/macros.h"
#include "base/value_object.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class ArmManagedRegister;
diff --git a/compiler/utils/stack_checks.h b/compiler/utils/stack_checks.h
index c348f2c..d0fff73 100644
--- a/compiler/utils/stack_checks.h
+++ b/compiler/utils/stack_checks.h
@@ -18,8 +18,9 @@
#define ART_COMPILER_UTILS_STACK_CHECKS_H_
#include "arch/instruction_set.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
// Size of a frame that we definitely consider large. Anything larger than this should
// definitely get a stack overflow check.
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
deleted file mode 100644
index 827e9a6..0000000
--- a/compiler/utils/swap_space.h
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_UTILS_SWAP_SPACE_H_
-#define ART_COMPILER_UTILS_SWAP_SPACE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <cstdlib>
-#include <list>
-#include <set>
-#include <vector>
-
-#include <android-base/logging.h>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-
-namespace art {
-
-// An arena pool that creates arenas backed by an mmaped file.
-class SwapSpace {
- public:
- SwapSpace(int fd, size_t initial_size);
- ~SwapSpace();
- void* Alloc(size_t size) REQUIRES(!lock_);
- void Free(void* ptr, size_t size) REQUIRES(!lock_);
-
- size_t GetSize() {
- return size_;
- }
-
- private:
- // Chunk of space.
- struct SpaceChunk {
- // We need mutable members as we keep these objects in a std::set<> (providing only const
- // access) but we modify these members while carefully preserving the std::set<> ordering.
- mutable uint8_t* ptr;
- mutable size_t size;
-
- uintptr_t Start() const {
- return reinterpret_cast<uintptr_t>(ptr);
- }
- uintptr_t End() const {
- return reinterpret_cast<uintptr_t>(ptr) + size;
- }
- };
-
- class SortChunkByPtr {
- public:
- bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
- return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
- }
- };
-
- using FreeByStartSet = std::set<SpaceChunk, SortChunkByPtr>;
-
- // Map size to an iterator to free_by_start_'s entry.
- struct FreeBySizeEntry {
- FreeBySizeEntry(size_t sz, FreeByStartSet::const_iterator entry)
- : size(sz), free_by_start_entry(entry) { }
-
- // We need mutable members as we keep these objects in a std::set<> (providing only const
- // access) but we modify these members while carefully preserving the std::set<> ordering.
- mutable size_t size;
- mutable FreeByStartSet::const_iterator free_by_start_entry;
- };
- struct FreeBySizeComparator {
- bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) const {
- if (lhs.size != rhs.size) {
- return lhs.size < rhs.size;
- } else {
- return lhs.free_by_start_entry->Start() < rhs.free_by_start_entry->Start();
- }
- }
- };
- using FreeBySizeSet = std::set<FreeBySizeEntry, FreeBySizeComparator>;
-
- SpaceChunk NewFileChunk(size_t min_size) REQUIRES(lock_);
-
- void RemoveChunk(FreeBySizeSet::const_iterator free_by_size_pos) REQUIRES(lock_);
- void InsertChunk(const SpaceChunk& chunk) REQUIRES(lock_);
-
- int fd_;
- size_t size_;
-
- // NOTE: Boost.Bimap would be useful for the two following members.
-
- // Map start of a free chunk to its size.
- FreeByStartSet free_by_start_ GUARDED_BY(lock_);
- // Free chunks ordered by size.
- FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
-
- mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- DISALLOW_COPY_AND_ASSIGN(SwapSpace);
-};
-
-template <typename T> class SwapAllocator;
-
-template <>
-class SwapAllocator<void> {
- public:
- using value_type = void;
- using pointer = void*;
- using const_pointer = const void*;
-
- template <typename U>
- struct rebind {
- using other = SwapAllocator<U>;
- };
-
- explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
-
- template <typename U>
- SwapAllocator(const SwapAllocator<U>& other)
- : swap_space_(other.swap_space_) {}
-
- SwapAllocator(const SwapAllocator& other) = default;
- SwapAllocator& operator=(const SwapAllocator& other) = default;
- ~SwapAllocator() = default;
-
- private:
- SwapSpace* swap_space_;
-
- template <typename U>
- friend class SwapAllocator;
-
- template <typename U>
- friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
-};
-
-template <typename T>
-class SwapAllocator {
- public:
- using value_type = T;
- using pointer = T*;
- using reference = T&;
- using const_pointer = const T*;
- using const_reference = const T&;
- using size_type = size_t;
- using difference_type = ptrdiff_t;
-
- template <typename U>
- struct rebind {
- using other = SwapAllocator<U>;
- };
-
- explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
-
- template <typename U>
- SwapAllocator(const SwapAllocator<U>& other)
- : swap_space_(other.swap_space_) {}
-
- SwapAllocator(const SwapAllocator& other) = default;
- SwapAllocator& operator=(const SwapAllocator& other) = default;
- ~SwapAllocator() = default;
-
- size_type max_size() const {
- return static_cast<size_type>(-1) / sizeof(T);
- }
-
- pointer address(reference x) const { return &x; }
- const_pointer address(const_reference x) const { return &x; }
-
- pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
- DCHECK_LE(n, max_size());
- if (swap_space_ == nullptr) {
- T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
- CHECK_IMPLIES(result == nullptr, n == 0u); // Abort if malloc() fails.
- return result;
- } else {
- return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
- }
- }
- void deallocate(pointer p, size_type n) {
- if (swap_space_ == nullptr) {
- free(p);
- } else {
- swap_space_->Free(p, n * sizeof(T));
- }
- }
-
- void construct(pointer p, const_reference val) {
- new (static_cast<void*>(p)) value_type(val);
- }
- template <class U, class... Args>
- void construct(U* p, Args&&... args) {
- ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
- }
- void destroy(pointer p) {
- p->~value_type();
- }
-
- inline bool operator==(SwapAllocator const& other) {
- return swap_space_ == other.swap_space_;
- }
- inline bool operator!=(SwapAllocator const& other) {
- return !operator==(other);
- }
-
- private:
- SwapSpace* swap_space_;
-
- template <typename U>
- friend class SwapAllocator;
-
- template <typename U>
- friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
-};
-
-template <typename T>
-inline bool operator==(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
- return lhs.swap_space_ == rhs.swap_space_;
-}
-
-template <typename T>
-inline bool operator!=(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
- return !(lhs == rhs);
-}
-
-template <typename T>
-using SwapVector = std::vector<T, SwapAllocator<T>>;
-template <typename T, typename Comparator>
-using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
-
-} // namespace art
-
-#endif // ART_COMPILER_UTILS_SWAP_SPACE_H_
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
deleted file mode 100644
index 1650080..0000000
--- a/compiler/utils/swap_space_test.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 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 "utils/swap_space.h"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <cstdio>
-
-#include "gtest/gtest.h"
-
-#include "base/os.h"
-#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
-
-namespace art {
-
-class SwapSpaceTest : public CommonRuntimeTest {
-};
-
-static void SwapTest(bool use_file) {
- ScratchFile scratch;
- int fd = scratch.GetFd();
- unlink(scratch.GetFilename().c_str());
-
- SwapSpace pool(fd, 1 * MB);
- SwapAllocator<void> alloc(use_file ? &pool : nullptr);
-
- SwapVector<int32_t> v(alloc);
- v.reserve(1000000);
- for (int32_t i = 0; i < 1000000; ++i) {
- v.push_back(i);
- EXPECT_EQ(i, v[i]);
- }
-
- SwapVector<int32_t> v2(alloc);
- v2.reserve(1000000);
- for (int32_t i = 0; i < 1000000; ++i) {
- v2.push_back(i);
- EXPECT_EQ(i, v2[i]);
- }
-
- SwapVector<int32_t> v3(alloc);
- v3.reserve(500000);
- for (int32_t i = 0; i < 1000000; ++i) {
- v3.push_back(i);
- EXPECT_EQ(i, v2[i]);
- }
-
- // Verify contents.
- for (int32_t i = 0; i < 1000000; ++i) {
- EXPECT_EQ(i, v[i]);
- EXPECT_EQ(i, v2[i]);
- EXPECT_EQ(i, v3[i]);
- }
-
- scratch.Close();
-}
-
-TEST_F(SwapSpaceTest, Memory) {
- SwapTest(false);
-}
-
-TEST_F(SwapSpaceTest, Swap) {
- SwapTest(true);
-}
-
-} // namespace art
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 861b27e..a6b9011 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -21,7 +21,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
std::ostream& operator<<(std::ostream& os, const XmmRegister& reg) {
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index c346ba9..0f7854d 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -32,7 +32,7 @@
#include "offsets.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
class Immediate : public ValueObject {
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 89c73c0..5da6f04 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -17,11 +17,12 @@
#include "assembler_x86.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "base/stl_util.h"
#include "utils/assembler_test.h"
-namespace art {
+namespace art HIDDEN {
TEST(AssemblerX86, CreateBuffer) {
MallocArenaPool pool;
diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h
index 477b915..0c0a7d4 100644
--- a/compiler/utils/x86/constants_x86.h
+++ b/compiler/utils/x86/constants_x86.h
@@ -25,7 +25,7 @@
#include "base/globals.h"
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
enum ByteRegister {
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc
index 685f5f1..7bb167c 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.cc
+++ b/compiler/utils/x86/jni_macro_assembler_x86.cc
@@ -22,7 +22,7 @@
#include "thread.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
static Register GetScratchRegister() {
@@ -187,8 +187,18 @@
__ fs()->movl(Address::Absolute(thr_offs), scratch);
}
-void X86JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
- __ fs()->movl(Address::Absolute(thr_offs), ESP);
+void X86JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) {
+ if (tag_sp) {
+ // There is no free register, store contents onto stack and restore back later.
+ Register scratch = ECX;
+ __ movl(Address(ESP, -32), scratch);
+ __ movl(scratch, ESP);
+ __ orl(scratch, Immediate(0x2));
+ __ fs()->movl(Address::Absolute(thr_offs), scratch);
+ __ movl(scratch, Address(ESP, -32));
+ } else {
+ __ fs()->movl(Address::Absolute(thr_offs), ESP);
+ }
}
void X86JNIMacroAssembler::StoreSpanning(FrameOffset /*dst*/,
@@ -402,6 +412,11 @@
}
}
+void X86JNIMacroAssembler::Move(ManagedRegister mdest, size_t value) {
+ X86ManagedRegister dest = mdest.AsX86();
+ __ movl(dest.AsCpuRegister(), Immediate(value));
+}
+
void X86JNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src) {
Register scratch = GetScratchRegister();
__ movl(scratch, Address(ESP, src));
@@ -724,6 +739,12 @@
__ j(UnaryConditionToX86Condition(cond), X86JNIMacroLabel::Cast(label)->AsX86());
}
+
+void X86JNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ __ cmpb(Address::Absolute(address), Immediate(0));
+ __ j(kNotZero, X86JNIMacroLabel::Cast(label)->AsX86());
+}
+
void X86JNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
__ Bind(X86JNIMacroLabel::Cast(label)->AsX86());
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.h b/compiler/utils/x86/jni_macro_assembler_x86.h
index 29fccfd..eba4b99 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.h
+++ b/compiler/utils/x86/jni_macro_assembler_x86.h
@@ -27,7 +27,7 @@
#include "offsets.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
class X86JNIMacroLabel;
@@ -66,7 +66,7 @@
void StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset32 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
@@ -92,6 +92,8 @@
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
+
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs, ManagedRegister scratch)
@@ -189,6 +191,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc
index cc7cedf..bef9480 100644
--- a/compiler/utils/x86/managed_register_x86.cc
+++ b/compiler/utils/x86/managed_register_x86.cc
@@ -18,7 +18,7 @@
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// Define register pairs.
diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h
index 27555bf..def4f68 100644
--- a/compiler/utils/x86/managed_register_x86.h
+++ b/compiler/utils/x86/managed_register_x86.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_
#define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_
+#include "base/macros.h"
#include "constants_x86.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// Values for register pairs.
diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc
index 28af531..9f5e197 100644
--- a/compiler/utils/x86/managed_register_x86_test.cc
+++ b/compiler/utils/x86/managed_register_x86_test.cc
@@ -17,9 +17,10 @@
#include "managed_register_x86.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
TEST(X86ManagedRegister, NoRegister) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 21a4481..3fdf05b 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -21,7 +21,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
std::ostream& operator<<(std::ostream& os, const CpuRegister& reg) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index ea944c2..9402fe3 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -32,7 +32,7 @@
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Encodes an immediate value for operands.
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index f7e890d..a7c206a 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -21,13 +21,14 @@
#include <random>
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "base/stl_util.h"
#include "jni_macro_assembler_x86_64.h"
#include "utils/assembler_test.h"
#include "utils/jni_macro_assembler_test.h"
-namespace art {
+namespace art HIDDEN {
TEST(AssemblerX86_64, CreateBuffer) {
MallocArenaPool pool;
diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h
index 301c8fc..52ac987 100644
--- a/compiler/utils/x86_64/constants_x86_64.h
+++ b/compiler/utils/x86_64/constants_x86_64.h
@@ -25,7 +25,7 @@
#include "base/globals.h"
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
class CpuRegister {
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index d5d1bba..044027a 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -22,7 +22,7 @@
#include "lock_word.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
static dwarf::Reg DWARFReg(Register reg) {
@@ -217,8 +217,15 @@
__ gs()->movq(Address::Absolute(thr_offs, true), scratch);
}
-void X86_64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 thr_offs) {
- __ gs()->movq(Address::Absolute(thr_offs, true), CpuRegister(RSP));
+void X86_64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) {
+ if (tag_sp) {
+ CpuRegister reg = GetScratchRegister();
+ __ movq(reg, CpuRegister(RSP));
+ __ orq(reg, Immediate(0x2));
+ __ gs()->movq(Address::Absolute(thr_offs, true), reg);
+ } else {
+ __ gs()->movq(Address::Absolute(thr_offs, true), CpuRegister(RSP));
+ }
}
void X86_64JNIMacroAssembler::StoreSpanning(FrameOffset /*dst*/,
@@ -477,6 +484,12 @@
}
}
+
+void X86_64JNIMacroAssembler::Move(ManagedRegister mdest, size_t value) {
+ X86_64ManagedRegister dest = mdest.AsX86_64();
+ __ movq(dest.AsCpuRegister(), Immediate(value));
+}
+
void X86_64JNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src) {
CpuRegister scratch = GetScratchRegister();
__ movl(scratch, Address(CpuRegister(RSP), src));
@@ -803,6 +816,13 @@
__ j(UnaryConditionToX86_64Condition(cond), X86_64JNIMacroLabel::Cast(label)->AsX86_64());
}
+void X86_64JNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ CpuRegister scratch = GetScratchRegister();
+ __ movq(scratch, Immediate(address));
+ __ cmpb(Address(scratch, 0), Immediate(0));
+ __ j(kNotZero, X86_64JNIMacroLabel::Cast(label)->AsX86_64());
+}
+
void X86_64JNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
__ Bind(X86_64JNIMacroLabel::Cast(label)->AsX86_64());
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
index e080f0b..3308c7e 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
@@ -28,7 +28,7 @@
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
class X86_64JNIMacroAssembler final : public JNIMacroAssemblerFwd<X86_64Assembler,
@@ -67,7 +67,7 @@
void StoreStackOffsetToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset64 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
@@ -95,6 +95,8 @@
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
+
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset64 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs, ManagedRegister scratch)
@@ -209,6 +211,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/x86_64/managed_register_x86_64.cc b/compiler/utils/x86_64/managed_register_x86_64.cc
index c0eec9d..75ff8aa 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.cc
+++ b/compiler/utils/x86_64/managed_register_x86_64.cc
@@ -18,7 +18,7 @@
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Define register pairs.
diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h
index 62c0e37..7a1be0b 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.h
+++ b/compiler/utils/x86_64/managed_register_x86_64.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_
#define ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_
+#include "base/macros.h"
#include "constants_x86_64.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Values for register pairs.
diff --git a/compiler/utils/x86_64/managed_register_x86_64_test.cc b/compiler/utils/x86_64/managed_register_x86_64_test.cc
index 46a405f..048268b 100644
--- a/compiler/utils/x86_64/managed_register_x86_64_test.cc
+++ b/compiler/utils/x86_64/managed_register_x86_64_test.cc
@@ -16,9 +16,10 @@
#include "managed_register_x86_64.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
TEST(X86_64ManagedRegister, NoRegister) {
diff --git a/default_build b/default_build
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/default_build
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 941b9e0..d04a8f6 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -29,6 +29,9 @@
host_supported: true,
srcs: [
"dex/quick_compiler_callbacks.cc",
+ "dex/verification_results.cc",
+ "driver/compiled_method.cc",
+ "driver/compiled_method_storage.cc",
"driver/compiler_driver.cc",
"linker/code_info_table_deduper.cc",
"linker/elf_writer.cc",
@@ -37,6 +40,7 @@
"linker/multi_oat_relative_patcher.cc",
"linker/oat_writer.cc",
"linker/relative_patcher.cc",
+ "utils/swap_space.cc",
],
codegen: {
@@ -106,6 +110,9 @@
"libartpalette",
"libprofile",
],
+ static_libs: [
+ "libelffile",
+ ],
apex_available: [
"com.android.art",
"com.android.art.debug",
@@ -157,6 +164,9 @@
"libartpalette",
"libprofiled",
],
+ static_libs: [
+ "libelffiled",
+ ],
apex_available: [
"com.android.art.debug",
],
@@ -242,6 +252,12 @@
profile_file: "art/dex2oat_arm_arm64.profdata",
},
},
+ android_riscv64: {
+ pgo: {
+ enable_profile_use: false,
+ profile_file: "",
+ },
+ },
android_x86_64: {
pgo: {
profile_file: "art/dex2oat_x86_x86_64.profdata",
@@ -296,6 +312,7 @@
],
static_libs: [
"libart-dex2oat",
+ "libelffile",
],
lto: {
thin: true,
@@ -355,6 +372,7 @@
],
static_libs: [
"libartd-dex2oat",
+ "libelffiled",
],
},
host: {
@@ -474,6 +492,7 @@
":art-gtest-jars-ManyMethods",
":art-gtest-jars-MultiDex",
":art-gtest-jars-MultiDexModifiedSecondary",
+ ":art-gtest-jars-MultiDexUncompressedAligned",
":art-gtest-jars-MyClassNatives",
":art-gtest-jars-Nested",
":art-gtest-jars-ProfileTestMultiDex",
@@ -495,6 +514,7 @@
"dex2oat_test.cc",
"dex2oat_vdex_test.cc",
"dex2oat_image_test.cc",
+ "driver/compiled_method_storage_test.cc",
"driver/compiler_driver_test.cc",
"linker/code_info_table_deduper_test.cc",
"linker/elf_writer_test.cc",
@@ -504,6 +524,7 @@
"linker/multi_oat_relative_patcher_test.cc",
"linker/oat_writer_test.cc",
"verifier_deps_test.cc",
+ "utils/swap_space_test.cc",
],
target: {
host: {
@@ -534,6 +555,9 @@
},
},
+ static_libs: [
+ "libziparchive",
+ ],
shared_libs: [
"libartpalette",
"libbase",
@@ -541,7 +565,7 @@
"liblz4", // libart(d)-dex2oat dependency; must be repeated here since it's a static lib.
"liblog",
"libsigchain",
- "libziparchive",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
],
}
@@ -555,13 +579,15 @@
],
shared_libs: [
"libartbased",
- "libartd-compiler",
"libartd-dexlayout",
+ "liblzma",
"libprofiled",
],
static_libs: [
- "libartd-dex2oat-gtest",
+ "libartd-compiler",
"libartd-dex2oat",
+ "libartd-dex2oat-gtest",
+ "libelffiled",
"libvixld",
],
}
@@ -575,14 +601,16 @@
],
data: [":generate-boot-image"],
shared_libs: [
- "libartbase",
- "libart-compiler",
"libart-dexlayout",
+ "libartbase",
+ "liblzma",
"libprofile",
],
static_libs: [
- "libart-dex2oat-gtest",
+ "libart-compiler",
"libart-dex2oat",
+ "libart-dex2oat-gtest",
+ "libelffile",
"libvixl",
],
test_config: "art_standalone_dex2oat_tests.xml",
@@ -598,9 +626,16 @@
":art-gtest-jars-MainStripped",
":art-gtest-jars-MultiDex",
":art-gtest-jars-MultiDexModifiedSecondary",
+ ":art-gtest-jars-MultiDexUncompressedAligned",
":art-gtest-jars-Nested",
":generate-boot-image",
],
+ shared_libs: [
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
+ ],
+ static_libs: [
+ "libziparchive",
+ ],
test_config: "art_standalone_dex2oat_cts_tests.xml",
test_suites: ["cts"],
}
diff --git a/dex2oat/art_standalone_dex2oat_cts_tests.xml b/dex2oat/art_standalone_dex2oat_cts_tests.xml
index 980b295..cf20994 100644
--- a/dex2oat/art_standalone_dex2oat_cts_tests.xml
+++ b/dex2oat/art_standalone_dex2oat_cts_tests.xml
@@ -32,6 +32,7 @@
<option name="push" value="art-gtest-jars-MainStripped.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MainStripped.jar" />
<option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDex.jar" />
<option name="push" value="art-gtest-jars-MultiDexModifiedSecondary.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDexModifiedSecondary.jar" />
+ <option name="push" value="art-gtest-jars-MultiDexUncompressedAligned.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-MultiDexUncompressedAligned.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_dex2oat_cts_tests/art-gtest-jars-Nested.jar" />
</target_preparer>
diff --git a/dex2oat/art_standalone_dex2oat_tests.xml b/dex2oat/art_standalone_dex2oat_tests.xml
index 8bd0fb1..3a8c7b9 100644
--- a/dex2oat/art_standalone_dex2oat_tests.xml
+++ b/dex2oat/art_standalone_dex2oat_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_dex2oat_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_dex2oat_tests->/data/local/tmp/art_standalone_dex2oat_tests/art_standalone_dex2oat_tests" />
@@ -39,6 +41,7 @@
<option name="push" value="art-gtest-jars-ManyMethods.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ManyMethods.jar" />
<option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDex.jar" />
<option name="push" value="art-gtest-jars-MultiDexModifiedSecondary.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDexModifiedSecondary.jar" />
+ <option name="push" value="art-gtest-jars-MultiDexUncompressedAligned.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MultiDexUncompressedAligned.jar" />
<option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-MyClassNatives.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Nested.jar" />
<option name="push" value="art-gtest-jars-ProfileTestMultiDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ProfileTestMultiDex.jar" />
@@ -64,6 +67,9 @@
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-timeout" value="5m" />
+ <!-- Set this to the same as native-test-timeout to effectively disable per test case timeout. -->
+ <option name="test-case-timeout" value="5m" />
<option name="native-test-device-path" value="/data/local/tmp/art_standalone_dex2oat_tests" />
<option name="module-name" value="art_standalone_dex2oat_tests" />
<option name="ld-library-path-32" value="/apex/com.android.art/lib" />
diff --git a/dex2oat/common_compiler_driver_test.cc b/dex2oat/common_compiler_driver_test.cc
index 7e71976..7a1d11c 100644
--- a/dex2oat/common_compiler_driver_test.cc
+++ b/dex2oat/common_compiler_driver_test.cc
@@ -21,6 +21,7 @@
#include "base/casts.h"
#include "base/timing_logger.h"
#include "dex/quick_compiler_callbacks.h"
+#include "dex/verification_results.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "utils/atomic_dex_ref_map-inl.h"
@@ -46,9 +47,7 @@
// Verification results in the `callback_` should not be used during compilation.
down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
reinterpret_cast<VerificationResults*>(inaccessible_page_));
- compiler_options_->verification_results_ = verification_results_.get();
compiler_driver_->CompileAll(class_loader, dex_files, timings);
- compiler_options_->verification_results_ = nullptr;
down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
verification_results_.get());
@@ -89,6 +88,7 @@
compiler_options_->image_classes_.swap(*GetImageClasses());
compiler_options_->profile_compilation_info_ = GetProfileCompilationInfo();
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
compiler_kind_,
number_of_threads_,
/* swap_fd= */ -1));
@@ -97,6 +97,7 @@
void CommonCompilerDriverTest::SetUpRuntimeOptions(RuntimeOptions* options) {
CommonCompilerTest::SetUpRuntimeOptions(options);
+ verification_results_.reset(new VerificationResults());
QuickCompilerCallbacks* callbacks =
new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp);
callbacks->SetVerificationResults(verification_results_.get());
@@ -121,6 +122,7 @@
}
image_reservation_.Reset();
compiler_driver_.reset();
+ verification_results_.reset();
CommonCompilerTest::TearDown();
}
diff --git a/dex2oat/common_compiler_driver_test.h b/dex2oat/common_compiler_driver_test.h
index 1ff88e5..b49d18f 100644
--- a/dex2oat/common_compiler_driver_test.h
+++ b/dex2oat/common_compiler_driver_test.h
@@ -31,6 +31,7 @@
class DexFile;
class ProfileCompilationInfo;
class TimingLogger;
+class VerificationResults;
class CommonCompilerDriverTest : public CommonCompilerTest {
public:
@@ -62,6 +63,7 @@
size_t number_of_threads_ = 2u;
+ std::unique_ptr<VerificationResults> verification_results_;
std::unique_ptr<CompilerDriver> compiler_driver_;
private:
diff --git a/compiler/dex/verification_results.cc b/dex2oat/dex/verification_results.cc
similarity index 100%
rename from compiler/dex/verification_results.cc
rename to dex2oat/dex/verification_results.cc
diff --git a/dex2oat/dex/verification_results.h b/dex2oat/dex/verification_results.h
new file mode 100644
index 0000000..0a85e43
--- /dev/null
+++ b/dex2oat/dex/verification_results.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_
+#define ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "dex/class_reference.h"
+#include "dex/method_reference.h"
+
+namespace art {
+
+namespace verifier {
+class VerifierDepsTest;
+} // namespace verifier
+
+// Used by CompilerCallbacks to track verification information from the Runtime.
+class VerificationResults {
+ public:
+ VerificationResults();
+ ~VerificationResults();
+
+ void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+ bool IsClassRejected(ClassReference ref) const REQUIRES(!rejected_classes_lock_);
+
+ void AddUncompilableClass(ClassReference ref) REQUIRES(!uncompilable_methods_lock_);
+ void AddUncompilableMethod(MethodReference ref) REQUIRES(!uncompilable_methods_lock_);
+ bool IsUncompilableMethod(MethodReference ref) const REQUIRES(!uncompilable_methods_lock_);
+
+ private:
+ // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+ mutable ReaderWriterMutex uncompilable_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::set<MethodReference> uncompilable_methods_ GUARDED_BY(uncompilable_methods_lock_);
+
+ // Rejected classes.
+ // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+ mutable ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+
+ friend class verifier::VerifierDepsTest;
+};
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 65b57b3..f205565 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -66,6 +66,7 @@
#include "base/zip_archive.h"
#include "class_linker.h"
#include "class_loader_context.h"
+#include "class_root-inl.h"
#include "cmdline_parser.h"
#include "compiler.h"
#include "compiler_callbacks.h"
@@ -109,7 +110,6 @@
#include "stream/file_output_stream.h"
#include "vdex_file.h"
#include "verifier/verifier_deps.h"
-#include "well_known_classes.h"
namespace art {
@@ -845,9 +845,7 @@
// Set the compilation target's implicit checks options.
switch (compiler_options_->GetInstructionSet()) {
case InstructionSet::kArm64:
- // TODO: Implicit suspend checks are currently disabled to facilitate search
- // for unrelated memory use regressions. Bug: 213757852.
- compiler_options_->implicit_suspend_checks_ = false;
+ compiler_options_->implicit_suspend_checks_ = true;
FALLTHROUGH_INTENDED;
case InstructionSet::kArm:
case InstructionSet::kThumb2:
@@ -1206,7 +1204,7 @@
if (!parser_options->boot_image_filename.empty()) {
Usage("Option --boot-image and --force-jit-zygote cannot be specified together");
}
- parser_options->boot_image_filename = "boot.art:/nonx/boot-framework.art";
+ parser_options->boot_image_filename = GetJitZygoteBootImageLocation();
}
// If we have a profile, change the default compiler filter to speed-profile
@@ -1834,7 +1832,7 @@
}
// Set up and create the compiler driver and then invoke it to compile all the dex files.
- jobject Compile() {
+ jobject Compile() REQUIRES(!Locks::mutator_lock_) {
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
@@ -1898,6 +1896,7 @@
compiler_options_->profile_compilation_info_ = profile_compilation_info_.get();
driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
compiler_kind_,
thread_count_,
swap_fd_));
@@ -1983,7 +1982,6 @@
timings_,
&compiler_options_->image_classes_);
callbacks_->SetVerificationResults(nullptr); // Should not be needed anymore.
- compiler_options_->verification_results_ = verification_results_.get();
driver_->CompileAll(class_loader, dex_files, timings_);
driver_->FreeThreadPools();
return class_loader;
@@ -2557,16 +2555,16 @@
}
bool PreparePreloadedClasses() {
- preloaded_classes_ = std::make_unique<HashSet<std::string>>();
if (!preloaded_classes_fds_.empty()) {
for (int fd : preloaded_classes_fds_) {
- if (!ReadCommentedInputFromFd(fd, nullptr, preloaded_classes_.get())) {
+ if (!ReadCommentedInputFromFd(fd, nullptr, &compiler_options_->preloaded_classes_)) {
return false;
}
}
} else {
for (const std::string& file : preloaded_classes_files_) {
- if (!ReadCommentedInputFromFile(file.c_str(), nullptr, preloaded_classes_.get())) {
+ if (!ReadCommentedInputFromFile(
+ file.c_str(), nullptr, &compiler_options_->preloaded_classes_)) {
return false;
}
}
@@ -2652,6 +2650,7 @@
bool do_oat_writer_layout = DoDexLayoutOptimizations() || DoOatLayoutOptimizations();
oat_writers_.emplace_back(new linker::OatWriter(
*compiler_options_,
+ verification_results_.get(),
timings_,
do_oat_writer_layout ? profile_compilation_info_.get() : nullptr,
compact_dex_level_));
@@ -2750,19 +2749,14 @@
interpreter::UnstartedRuntime::Initialize();
Thread* self = Thread::Current();
+ runtime_->GetClassLinker()->RunEarlyRootClinits(self);
+ InitializeIntrinsics();
runtime_->RunRootClinits(self);
// Runtime::Create acquired the mutator_lock_ that is normally given away when we
// Runtime::Start, give it away now so that we don't starve GC.
self->TransitionFromRunnableToSuspended(ThreadState::kNative);
- // Now that we are in native state, initialize well known classes and
- // intrinsics if we don't have a boot image.
- WellKnownClasses::Init(self->GetJniEnv());
- if (IsBootImage() || runtime_->GetHeap()->GetBootImageSpaces().empty()) {
- InitializeIntrinsics();
- }
-
WatchDog::SetRuntime(runtime_.get());
return true;
@@ -2961,7 +2955,6 @@
const char* dirty_image_objects_filename_;
int dirty_image_objects_fd_;
std::unique_ptr<HashSet<std::string>> dirty_image_objects_;
- std::unique_ptr<HashSet<std::string>> preloaded_classes_;
std::unique_ptr<std::vector<std::string>> passes_to_run_;
bool is_host_;
std::string android_root_;
@@ -3073,7 +3066,8 @@
jobject obj_;
};
-static dex2oat::ReturnCode DoCompilation(Dex2Oat& dex2oat) {
+static dex2oat::ReturnCode DoCompilation(Dex2Oat& dex2oat) REQUIRES(!Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current());
dex2oat.LoadClassProfileDescriptors();
jobject class_loader = dex2oat.Compile();
// Keep the class loader that was used for compilation live for the rest of the compilation
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 2978fb4..4518905 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -431,6 +431,7 @@
relocate,
/*executable=*/ true,
/*extra_reservation_size=*/ 0u,
+ /*allow_in_memory_compilation=*/ true,
&boot_image_spaces,
&extra_reservation);
};
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 1f9138e..cf60008 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -55,6 +55,7 @@
using Builder = Parser::Builder;
static void AddInputMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--dex-file=_")
.WithType<std::vector<std::string>>().AppendValues()
@@ -102,9 +103,11 @@
" class path components' paths)\n"
"Default: $ANDROID_ROOT/system/framework/boot.art")
.IntoKey(M::BootImage);
+ // clang-format on
}
static void AddGeneratedArtifactMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--input-vdex-fd=_")
.WithType<int>()
@@ -156,9 +159,11 @@
"specified by --oat-fd.\n"
"Eg: --oat-location=/data/dalvik-cache/system@app@Calculator.apk.oat")
.IntoKey(M::OatLocation);
+ // clang-format on
}
static void AddImageMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--image=_")
.WithType<std::string>()
@@ -214,9 +219,11 @@
.WithHelp("Which format to store the image Defaults to uncompressed. Eg:"
" --image-format=lz4")
.IntoKey(M::ImageFormat);
+ // clang-format on
}
static void AddSwapMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--swap-file=_")
.WithType<std::string>()
@@ -234,9 +241,11 @@
.WithType<unsigned int>()
.WithHelp("specifies the minimum number of dex file to allow the use of swap.")
.IntoKey(M::SwapDexCountThreshold);
+ // clang-format on
}
static void AddCompilerMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--run-passes=_")
.WithType<std::string>()
@@ -264,9 +273,11 @@
.WithType<std::vector<int>>().AppendValues()
.WithHelp("Specify files containing list of classes preloaded in the zygote.")
.IntoKey(M::PreloadedClassesFds);
+ // clang-format on
}
static void AddTargetMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--instruction-set=_")
.WithType<InstructionSet>()
@@ -288,6 +299,7 @@
"Example: --instruction-set-features=div\n"
"Default: default")
.IntoKey(M::TargetInstructionSetFeatures);
+ // clang-format on
}
Parser CreateDex2oatArgumentParser() {
@@ -300,6 +312,7 @@
AddCompilerMappings(*parser_builder);
AddTargetMappings(*parser_builder);
+ // clang-format off
parser_builder->
Define({"--watch-dog", "--no-watch-dog"})
.WithHelp("Enable or disable the watchdog timer.")
@@ -448,7 +461,9 @@
.IntoKey(M::ForceJitZygote)
.Define("--force-palette-compilation-hooks")
.WithHelp("Force PaletteNotify{Start,End}Dex2oatCompilation calls.")
- .IntoKey(M::ForcePaletteCompilationHooks);
+ .IntoKey(M::ForcePaletteCompilationHooks)
+ .Ignore({"--comments=_"});
+ // clang-format on
AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 9ee532a..0099ba3 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -74,6 +74,7 @@
bool use_fd = false) {
std::unique_ptr<File> oat_file;
std::vector<std::string> args;
+ args.reserve(dex_locations.size() + extra_args.size() + 6);
// Add dex file args.
for (const std::string& dex_location : dex_locations) {
args.push_back("--dex-file=" + dex_location);
diff --git a/dex2oat/dex2oat_vdex_test.cc b/dex2oat/dex2oat_vdex_test.cc
index 895e9c2..df76d58 100644
--- a/dex2oat/dex2oat_vdex_test.cc
+++ b/dex2oat/dex2oat_vdex_test.cc
@@ -19,10 +19,8 @@
#include "common_runtime_test.h"
#include "dex2oat_environment_test.h"
-
#include "vdex_file.h"
#include "verifier/verifier_deps.h"
-#include "ziparchive/zip_writer.h"
namespace art {
@@ -113,23 +111,6 @@
return deps->GetVerifiedClasses(dex_file)[class_def_idx];
}
- void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) {
- // Read the vdex bytes.
- std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str()));
- std::vector<uint8_t> data(vdex_file->GetLength());
- ASSERT_TRUE(vdex_file->ReadFully(data.data(), data.size()));
-
- // Zip the content.
- FILE* file = fopen(out_dm.c_str(), "wb");
- ZipWriter writer(file);
- writer.StartEntry("primary.vdex", ZipWriter::kAlign32);
- writer.WriteBytes(data.data(), data.size());
- writer.FinishEntry();
- writer.Finish();
- fflush(file);
- fclose(file);
- }
-
std::string GetFilename(const std::unique_ptr<const DexFile>& dex_file) {
const std::string& str = dex_file->GetLocation();
size_t idx = str.rfind('/');
diff --git a/dex2oat/driver/compiled_method-inl.h b/dex2oat/driver/compiled_method-inl.h
new file mode 100644
index 0000000..77ac85c
--- /dev/null
+++ b/dex2oat/driver/compiled_method-inl.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_
+#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_
+
+#include "compiled_method.h"
+
+#include "base/array_ref.h"
+#include "base/length_prefixed_array.h"
+#include "linker/linker_patch.h"
+
+namespace art {
+
+inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const {
+ return GetArray(quick_code_);
+}
+
+template <typename T>
+inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) {
+ if (array == nullptr) {
+ return ArrayRef<const T>();
+ }
+ DCHECK_NE(array->size(), 0u);
+ return ArrayRef<const T>(&array->At(0), array->size());
+}
+
+inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const {
+ return GetArray(vmap_table_);
+}
+
+inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const {
+ return GetArray(cfi_info_);
+}
+
+inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const {
+ return GetArray(patches_);
+}
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_
diff --git a/dex2oat/driver/compiled_method.cc b/dex2oat/driver/compiled_method.cc
new file mode 100644
index 0000000..0a0a005
--- /dev/null
+++ b/dex2oat/driver/compiled_method.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiled_method.h"
+
+#include "driver/compiled_method_storage.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+CompiledCode::CompiledCode(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code)
+ : storage_(storage),
+ quick_code_(storage->DeduplicateCode(quick_code)),
+ packed_fields_(InstructionSetField::Encode(instruction_set)) {
+}
+
+CompiledCode::~CompiledCode() {
+ GetStorage()->ReleaseCode(quick_code_);
+}
+
+bool CompiledCode::operator==(const CompiledCode& rhs) const {
+ if (quick_code_ != nullptr) {
+ if (rhs.quick_code_ == nullptr) {
+ return false;
+ } else if (quick_code_->size() != rhs.quick_code_->size()) {
+ return false;
+ } else {
+ return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin());
+ }
+ }
+ return (rhs.quick_code_ == nullptr);
+}
+
+size_t CompiledCode::AlignCode(size_t offset) const {
+ return AlignCode(offset, GetInstructionSet());
+}
+
+size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) {
+ return RoundUp(offset, GetInstructionSetCodeAlignment(instruction_set));
+}
+
+size_t CompiledCode::GetEntryPointAdjustment() const {
+ return GetInstructionSetEntryPointAdjustment(GetInstructionSet());
+}
+
+CompiledMethod::CompiledMethod(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches)
+ : CompiledCode(storage, instruction_set, quick_code),
+ vmap_table_(storage->DeduplicateVMapTable(vmap_table)),
+ cfi_info_(storage->DeduplicateCFIInfo(cfi_info)),
+ patches_(storage->DeduplicateLinkerPatches(patches)) {
+}
+
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
+ CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches) {
+ SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
+ CompiledMethod* ret = alloc.allocate(1);
+ alloc.construct(ret,
+ storage,
+ instruction_set,
+ quick_code,
+ vmap_table,
+ cfi_info, patches);
+ return ret;
+}
+
+void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage,
+ CompiledMethod* m) {
+ SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
+ alloc.destroy(m);
+ alloc.deallocate(m, 1);
+}
+
+CompiledMethod::~CompiledMethod() {
+ CompiledMethodStorage* storage = GetStorage();
+ storage->ReleaseLinkerPatches(patches_);
+ storage->ReleaseCFIInfo(cfi_info_);
+ storage->ReleaseVMapTable(vmap_table_);
+}
+
+} // namespace art
diff --git a/dex2oat/driver/compiled_method.h b/dex2oat/driver/compiled_method.h
new file mode 100644
index 0000000..a92c757
--- /dev/null
+++ b/dex2oat/driver/compiled_method.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_
+#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "base/bit_field.h"
+#include "base/bit_utils.h"
+
+namespace art {
+
+template <typename T> class ArrayRef;
+class CompiledMethodStorage;
+template<typename T> class LengthPrefixedArray;
+
+namespace linker {
+class LinkerPatch;
+} // namespace linker
+
+class CompiledCode {
+ public:
+ // For Quick to supply an code blob
+ CompiledCode(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code);
+
+ virtual ~CompiledCode();
+
+ InstructionSet GetInstructionSet() const {
+ return GetPackedField<InstructionSetField>();
+ }
+
+ ArrayRef<const uint8_t> GetQuickCode() const;
+
+ bool operator==(const CompiledCode& rhs) const;
+
+ // To align an offset from a page-aligned value to make it suitable
+ // for code storage. For example on ARM, to ensure that PC relative
+ // valu computations work out as expected.
+ size_t AlignCode(size_t offset) const;
+ static size_t AlignCode(size_t offset, InstructionSet instruction_set);
+
+ // Returns the difference between the code address and a usable PC.
+ // Mainly to cope with `kThumb2` where the lower bit must be set.
+ size_t GetEntryPointAdjustment() const;
+
+ protected:
+ static constexpr size_t kInstructionSetFieldSize =
+ MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast));
+ static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize;
+ static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
+
+ template <typename T>
+ static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array);
+
+ CompiledMethodStorage* GetStorage() {
+ return storage_;
+ }
+
+ template <typename BitFieldType>
+ typename BitFieldType::value_type GetPackedField() const {
+ return BitFieldType::Decode(packed_fields_);
+ }
+
+ template <typename BitFieldType>
+ void SetPackedField(typename BitFieldType::value_type value) {
+ DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
+ packed_fields_ = BitFieldType::Update(value, packed_fields_);
+ }
+
+ private:
+ using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>;
+
+ CompiledMethodStorage* const storage_;
+
+ // Used to store the compiled code.
+ const LengthPrefixedArray<uint8_t>* const quick_code_;
+
+ uint32_t packed_fields_;
+};
+
+class CompiledMethod final : public CompiledCode {
+ public:
+ // Constructs a CompiledMethod.
+ // Note: Consider using the static allocation methods below that will allocate the CompiledMethod
+ // in the swap space.
+ CompiledMethod(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches);
+
+ virtual ~CompiledMethod();
+
+ static CompiledMethod* SwapAllocCompiledMethod(
+ CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches);
+
+ static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m);
+
+ bool IsIntrinsic() const {
+ return GetPackedField<IsIntrinsicField>();
+ }
+
+ // Marks the compiled method as being generated using an intrinsic codegen.
+ // Such methods have no relationships to their code items.
+ // This affects debug information generated at link time.
+ void MarkAsIntrinsic() {
+ DCHECK(!IsIntrinsic());
+ SetPackedField<IsIntrinsicField>(/* value= */ true);
+ }
+
+ ArrayRef<const uint8_t> GetVmapTable() const;
+
+ ArrayRef<const uint8_t> GetCFIInfo() const;
+
+ ArrayRef<const linker::LinkerPatch> GetPatches() const;
+
+ private:
+ static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits;
+ static constexpr size_t kIsIntrinsicSize = 1u;
+ static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize;
+ static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
+ using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>;
+
+ // For quick code, holds code infos which contain stack maps, inline information, and etc.
+ const LengthPrefixedArray<uint8_t>* const vmap_table_;
+ // For quick code, a FDE entry for the debug_frame section.
+ const LengthPrefixedArray<uint8_t>* const cfi_info_;
+ // For quick code, linker patches needed by the method.
+ const LengthPrefixedArray<linker::LinkerPatch>* const patches_;
+};
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_
diff --git a/dex2oat/driver/compiled_method_storage.cc b/dex2oat/driver/compiled_method_storage.cc
new file mode 100644
index 0000000..0e46f4e
--- /dev/null
+++ b/dex2oat/driver/compiled_method_storage.cc
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <ostream>
+
+#include "compiled_method_storage.h"
+
+#include <android-base/logging.h>
+
+#include "base/data_hash.h"
+#include "base/utils.h"
+#include "compiled_method.h"
+#include "linker/linker_patch.h"
+#include "thread-current-inl.h"
+#include "utils/dedupe_set-inl.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+namespace { // anonymous namespace
+
+template <typename T>
+const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) {
+ DCHECK(!array.empty());
+ SwapAllocator<uint8_t> allocator(swap_space);
+ void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size()));
+ LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size());
+ std::copy(array.begin(), array.end(), array_copy->begin());
+ return array_copy;
+}
+
+template <typename T>
+void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) {
+ SwapAllocator<uint8_t> allocator(swap_space);
+ size_t size = LengthPrefixedArray<T>::ComputeSize(array->size());
+ array->~LengthPrefixedArray<T>();
+ allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size);
+}
+
+} // anonymous namespace
+
+template <typename T, typename DedupeSetType>
+inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray(
+ const ArrayRef<const T>& data,
+ DedupeSetType* dedupe_set) {
+ if (data.empty()) {
+ return nullptr;
+ } else if (!DedupeEnabled()) {
+ return CopyArray(swap_space_.get(), data);
+ } else {
+ return dedupe_set->Add(Thread::Current(), data);
+ }
+}
+
+template <typename T>
+inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
+ const LengthPrefixedArray<T>* array) {
+ if (array != nullptr && !DedupeEnabled()) {
+ ReleaseArray(swap_space_.get(), array);
+ }
+}
+
+template <typename ContentType>
+class CompiledMethodStorage::DedupeHashFunc {
+ private:
+ static constexpr bool kUseMurmur3Hash = true;
+
+ public:
+ size_t operator()(const ArrayRef<ContentType>& array) const {
+ return DataHash()(array);
+ }
+};
+
+template <typename T>
+class CompiledMethodStorage::LengthPrefixedArrayAlloc {
+ public:
+ explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
+ : swap_space_(swap_space) {
+ }
+
+ const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
+ return CopyArray(swap_space_, array);
+ }
+
+ void Destroy(const LengthPrefixedArray<T>* array) {
+ ReleaseArray(swap_space_, array);
+ }
+
+ private:
+ SwapSpace* const swap_space_;
+};
+
+class CompiledMethodStorage::ThunkMapKey {
+ public:
+ ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2)
+ : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {}
+
+ bool operator<(const ThunkMapKey& other) const {
+ if (custom_value1_ != other.custom_value1_) {
+ return custom_value1_ < other.custom_value1_;
+ }
+ if (custom_value2_ != other.custom_value2_) {
+ return custom_value2_ < other.custom_value2_;
+ }
+ return type_ < other.type_;
+ }
+
+ private:
+ linker::LinkerPatch::Type type_;
+ uint32_t custom_value1_;
+ uint32_t custom_value2_;
+};
+
+class CompiledMethodStorage::ThunkMapValue {
+ public:
+ ThunkMapValue(std::vector<uint8_t, SwapAllocator<uint8_t>>&& code,
+ const std::string& debug_name)
+ : code_(std::move(code)), debug_name_(debug_name) {}
+
+ ArrayRef<const uint8_t> GetCode() const {
+ return ArrayRef<const uint8_t>(code_);
+ }
+
+ const std::string& GetDebugName() const {
+ return debug_name_;
+ }
+
+ private:
+ std::vector<uint8_t, SwapAllocator<uint8_t>> code_;
+ std::string debug_name_;
+};
+
+CompiledMethodStorage::CompiledMethodStorage(int swap_fd)
+ : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
+ dedupe_enabled_(true),
+ dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_vmap_table_("dedupe vmap table",
+ LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_linker_patches_("dedupe cfi info",
+ LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())),
+ thunk_map_lock_("thunk_map_lock"),
+ thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) {
+}
+
+CompiledMethodStorage::~CompiledMethodStorage() {
+ // All done by member destructors.
+}
+
+void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
+ if (swap_space_.get() != nullptr) {
+ const size_t swap_size = swap_space_->GetSize();
+ os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
+ }
+ if (extended) {
+ Thread* self = Thread::Current();
+ os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
+ os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
+ os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
+ }
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
+ const ArrayRef<const uint8_t>& code) {
+ return AllocateOrDeduplicateArray(code, &dedupe_code_);
+}
+
+void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
+ ReleaseArrayIfNotDeduplicated(code);
+}
+
+size_t CompiledMethodStorage::UniqueCodeEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_code_.Size(Thread::Current());
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
+ const ArrayRef<const uint8_t>& table) {
+ return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
+}
+
+void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
+ ReleaseArrayIfNotDeduplicated(table);
+}
+
+size_t CompiledMethodStorage::UniqueVMapTableEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_vmap_table_.Size(Thread::Current());
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
+ const ArrayRef<const uint8_t>& cfi_info) {
+ return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
+}
+
+void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
+ ReleaseArrayIfNotDeduplicated(cfi_info);
+}
+
+size_t CompiledMethodStorage::UniqueCFIInfoEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_cfi_info_.Size(Thread::Current());
+}
+
+const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
+ const ArrayRef<const linker::LinkerPatch>& linker_patches) {
+ return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
+}
+
+void CompiledMethodStorage::ReleaseLinkerPatches(
+ const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
+ ReleaseArrayIfNotDeduplicated(linker_patches);
+}
+
+size_t CompiledMethodStorage::UniqueLinkerPatchesEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_linker_patches_.Size(Thread::Current());
+}
+
+CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
+ const linker::LinkerPatch& linker_patch) {
+ uint32_t custom_value1 = 0u;
+ uint32_t custom_value2 = 0u;
+ switch (linker_patch.GetType()) {
+ case linker::LinkerPatch::Type::kCallEntrypoint:
+ custom_value1 = linker_patch.EntrypointOffset();
+ break;
+ case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
+ custom_value1 = linker_patch.GetBakerCustomValue1();
+ custom_value2 = linker_patch.GetBakerCustomValue2();
+ break;
+ case linker::LinkerPatch::Type::kCallRelative:
+ // No custom values.
+ break;
+ default:
+ LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
+ UNREACHABLE();
+ }
+ return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
+}
+
+CompiledMethod* CompiledMethodStorage::CreateCompiledMethod(
+ InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic) {
+ CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
+ this, instruction_set, code, stack_map, cfi, patches);
+ if (is_intrinsic) {
+ compiled_method->MarkAsIntrinsic();
+ }
+ return compiled_method;
+}
+
+ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
+ /*out*/ std::string* debug_name) {
+ ThunkMapKey key = GetThunkMapKey(linker_patch);
+ MutexLock lock(Thread::Current(), thunk_map_lock_);
+ auto it = thunk_map_.find(key);
+ if (it != thunk_map_.end()) {
+ const ThunkMapValue& value = it->second;
+ if (debug_name != nullptr) {
+ *debug_name = value.GetDebugName();
+ }
+ return value.GetCode();
+ } else {
+ if (debug_name != nullptr) {
+ *debug_name = std::string();
+ }
+ return ArrayRef<const uint8_t>();
+ }
+}
+
+void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
+ ArrayRef<const uint8_t> code,
+ const std::string& debug_name) {
+ DCHECK(!code.empty());
+ ThunkMapKey key = GetThunkMapKey(linker_patch);
+ std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
+ code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
+ ThunkMapValue value(std::move(code_copy), debug_name);
+ MutexLock lock(Thread::Current(), thunk_map_lock_);
+ // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
+ thunk_map_.emplace(key, std::move(value));
+}
+
+} // namespace art
diff --git a/dex2oat/driver/compiled_method_storage.h b/dex2oat/driver/compiled_method_storage.h
new file mode 100644
index 0000000..3b0304e
--- /dev/null
+++ b/dex2oat/driver/compiled_method_storage.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_
+#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_
+
+#include <iosfwd>
+#include <map>
+#include <memory>
+
+#include "base/array_ref.h"
+#include "base/length_prefixed_array.h"
+#include "base/macros.h"
+#include "driver/compiled_code_storage.h"
+#include "utils/dedupe_set.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+namespace linker {
+class LinkerPatch;
+} // namespace linker
+
+// TODO: Find a better name. This stores both method and non-method (thunks) code.
+class CompiledMethodStorage final : public CompiledCodeStorage {
+ public:
+ explicit CompiledMethodStorage(int swap_fd);
+ ~CompiledMethodStorage();
+
+ void DumpMemoryUsage(std::ostream& os, bool extended) const;
+
+ void SetDedupeEnabled(bool dedupe_enabled) {
+ dedupe_enabled_ = dedupe_enabled;
+ }
+ bool DedupeEnabled() const {
+ return dedupe_enabled_;
+ }
+
+ SwapAllocator<void> GetSwapSpaceAllocator() {
+ return SwapAllocator<void>(swap_space_.get());
+ }
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
+ void ReleaseCode(const LengthPrefixedArray<uint8_t>* code);
+ size_t UniqueCodeEntries() const;
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& table);
+ void ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table);
+ size_t UniqueVMapTableEntries() const;
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
+ void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info);
+ size_t UniqueCFIInfoEntries() const;
+
+ const LengthPrefixedArray<linker::LinkerPatch>* DeduplicateLinkerPatches(
+ const ArrayRef<const linker::LinkerPatch>& linker_patches);
+ void ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch>* linker_patches);
+ size_t UniqueLinkerPatchesEntries() const;
+
+ CompiledMethod* CreateCompiledMethod(InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic) override;
+
+ // Returns the code associated with the given patch.
+ // If the code has not been set, returns empty data.
+ // If `debug_name` is not null, stores the associated debug name in `*debug_name`.
+ ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& linker_patch,
+ /*out*/ std::string* debug_name = nullptr) override;
+
+ // Sets the code and debug name associated with the given patch.
+ void SetThunkCode(const linker::LinkerPatch& linker_patch,
+ ArrayRef<const uint8_t> code,
+ const std::string& debug_name) override;
+
+ private:
+ class ThunkMapKey;
+ class ThunkMapValue;
+ using ThunkMapValueType = std::pair<const ThunkMapKey, ThunkMapValue>;
+ using ThunkMap = std::map<ThunkMapKey,
+ ThunkMapValue,
+ std::less<ThunkMapKey>,
+ SwapAllocator<ThunkMapValueType>>;
+ static_assert(std::is_same<ThunkMapValueType, ThunkMap::value_type>::value, "Value type check.");
+
+ static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch);
+
+ template <typename T, typename DedupeSetType>
+ const LengthPrefixedArray<T>* AllocateOrDeduplicateArray(const ArrayRef<const T>& data,
+ DedupeSetType* dedupe_set);
+
+ template <typename T>
+ void ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T>* array);
+
+ // DeDuplication data structures.
+ template <typename ContentType>
+ class DedupeHashFunc;
+
+ template <typename T>
+ class LengthPrefixedArrayAlloc;
+
+ template <typename T>
+ using ArrayDedupeSet = DedupeSet<ArrayRef<const T>,
+ LengthPrefixedArray<T>,
+ LengthPrefixedArrayAlloc<T>,
+ size_t,
+ DedupeHashFunc<const T>,
+ 4>;
+
+ // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
+ // as other fields rely on this.
+ std::unique_ptr<SwapSpace> swap_space_;
+
+ bool dedupe_enabled_;
+
+ ArrayDedupeSet<uint8_t> dedupe_code_;
+ ArrayDedupeSet<uint8_t> dedupe_vmap_table_;
+ ArrayDedupeSet<uint8_t> dedupe_cfi_info_;
+ ArrayDedupeSet<linker::LinkerPatch> dedupe_linker_patches_;
+
+ Mutex thunk_map_lock_;
+ ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage);
+};
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_
diff --git a/compiler/driver/compiled_method_storage_test.cc b/dex2oat/driver/compiled_method_storage_test.cc
similarity index 100%
rename from compiler/driver/compiled_method_storage_test.cc
rename to dex2oat/driver/compiled_method_storage_test.cc
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index 862062f..4f85140 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -87,6 +87,7 @@
#include "verifier/class_verifier.h"
#include "verifier/verifier_deps.h"
#include "verifier/verifier_enums.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -252,10 +253,12 @@
CompilerDriver::CompilerDriver(
const CompilerOptions* compiler_options,
+ const VerificationResults* verification_results,
Compiler::Kind compiler_kind,
size_t thread_count,
int swap_fd)
: compiler_options_(compiler_options),
+ verification_results_(verification_results),
compiler_(),
compiler_kind_(compiler_kind),
number_of_soft_verifier_failures_(0),
@@ -493,7 +496,7 @@
// Method is annotated with @NeverCompile and should not be compiled.
} else {
const CompilerOptions& compiler_options = driver->GetCompilerOptions();
- const VerificationResults* results = compiler_options.GetVerificationResults();
+ const VerificationResults* results = driver->GetVerificationResults();
DCHECK(results != nullptr);
MethodReference method_ref(&dex_file, method_idx);
// Don't compile class initializers unless kEverything.
@@ -1008,13 +1011,68 @@
HashSet<std::string>* const image_classes_;
};
-// Add classes which contain intrinsics methods to the list of image classes.
-static void AddClassesContainingIntrinsics(/* out */ HashSet<std::string>* image_classes) {
-#define ADD_INTRINSIC_OWNER_CLASS(_, __, ___, ____, _____, ClassName, ______, _______) \
- image_classes->insert(ClassName);
+// Verify that classes which contain intrinsics methods are in the list of image classes.
+static void VerifyClassesContainingIntrinsicsAreImageClasses(HashSet<std::string>* image_classes) {
+#define CHECK_INTRINSIC_OWNER_CLASS(_, __, ___, ____, _____, ClassName, ______, _______) \
+ CHECK(image_classes->find(std::string_view(ClassName)) != image_classes->end());
- INTRINSICS_LIST(ADD_INTRINSIC_OWNER_CLASS)
-#undef ADD_INTRINSIC_OWNER_CLASS
+ INTRINSICS_LIST(CHECK_INTRINSIC_OWNER_CLASS)
+#undef CHECK_INTRINSIC_OWNER_CLASS
+}
+
+// We need to put classes required by app class loaders to the boot image,
+// otherwise we would not be able to store app class loaders in app images.
+static void AddClassLoaderClasses(/* out */ HashSet<std::string>* image_classes) {
+ ScopedObjectAccess soa(Thread::Current());
+ // Well known classes have been loaded and shall be added to image classes
+ // by the `RecordImageClassesVisitor`. However, there are fields with array
+ // types which we need to add to the image classes explicitly.
+ ArtField* class_loader_array_fields[] = {
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders,
+ // BaseDexClassLoader.sharedLibraryLoadersAfter has the same array type as above.
+ WellKnownClasses::dalvik_system_DexPathList_dexElements,
+ };
+ for (ArtField* field : class_loader_array_fields) {
+ const char* field_type_descriptor = field->GetTypeDescriptor();
+ DCHECK_EQ(field_type_descriptor[0], '[');
+ image_classes->insert(field_type_descriptor);
+ }
+}
+
+static void VerifyClassLoaderClassesAreImageClasses(/* out */ HashSet<std::string>* image_classes) {
+ ScopedObjectAccess soa(Thread::Current());
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ ObjPtr<mirror::Class> class_loader_classes[] = {
+ WellKnownClasses::dalvik_system_BaseDexClassLoader.Get(),
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
+ WellKnownClasses::dalvik_system_DexClassLoader.Get(),
+ WellKnownClasses::dalvik_system_DexFile.Get(),
+ WellKnownClasses::dalvik_system_DexPathList.Get(),
+ WellKnownClasses::dalvik_system_DexPathList__Element.Get(),
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get(),
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
+ WellKnownClasses::java_lang_BootClassLoader.Get(),
+ WellKnownClasses::java_lang_ClassLoader.Get(),
+ };
+ for (ObjPtr<mirror::Class> klass : class_loader_classes) {
+ std::string temp;
+ std::string_view descriptor = klass->GetDescriptor(&temp);
+ CHECK(image_classes->find(descriptor) != image_classes->end());
+ }
+ ArtField* class_loader_fields[] = {
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList,
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders,
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter,
+ WellKnownClasses::dalvik_system_DexFile_cookie,
+ WellKnownClasses::dalvik_system_DexFile_fileName,
+ WellKnownClasses::dalvik_system_DexPathList_dexElements,
+ WellKnownClasses::dalvik_system_DexPathList__Element_dexFile,
+ WellKnownClasses::java_lang_ClassLoader_parent,
+ };
+ for (ArtField* field : class_loader_fields) {
+ std::string_view field_type_descriptor = field->GetTypeDescriptor();
+ CHECK(image_classes->find(field_type_descriptor) != image_classes->end());
+ }
}
// Make a list of descriptors for classes to include in the image
@@ -1028,7 +1086,10 @@
TimingLogger::ScopedTiming t("LoadImageClasses", timings);
if (GetCompilerOptions().IsBootImage()) {
- AddClassesContainingIntrinsics(image_classes);
+ // Image classes of intrinsics are loaded and shall be added
+ // to image classes by the `RecordImageClassesVisitor`.
+ // Add classes needed for storing class loaders in app images.
+ AddClassLoaderClasses(image_classes);
}
// Make a first pass to load all classes explicitly listed in the file
@@ -1099,6 +1160,11 @@
RecordImageClassesVisitor visitor(image_classes);
class_linker->VisitClasses(&visitor);
+ if (kIsDebugBuild && GetCompilerOptions().IsBootImage()) {
+ VerifyClassesContainingIntrinsicsAreImageClasses(image_classes);
+ VerifyClassLoaderClassesAreImageClasses(image_classes);
+ }
+
if (GetCompilerOptions().IsBootImage()) {
CHECK(!image_classes->empty());
}
@@ -1219,12 +1285,16 @@
data_->image_class_descriptors_->erase(it);
}
} else if (can_include_in_image) {
- // Check whether it is initialized and has a clinit. They must be kept, too.
- if (klass->IsInitialized() && klass->FindClassInitializer(
- Runtime::Current()->GetClassLinker()->GetImagePointerSize()) != nullptr) {
- DCHECK(!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache()))
- << klass->PrettyDescriptor();
- data_->image_classes_.push_back(data_->hs_.NewHandle(klass));
+ // Check whether the class is initialized and has a clinit or static fields.
+ // Such classes must be kept too.
+ if (klass->IsInitialized()) {
+ PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ if (klass->FindClassInitializer(pointer_size) != nullptr ||
+ klass->NumStaticFields() != 0) {
+ DCHECK(!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass->GetDexCache()))
+ << klass->PrettyDescriptor();
+ data_->image_classes_.push_back(data_->hs_.NewHandle(klass));
+ }
}
}
return true;
@@ -1537,8 +1607,7 @@
mirror::Throwable* exception = soa.Self()->GetException();
DCHECK(exception != nullptr);
VLOG(compiler) << "Exception during type resolution: " << exception->Dump();
- if (exception->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_OutOfMemoryError)) {
+ if (exception->GetClass() == WellKnownClasses::java_lang_OutOfMemoryError.Get()) {
// There's little point continuing compilation if the heap is exhausted.
// Trying to do so would also introduce non-deterministic compilation results.
LOG(FATAL) << "Out of memory during type resolution for compilation";
@@ -1673,6 +1742,9 @@
class_loader,
dex_files,
&error_msg)) {
+ // Clear the information we have as we are going to re-verify and we do not
+ // want to keep that a class is verified.
+ verifier_deps->ClearData(dex_files);
LOG(WARNING) << "Fast verification failed: " << error_msg;
return false;
}
@@ -1699,8 +1771,10 @@
// the type.
ClassReference ref(dex_file, accessor.GetClassDefIndex());
const ClassStatus existing = ClassStatus::kNotReady;
- ClassStateTable::InsertResult result = compiled_classes_.Insert(ref, existing, status);
- CHECK_EQ(result, ClassStateTable::kInsertResultSuccess) << ref.dex_file->GetLocation();
+ // Note: when dex files are compiled inidividually, the class may have
+ // been verified in a previous stage. This means this insertion can
+ // fail, but that's OK.
+ compiled_classes_.Insert(ref, existing, status);
} else {
// Update the class status, so later compilation stages know they don't need to verify
// the class.
@@ -2237,6 +2311,19 @@
// Make sure the class initialization did not leave any local references.
self->GetJniEnv()->AssertLocalsEmpty();
}
+
+ if (!klass->IsVisiblyInitialized() &&
+ (is_boot_image || is_boot_image_extension) &&
+ !compiler_options.IsPreloadedClass(PrettyDescriptor(descriptor).c_str())) {
+ klass->SetInBootImageAndNotInPreloadedClasses();
+ }
+
+ if (compiler_options.CompileArtTest()) {
+ // For stress testing and unit-testing the clinit check in compiled code feature.
+ if (kIsDebugBuild || EndsWith(std::string_view(descriptor), "$NoPreloadHolder;")) {
+ klass->SetInBootImageAndNotInPreloadedClasses();
+ }
+ }
}
private:
@@ -2498,7 +2585,8 @@
ClassAccessor accessor(dex_file, class_def_index);
CompilerDriver* const driver = context.GetCompiler();
// Skip compiling classes with generic verifier failures since they will still fail at runtime
- if (driver->GetCompilerOptions().GetVerificationResults()->IsClassRejected(ref)) {
+ DCHECK(driver->GetVerificationResults() != nullptr);
+ if (driver->GetVerificationResults()->IsClassRejected(ref)) {
return;
}
// Use a scoped object access to perform to the quick SkipClass check.
diff --git a/dex2oat/driver/compiler_driver.h b/dex2oat/driver/compiler_driver.h
index ed8fc2f..7985771 100644
--- a/dex2oat/driver/compiler_driver.h
+++ b/dex2oat/driver/compiler_driver.h
@@ -85,6 +85,7 @@
// can assume will be in the image, with null implying all available
// classes.
CompilerDriver(const CompilerOptions* compiler_options,
+ const VerificationResults* verification_results,
Compiler::Kind compiler_kind,
size_t thread_count,
int swap_fd);
@@ -115,6 +116,10 @@
return *compiler_options_;
}
+ const VerificationResults* GetVerificationResults() const {
+ return verification_results_;
+ }
+
Compiler* GetCompiler() const {
return compiler_.get();
}
@@ -295,6 +300,7 @@
/*inout*/ TimingLogger* timings);
const CompilerOptions* const compiler_options_;
+ const VerificationResults* const verification_results_;
std::unique_ptr<Compiler> compiler_;
Compiler::Kind compiler_kind_;
diff --git a/dex2oat/driver/compiler_driver_test.cc b/dex2oat/driver/compiler_driver_test.cc
index 65aa888..759426a 100644
--- a/dex2oat/driver/compiler_driver_test.cc
+++ b/dex2oat/driver/compiler_driver_test.cc
@@ -25,6 +25,7 @@
#include "base/casts.h"
#include "class_linker-inl.h"
#include "common_compiler_driver_test.h"
+#include "compiled_method-inl.h"
#include "compiler_callbacks.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
@@ -80,14 +81,19 @@
void MakeExecutable(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK(method != nullptr);
- const CompiledMethod* compiled_method = nullptr;
+ const void* method_code = nullptr;
if (!method->IsAbstract()) {
- const DexFile& dex_file = *method->GetDexFile();
- compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
- method->GetDexMethodIndex()));
+ MethodReference method_ref(method->GetDexFile(), method->GetDexMethodIndex());
+ const CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(method_ref);
+ // If the code size is 0 it means the method was skipped due to profile guided compilation.
+ if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) {
+ method_code = CommonCompilerTest::MakeExecutable(compiled_method->GetQuickCode(),
+ compiled_method->GetVmapTable(),
+ compiled_method->GetInstructionSet());
+ LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
+ }
}
- CommonCompilerTest::MakeExecutable(method, compiled_method);
+ runtime_->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ method_code);
}
void MakeDexFileExecutable(jobject class_loader, const DexFile& dex_file) {
@@ -124,19 +130,15 @@
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
const DexFile& dex = *java_lang_dex_file_;
ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(soa.Self(), dex);
- EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings());
for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
const ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(i));
EXPECT_TRUE(string != nullptr) << "string_idx=" << i;
}
- EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes());
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
const ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(dex::TypeIndex(i));
EXPECT_TRUE(type != nullptr)
<< "type_idx=" << i << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i)));
}
- EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods()
- || dex.NumMethodIds() == dex_cache->NumResolvedMethods());
for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
// FIXME: This is outdated for hash-based method array.
ArtMethod* method = dex_cache->GetResolvedMethod(i);
@@ -147,8 +149,6 @@
<< " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i)) << " "
<< dex.GetMethodName(dex.GetMethodId(i));
}
- EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
- || dex.NumFieldIds() == dex_cache->NumResolvedFields());
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
// FIXME: This is outdated for hash-based field array.
ArtField* field = dex_cache->GetResolvedField(i);
diff --git a/dex2oat/linker/arm/relative_patcher_arm_base.cc b/dex2oat/linker/arm/relative_patcher_arm_base.cc
index 35e799a..1cb72f6 100644
--- a/dex2oat/linker/arm/relative_patcher_arm_base.cc
+++ b/dex2oat/linker/arm/relative_patcher_arm_base.cc
@@ -17,9 +17,9 @@
#include "linker/arm/relative_patcher_arm_base.h"
#include "base/stl_util.h"
-#include "compiled_method-inl.h"
#include "debug/method_debug_info.h"
#include "dex/dex_file_types.h"
+#include "driver/compiled_method-inl.h"
#include "linker/linker_patch.h"
#include "oat.h"
#include "oat_quick_method_header.h"
@@ -462,7 +462,7 @@
}
unreserved_thunks_.insert(unreserved_thunks_.begin() + index, data);
// We may need to update the max next offset(s) if the thunk code would not fit.
- size_t alignment = GetInstructionSetAlignment(instruction_set_);
+ size_t alignment = GetInstructionSetCodeAlignment(instruction_set_);
if (index + 1u != unreserved_thunks_.size()) {
// Note: Ignore the return value as we need to process previous thunks regardless.
data->MakeSpaceBefore(*unreserved_thunks_[index + 1u], alignment);
@@ -501,7 +501,8 @@
if (!result.first) {
break;
}
- uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
+ uint32_t target_offset =
+ result.second - GetInstructionSetEntryPointAdjustment(instruction_set_);
if (target_offset >= patch_offset) {
DCHECK_LE(target_offset - patch_offset, max_positive_displacement);
} else if (patch_offset - target_offset > max_negative_displacement) {
@@ -535,7 +536,7 @@
inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_offset,
const ThunkKey& key) {
return RoundDown(patch_offset + MaxPositiveDisplacement(key),
- GetInstructionSetAlignment(instruction_set_));
+ GetInstructionSetCodeAlignment(instruction_set_));
}
inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch(
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.cc b/dex2oat/linker/arm/relative_patcher_thumb2.cc
index 99728cf..45a4e8b 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2.cc
+++ b/dex2oat/linker/arm/relative_patcher_thumb2.cc
@@ -22,7 +22,7 @@
#include "art_method.h"
#include "base/bit_utils.h"
#include "base/malloc_arena_pool.h"
-#include "compiled_method.h"
+#include "driver/compiled_method.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "linker/linker_patch.h"
#include "lock_word.h"
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
index 296bf61..f7abd6b 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
+++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
@@ -145,7 +145,7 @@
const ArrayRef<const uint8_t>& last_method_code,
const ArrayRef<const LinkerPatch>& last_method_patches,
uint32_t distance_without_thunks) {
- CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
+ CHECK_EQ(distance_without_thunks % kArmCodeAlignment, 0u);
uint32_t method1_offset =
kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
@@ -153,7 +153,7 @@
// We want to put the last method at a very precise offset.
const uint32_t last_method_offset = method1_offset + distance_without_thunks;
- CHECK_ALIGNED(last_method_offset, kArmAlignment);
+ CHECK_ALIGNED(last_method_offset, kArmCodeAlignment);
const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
// Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB).
@@ -562,24 +562,25 @@
bl_offset_in_method1 + just_over_max_positive_disp);
ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
uint32_t method_after_thunk_idx = last_method_idx;
- if (sizeof(OatQuickMethodHeader) < kArmAlignment) {
- // The thunk needs to start on a kArmAlignment-aligned address before the address where the
+ if (sizeof(OatQuickMethodHeader) < kArmCodeAlignment) {
+ // The thunk needs to start on a kArmCodeAlignment-aligned address before the address where the
// last method would have been if there was no thunk. If the size of the OatQuickMethodHeader
- // is at least kArmAlignment, the thunk start shall fit between the previous filler method
+ // is at least kArmCodeAlignment, the thunk start shall fit between the previous filler method
// and that address. Otherwise, it shall be inserted before that filler method.
method_after_thunk_idx -= 1u;
}
uint32_t method1_offset = GetMethodOffset(1u);
uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx);
- ASSERT_TRUE(IsAligned<kArmAlignment>(method_after_thunk_offset));
+ ASSERT_TRUE(IsAligned<kArmCodeAlignment>(method_after_thunk_offset));
uint32_t method_after_thunk_header_offset =
method_after_thunk_offset - sizeof(OatQuickMethodHeader);
uint32_t thunk_size = MethodCallThunkSize();
- uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArmAlignment);
+ uint32_t thunk_offset =
+ RoundDown(method_after_thunk_header_offset - thunk_size, kArmCodeAlignment);
DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
method_after_thunk_header_offset);
- ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
+ ASSERT_TRUE(IsAligned<kArmCodeAlignment>(thunk_offset));
uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
ASSERT_TRUE(IsAligned<2u>(diff));
ASSERT_GE(diff, 16 * MB - (1u << 22)); // Simple encoding, unknown bits fit into imm10:imm11:0.
@@ -725,7 +726,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : kBakerValidRegs) {
for (uint32_t holder_reg : kBakerValidRegs) {
@@ -791,7 +792,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
}
@@ -823,7 +824,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : kBakerValidRegs) {
if (base_reg >= 8u) {
@@ -892,7 +893,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
}
@@ -945,9 +946,10 @@
constexpr uint32_t expected_thunk_offset =
kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
+ static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset),
+ "Target offset must be aligned.");
size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
+ RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -956,7 +958,7 @@
AddCompiledMethod(MethodRef(3u), kNopCode);
constexpr uint32_t kLiteralOffset2 = 4;
- static_assert(IsAligned<kArmAlignment>(kLiteralOffset2 + kPcAdjustment),
+ static_assert(IsAligned<kArmCodeAlignment>(kLiteralOffset2 + kPcAdjustment),
"PC for BNE must be aligned.");
// Allow reaching the thunk from the very beginning of a method almost 1MiB away. Backward branch
@@ -968,8 +970,8 @@
CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
size_t filler2_size =
1 * MB - (kLiteralOffset2 + kPcAdjustment)
- - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
+ - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1013,16 +1015,18 @@
constexpr uint32_t expected_thunk_offset =
kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement + 2 */ (1u << 20);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
+ static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset),
+ "Target offset must be aligned.");
size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
+ RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
Link();
- const uint32_t bne = BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmAlignment));
+ const uint32_t bne =
+ BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmCodeAlignment));
const std::vector<uint8_t> expected_code1 = RawCode({kNopWInsn, bne, kLdrWInsn, kNopInsn});
ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
}
@@ -1043,9 +1047,10 @@
constexpr uint32_t expected_thunk_offset =
kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
+ static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset),
+ "Target offset must be aligned.");
size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
+ RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -1055,7 +1060,7 @@
constexpr uint32_t kReachableFromOffset2 = 4;
constexpr uint32_t kLiteralOffset2 = kReachableFromOffset2 + 2;
- static_assert(IsAligned<kArmAlignment>(kReachableFromOffset2 + kPcAdjustment),
+ static_assert(IsAligned<kArmCodeAlignment>(kReachableFromOffset2 + kPcAdjustment),
"PC for BNE must be aligned.");
// If not for the extra NOP, this would allow reaching the thunk from the BNE
@@ -1068,8 +1073,8 @@
CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
size_t filler2_size =
1 * MB - (kReachableFromOffset2 + kPcAdjustment)
- - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
+ - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1091,7 +1096,7 @@
const uint32_t bne_max_forward = kBneWPlus0 | 0x003f2fff;
const uint32_t bne_last =
- BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmAlignment));
+ BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmCodeAlignment));
const std::vector<uint8_t> expected_code1 =
RawCode({kNopWInsn, kNopInsn, bne_max_forward, kLdrWInsn});
const std::vector<uint8_t> expected_code2 =
@@ -1123,7 +1128,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : kBakerValidRegs) {
++method_idx;
@@ -1177,7 +1182,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
@@ -1200,7 +1205,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t root_reg : kBakerValidRegs) {
++method_idx;
@@ -1232,7 +1237,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
@@ -1255,7 +1260,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t root_reg : kBakerValidRegsNarrow) {
++method_idx;
@@ -1281,7 +1286,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
@@ -1309,7 +1314,7 @@
Link();
// The thunk is right after the method code.
- DCHECK_ALIGNED(1 * MB, kArmAlignment);
+ DCHECK_ALIGNED(1 * MB, kArmCodeAlignment);
std::vector<uint8_t> expected_code;
for (size_t i = 0; i != num_patches; ++i) {
PushBackInsn(&expected_code, ldr);
@@ -1343,7 +1348,7 @@
// Add a method with the right size that the method code for the next one starts 1MiB
// after code for method 1.
size_t filler_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> filler_code = GenNops(filler_size / 2u);
++method_idx;
@@ -1358,16 +1363,16 @@
}
// Add 2 Baker GC root patches to the last method, one that would allow the thunk at
- // 1MiB + kArmAlignment, i.e. kArmAlignment after the method call thunk, and the
- // second that needs it kArmAlignment after that. Given the size of the GC root thunk
- // is more than the space required by the method call thunk plus kArmAlignment,
+ // 1MiB + kArmCodeAlignment, i.e. kArmCodeAlignment after the method call thunk, and the
+ // second that needs it kArmCodeAlignment after that. Given the size of the GC root thunk
+ // is more than the space required by the method call thunk plus kArmCodeAlignment,
// this pushes the first GC root thunk's pending MaxNextOffset() before the method call
// thunk's pending MaxNextOffset() which needs to be adjusted.
- ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmAlignment) + kArmAlignment,
+ ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmCodeAlignment) + kArmCodeAlignment,
CompileBakerGcRootThunk(/* root_reg */ 0, /* narrow */ false).size());
- static_assert(kArmAlignment == 8, "Code below assumes kArmAlignment == 8");
- constexpr size_t kBakerLiteralOffset1 = kArmAlignment + 2u - kPcAdjustment;
- constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmAlignment;
+ static_assert(kArmCodeAlignment == 8, "Code below assumes kArmCodeAlignment == 8");
+ constexpr size_t kBakerLiteralOffset1 = kArmCodeAlignment + 2u - kPcAdjustment;
+ constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmCodeAlignment;
// Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | (root_reg << 12)`.
const uint32_t ldr1 = kLdrWInsn | (/* root_reg */ 1 << 12);
const uint32_t ldr2 = kLdrWInsn | (/* root_reg */ 2 << 12);
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc
index 5794040..6b84472 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc
@@ -21,7 +21,7 @@
#include "art_method.h"
#include "base/bit_utils.h"
#include "base/malloc_arena_pool.h"
-#include "compiled_method-inl.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "heap_poisoning.h"
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
index 8bae5d4..ce61f43 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
@@ -112,7 +112,7 @@
const ArrayRef<const uint8_t>& last_method_code,
const ArrayRef<const LinkerPatch>& last_method_patches,
uint32_t distance_without_thunks) {
- CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u);
+ CHECK_EQ(distance_without_thunks % kArm64CodeAlignment, 0u);
uint32_t method1_offset =
kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
@@ -120,7 +120,7 @@
// We want to put the last method at a very precise offset.
const uint32_t last_method_offset = method1_offset + distance_without_thunks;
- CHECK_ALIGNED(last_method_offset, kArm64Alignment);
+ CHECK_ALIGNED(last_method_offset, kArm64CodeAlignment);
const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
// Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB).
@@ -733,24 +733,26 @@
bl_offset_in_method1 + just_over_max_positive_disp);
ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
uint32_t method_after_thunk_idx = last_method_idx;
- if (sizeof(OatQuickMethodHeader) < kArm64Alignment) {
- // The thunk needs to start on a kArm64Alignment-aligned address before the address where the
- // last method would have been if there was no thunk. If the size of the OatQuickMethodHeader
- // is at least kArm64Alignment, the thunk start shall fit between the previous filler method
- // and that address. Otherwise, it shall be inserted before that filler method.
+ if (sizeof(OatQuickMethodHeader) < kArm64CodeAlignment) {
+ // The thunk needs to start on a kArm64CodeAlignment-aligned address before the address where
+ // the last method would have been if there was no thunk. If the size of the
+ // OatQuickMethodHeader is at least kArm64CodeAlignment, the thunk start shall fit between the
+ // previous filler method and that address. Otherwise, it shall be inserted before that filler
+ // method.
method_after_thunk_idx -= 1u;
}
uint32_t method1_offset = GetMethodOffset(1u);
uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx);
- ASSERT_TRUE(IsAligned<kArm64Alignment>(method_after_thunk_offset));
+ ASSERT_TRUE(IsAligned<kArm64CodeAlignment>(method_after_thunk_offset));
uint32_t method_after_thunk_header_offset =
method_after_thunk_offset - sizeof(OatQuickMethodHeader);
uint32_t thunk_size = MethodCallThunkSize();
- uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArm64Alignment);
+ uint32_t thunk_offset = RoundDown(
+ method_after_thunk_header_offset - thunk_size, kArm64CodeAlignment);
DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
method_after_thunk_header_offset);
- ASSERT_TRUE(IsAligned<kArm64Alignment>(thunk_offset));
+ ASSERT_TRUE(IsAligned<kArm64CodeAlignment>(thunk_offset));
uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
ASSERT_TRUE(IsAligned<4u>(diff));
ASSERT_LT(diff, 128 * MB);
@@ -1065,7 +1067,8 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+ uint32_t thunk_offset =
+ GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : valid_regs) {
for (uint32_t holder_reg : valid_regs) {
@@ -1118,7 +1121,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment);
}
}
}
@@ -1155,7 +1158,7 @@
// Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
// allows the branch to reach that thunk.
size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -1170,8 +1173,8 @@
// - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
size_t filler2_size =
- 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+ 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1215,14 +1218,14 @@
// Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
// allows the branch to reach that thunk.
size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
Link();
- const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64Alignment) - kLiteralOffset1;
+ const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64CodeAlignment) - kLiteralOffset1;
const uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
const std::vector<uint8_t> expected_code1 = RawCode({cbnz, kLdrWInsn, kNopInsn});
ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
@@ -1244,7 +1247,7 @@
// Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
// allows the branch to reach that thunk.
size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -1259,8 +1262,8 @@
// - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
size_t filler2_size =
- 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+ 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1278,7 +1281,8 @@
Link();
const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
- const uint32_t cbnz_last_offset = RoundUp(raw_code2.size(), kArm64Alignment) - kLiteralOffset2;
+ const uint32_t cbnz_last_offset =
+ RoundUp(raw_code2.size(), kArm64CodeAlignment) - kLiteralOffset2;
const uint32_t cbnz_last = kCbnzIP1Plus0Insn | (cbnz_last_offset << (5 - 2));
const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
const std::vector<uint8_t> expected_code2 = RawCode({kNopInsn, cbnz_last, kLdrWInsn});
@@ -1315,7 +1319,8 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+ uint32_t thunk_offset =
+ GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : valid_regs) {
++method_idx;
@@ -1363,7 +1368,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment);
}
}
@@ -1392,7 +1397,8 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+ uint32_t thunk_offset =
+ GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment);
method_idx = 0u;
for (uint32_t root_reg : valid_regs) {
++method_idx;
@@ -1419,7 +1425,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment);
}
}
@@ -1447,7 +1453,7 @@
// Add a method with the right size that the method code for the next one starts 1MiB
// after code for method 1.
size_t filler_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> filler_code = GenNops(filler_size / 4u);
++method_idx;
@@ -1462,16 +1468,16 @@
}
// Add 2 Baker GC root patches to the last method, one that would allow the thunk at
- // 1MiB + kArm64Alignment, i.e. kArm64Alignment after the method call thunk, and the
- // second that needs it kArm64Alignment after that. Given the size of the GC root thunk
- // is more than the space required by the method call thunk plus kArm64Alignment,
+ // 1MiB + kArm64CodeAlignment, i.e. kArm64CodeAlignment after the method call thunk, and the
+ // second that needs it kArm64CodeAlignment after that. Given the size of the GC root thunk
+ // is more than the space required by the method call thunk plus kArm64CodeAlignment,
// this pushes the first GC root thunk's pending MaxNextOffset() before the method call
// thunk's pending MaxNextOffset() which needs to be adjusted.
- ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64Alignment) + kArm64Alignment,
+ ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64CodeAlignment) + kArm64CodeAlignment,
CompileBakerGcRootThunk(/* root_reg */ 0).size());
- static_assert(kArm64Alignment == 16, "Code below assumes kArm64Alignment == 16");
- constexpr size_t kBakerLiteralOffset1 = 4u + kArm64Alignment;
- constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64Alignment;
+ static_assert(kArm64CodeAlignment == 16, "Code below assumes kArm64CodeAlignment == 16");
+ constexpr size_t kBakerLiteralOffset1 = 4u + kArm64CodeAlignment;
+ constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64CodeAlignment;
// Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | root_reg`.
const uint32_t ldr1 = kLdrWInsn | /* root_reg */ 1;
const uint32_t ldr2 = kLdrWInsn | /* root_reg */ 2;
diff --git a/dex2oat/linker/code_info_table_deduper_test.cc b/dex2oat/linker/code_info_table_deduper_test.cc
index 8913b07..54b7dd5 100644
--- a/dex2oat/linker/code_info_table_deduper_test.cc
+++ b/dex2oat/linker/code_info_table_deduper_test.cc
@@ -35,7 +35,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
stream.BeginStackMapEntry(0, 64 * kPcAlign);
stream.AddDexRegisterEntry(Kind::kInStack, 0);
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 424c252..61e5783 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -25,7 +25,6 @@
#include "base/globals.h"
#include "base/leb128.h"
#include "base/utils.h"
-#include "compiled_method.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "driver/compiler_options.h"
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 39ce4d7..8dea9a6 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -64,6 +64,7 @@
struct CompilationHelper {
std::vector<std::string> dex_file_locations;
std::vector<ScratchFile> image_locations;
+ std::string extra_dex;
std::vector<std::unique_ptr<const DexFile>> extra_dex_files;
std::vector<ScratchFile> image_files;
std::vector<ScratchFile> oat_files;
@@ -79,7 +80,7 @@
protected:
void SetUp() override {
ReserveImageSpace();
- CommonCompilerTest::SetUp();
+ CommonCompilerDriverTest::SetUp();
}
void Compile(ImageHeader::StorageMode storage_mode,
@@ -91,7 +92,7 @@
const std::initializer_list<std::string>& image_classes_failing_resolution = {});
void SetUpRuntimeOptions(RuntimeOptions* options) override {
- CommonCompilerTest::SetUpRuntimeOptions(options);
+ CommonCompilerDriverTest::SetUpRuntimeOptions(options);
QuickCompilerCallbacks* new_callbacks =
new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileBootImage);
new_callbacks->SetVerificationResults(verification_results_.get());
@@ -151,18 +152,11 @@
inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode,
/*out*/ CompilationHelper& out_helper) {
CompilerDriver* driver = compiler_driver_.get();
+ Runtime::Current()->AppendToBootClassPath(
+ out_helper.extra_dex, out_helper.extra_dex, out_helper.extra_dex_files);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
std::vector<const DexFile*> class_path = class_linker->GetBootClassPath();
- for (const std::unique_ptr<const DexFile>& dex_file : out_helper.extra_dex_files) {
- {
- ScopedObjectAccess soa(Thread::Current());
- // Inject in boot class path so that the compiler driver can see it.
- class_linker->AppendToBootClassPath(soa.Self(), dex_file.get());
- }
- class_path.push_back(dex_file.get());
- }
-
// Enable write for dex2dex.
for (const DexFile* dex_file : class_path) {
out_helper.dex_file_locations.push_back(dex_file->GetLocation());
@@ -239,6 +233,7 @@
elf_writers.emplace_back(CreateElfWriterQuick(*compiler_options_, oat_file.GetFile()));
elf_writers.back()->Start();
oat_writers.emplace_back(new OatWriter(*compiler_options_,
+ verification_results_.get(),
&timings,
/*profile_compilation_info*/nullptr,
CompactDexLevel::kCompactDexLevelNone));
@@ -371,6 +366,7 @@
compiler_options_->SetMaxImageBlockSize(max_image_block_size);
image_classes_.clear();
if (!extra_dex.empty()) {
+ helper.extra_dex = extra_dex;
helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str());
}
DoCompile(storage_mode, helper);
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 692016f..729b551 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -21,10 +21,12 @@
#include <sys/stat.h>
#include <zlib.h>
+#include <charconv>
#include <memory>
#include <numeric>
#include <vector>
+#include "android-base/strings.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/callee_save_type.h"
@@ -35,7 +37,6 @@
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
#include "class_root-inl.h"
-#include "compiled_method.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_types.h"
#include "driver/compiler_options.h"
@@ -83,7 +84,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "subtype_check.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
using ::art::mirror::Class;
using ::art::mirror::DexCache;
@@ -101,7 +102,7 @@
// to make them full. We never insert additional elements to them, so we do not want to waste
// extra memory. And unlike runtime class tables, we do not want this to depend on runtime
// properties (see `Runtime::GetHashTableMaxLoadFactor()` checking for low memory mode).
-constexpr double kImageClassTableMaxLoadFactor = 0.7;
+constexpr double kImageClassTableMaxLoadFactor = 0.6;
// The actual value of `kImageInternTableMinLoadFactor` is irrelevant because image intern tables
// are never resized, but we still need to pass a reasonable value to the constructor.
@@ -110,7 +111,7 @@
// to make them full. We never insert additional elements to them, so we do not want to waste
// extra memory. And unlike runtime intern tables, we do not want this to depend on runtime
// properties (see `Runtime::GetHashTableMaxLoadFactor()` checking for low memory mode).
-constexpr double kImageInternTableMaxLoadFactor = 0.7;
+constexpr double kImageInternTableMaxLoadFactor = 0.6;
static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source,
ImageHeader::StorageMode image_storage_mode,
@@ -273,8 +274,8 @@
auto visitor = [](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
Class* klass = obj->GetClass();
- if (klass == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexFile)) {
- ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ if (klass == WellKnownClasses::dalvik_system_DexFile) {
+ ArtField* field = WellKnownClasses::dalvik_system_DexFile_cookie;
// Null out the cookie to enable determinism. b/34090128
field->SetObject</*kTransactionActive*/false>(obj, nullptr);
}
@@ -335,6 +336,13 @@
TimingLogger::ScopedTiming t("CalculateNewObjectOffsets", timings);
ScopedObjectAccess soa(self);
CalculateNewObjectOffsets();
+
+ // If dirty_image_objects_ is present - try optimizing object layout.
+ // It can only be done after the first CalculateNewObjectOffsets,
+ // because calculated offsets are used to match dirty objects between imgdiag and dex2oat.
+ if (compiler_options_.IsBootImage() && dirty_image_objects_ != nullptr) {
+ TryRecalculateOffsetsWithDirtyObjects();
+ }
}
// This needs to happen after CalculateNewObjectOffsets since it relies on intern_table_bytes_ and
@@ -730,7 +738,13 @@
// so packing them together will not result in a noticeably tighter dirty-to-clean ratio.
//
ObjPtr<mirror::Class> klass = object->GetClass<kVerifyNone, kWithoutReadBarrier>();
- if (klass->IsClassClass()) {
+ if (klass->IsStringClass<kVerifyNone>()) {
+ // Assign strings to their bin before checking dirty objects, because
+ // string intern processing expects strings to be in Bin::kString.
+ bin = Bin::kString; // Strings are almost always immutable (except for object header).
+ } else if (dirty_objects_.find(object) != dirty_objects_.end()) {
+ bin = Bin::kKnownDirty;
+ } else if (klass->IsClassClass()) {
bin = Bin::kClassVerified;
ObjPtr<mirror::Class> as_klass = object->AsClass<kVerifyNone>();
@@ -767,8 +781,6 @@
}
}
}
- } else if (klass->IsStringClass<kVerifyNone>()) {
- bin = Bin::kString; // Strings are almost always immutable (except for object header).
} else if (!klass->HasSuperClass()) {
// Only `j.l.Object` and primitive classes lack the superclass and
// there are no instances of primitive classes.
@@ -2225,12 +2237,11 @@
CHECK(ref != nullptr);
CHECK(image_writer_->IsImageBinSlotAssigned(ref.Ptr()));
ObjPtr<mirror::Class> ref_klass = ref->GetClass<kVerifyNone, kWithoutReadBarrier>();
- CHECK(ref_klass ==
- DecodeGlobalWithoutRB<mirror::Class>(vm, WellKnownClasses::dalvik_system_DexFile));
+ CHECK(ref_klass == WellKnownClasses::dalvik_system_DexFile.Get<kWithoutReadBarrier>());
// Note: The app class loader is used only for checking against the runtime
// class loader, the dex file cookie is cleared and therefore we do not need
// to run the finalizer even if we implement app image objects collection.
- ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ ArtField* field = WellKnownClasses::dalvik_system_DexFile_cookie;
CHECK(field->GetObject<kWithoutReadBarrier>(ref) == nullptr);
return;
}
@@ -2535,6 +2546,142 @@
}
}
+std::optional<HashSet<mirror::Object*>> ImageWriter::MatchDirtyObjectOffsets(
+ const HashMap<uint32_t, DirtyEntry>& dirty_entries) REQUIRES_SHARED(Locks::mutator_lock_) {
+ HashSet<mirror::Object*> dirty_objects;
+ bool mismatch_found = false;
+
+ auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(obj != nullptr);
+ if (mismatch_found) {
+ return;
+ }
+ if (!IsImageBinSlotAssigned(obj)) {
+ return;
+ }
+
+ uint8_t* image_address = reinterpret_cast<uint8_t*>(GetImageAddress(obj));
+ uint32_t offset = static_cast<uint32_t>(image_address - global_image_begin_);
+
+ auto entry_it = dirty_entries.find(offset);
+ if (entry_it == dirty_entries.end()) {
+ return;
+ }
+
+ const DirtyEntry& entry = entry_it->second;
+
+ const bool is_class = obj->IsClass();
+ const uint32_t descriptor_hash =
+ is_class ? obj->AsClass()->DescriptorHash() : obj->GetClass()->DescriptorHash();
+
+ if (is_class != entry.is_class || descriptor_hash != entry.descriptor_hash) {
+ LOG(WARNING) << "Dirty image objects offset mismatch (outdated file?)";
+ mismatch_found = true;
+ return;
+ }
+
+ dirty_objects.insert(obj);
+ };
+ Runtime::Current()->GetHeap()->VisitObjects(visitor);
+
+ // A single mismatch indicates that dirty-image-objects layout differs from
+ // current ImageWriter layout. In this case any "valid" matches are likely to be accidental,
+ // so there's no point in optimizing the layout with such data.
+ if (mismatch_found) {
+ return {};
+ }
+ if (dirty_objects.size() != dirty_entries.size()) {
+ LOG(WARNING) << "Dirty image objects missing offsets (outdated file?)";
+ return {};
+ }
+ return dirty_objects;
+}
+
+void ImageWriter::ResetObjectOffsets() {
+ const size_t image_infos_size = image_infos_.size();
+ image_infos_.clear();
+ image_infos_.resize(image_infos_size);
+
+ native_object_relocations_.clear();
+
+ // CalculateNewObjectOffsets stores image offsets of the objects in lock words,
+ // while original lock words are preserved in saved_hashcode_map.
+ // Restore original lock words.
+ auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(obj != nullptr);
+ const auto it = saved_hashcode_map_.find(obj);
+ obj->SetLockWord(it != saved_hashcode_map_.end() ? LockWord::FromHashCode(it->second, 0u) :
+ LockWord::Default(),
+ false);
+ };
+ Runtime::Current()->GetHeap()->VisitObjects(visitor);
+
+ saved_hashcode_map_.clear();
+}
+
+void ImageWriter::TryRecalculateOffsetsWithDirtyObjects() {
+ const std::optional<HashMap<uint32_t, ImageWriter::DirtyEntry>> dirty_entries =
+ ParseDirtyObjectOffsets(*dirty_image_objects_);
+ if (!dirty_entries || dirty_entries->empty()) {
+ return;
+ }
+
+ std::optional<HashSet<mirror::Object*>> dirty_objects = MatchDirtyObjectOffsets(*dirty_entries);
+ if (!dirty_objects || dirty_objects->empty()) {
+ return;
+ }
+ // Calculate offsets again, now with dirty object offsets.
+ LOG(INFO) << "Recalculating object offsets using dirty-image-objects";
+ dirty_objects_ = std::move(*dirty_objects);
+ ResetObjectOffsets();
+ CalculateNewObjectOffsets();
+}
+
+std::optional<HashMap<uint32_t, ImageWriter::DirtyEntry>> ImageWriter::ParseDirtyObjectOffsets(
+ const HashSet<std::string>& dirty_image_objects) REQUIRES_SHARED(Locks::mutator_lock_) {
+ HashMap<uint32_t, DirtyEntry> dirty_entries;
+
+ // Go through each dirty-image-object line, parse only lines of the format:
+ // "dirty_obj: <offset> <type> <descriptor_hash>"
+ // <offset> -- decimal uint32.
+ // <type> -- "class" or "instance" (defines if descriptor is referring to a class or an instance).
+ // <descriptor_hash> -- decimal uint32 (from DescriptorHash() method).
+ const std::string prefix = "dirty_obj:";
+ for (const std::string& entry_str : dirty_image_objects) {
+ // Skip the lines of old dirty-image-object format.
+ if (std::strncmp(entry_str.data(), prefix.data(), prefix.size()) != 0) {
+ continue;
+ }
+
+ const std::vector<std::string> tokens = android::base::Split(entry_str, " ");
+ if (tokens.size() != 4) {
+ LOG(WARNING) << "Invalid dirty image objects format: \"" << entry_str << "\"";
+ return {};
+ }
+
+ uint32_t offset = 0;
+ std::from_chars_result res =
+ std::from_chars(tokens[1].data(), tokens[1].data() + tokens[1].size(), offset);
+ if (res.ec != std::errc()) {
+ LOG(WARNING) << "Couldn't parse dirty object offset: \"" << entry_str << "\"";
+ return {};
+ }
+
+ DirtyEntry entry;
+ entry.is_class = (tokens[2] == "class");
+ res = std::from_chars(
+ tokens[3].data(), tokens[3].data() + tokens[3].size(), entry.descriptor_hash);
+ if (res.ec != std::errc()) {
+ LOG(WARNING) << "Couldn't parse dirty object descriptor hash: \"" << entry_str << "\"";
+ return {};
+ }
+
+ dirty_entries.insert(std::make_pair(offset, entry));
+ }
+
+ return dirty_entries;
+}
+
std::pair<size_t, dchecked_vector<ImageSection>>
ImageWriter::ImageInfo::CreateImageSections() const {
dchecked_vector<ImageSection> sections(ImageHeader::kSectionCount);
@@ -3296,25 +3443,7 @@
const OatFile* oat_file = image_spaces[0]->GetOatFile();
CHECK(oat_file != nullptr);
const OatHeader& header = oat_file->GetOatHeader();
- switch (type) {
- // TODO: We could maybe clean this up if we stored them in an array in the oat header.
- case StubType::kQuickGenericJNITrampoline:
- return static_cast<const uint8_t*>(header.GetQuickGenericJniTrampoline());
- case StubType::kJNIDlsymLookupTrampoline:
- return static_cast<const uint8_t*>(header.GetJniDlsymLookupTrampoline());
- case StubType::kJNIDlsymLookupCriticalTrampoline:
- return static_cast<const uint8_t*>(header.GetJniDlsymLookupCriticalTrampoline());
- case StubType::kQuickIMTConflictTrampoline:
- return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline());
- case StubType::kQuickResolutionTrampoline:
- return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline());
- case StubType::kQuickToInterpreterBridge:
- return static_cast<const uint8_t*>(header.GetQuickToInterpreterBridge());
- case StubType::kNterpTrampoline:
- return static_cast<const uint8_t*>(header.GetNterpTrampoline());
- default:
- UNREACHABLE();
- }
+ return header.GetOatAddress(type);
}
const ImageInfo& primary_image_info = GetImageInfo(0);
return GetOatAddressForOffset(primary_image_info.GetStubOffset(type), primary_image_info);
@@ -3344,8 +3473,7 @@
quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
}
- bool needs_clinit_check = NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass<kWithoutReadBarrier>()->IsVisiblyInitialized();
+ bool still_needs_clinit_check = method->StillNeedsClinitCheck<kWithoutReadBarrier>();
if (quick_code == nullptr) {
// If we don't have code, use generic jni / interpreter.
@@ -3355,7 +3483,7 @@
} else if (CanMethodUseNterp(method, compiler_options_.GetInstructionSet())) {
// The nterp trampoline doesn't do initialization checks, so install the
// resolution stub if needed.
- if (needs_clinit_check) {
+ if (still_needs_clinit_check) {
quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
} else {
quick_code = GetOatAddress(StubType::kNterpTrampoline);
@@ -3364,7 +3492,7 @@
// The interpreter brige performs class initialization check if needed.
quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge);
}
- } else if (needs_clinit_check) {
+ } else if (still_needs_clinit_check && !compiler_options_.ShouldCompileWithClinitCheck(method)) {
// If we do have code but the method needs a class initialization check before calling
// that code, install the resolution stub that will perform the check.
quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index e5eeacc..f7d3a2d 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -45,6 +45,7 @@
#include "intern_table.h"
#include "lock_word.h"
#include "mirror/dex_cache.h"
+#include "oat.h"
#include "oat_file.h"
#include "obj_ptr.h"
@@ -229,18 +230,6 @@
};
friend std::ostream& operator<<(std::ostream& stream, NativeObjectRelocationType type);
- enum class StubType {
- kJNIDlsymLookupTrampoline,
- kJNIDlsymLookupCriticalTrampoline,
- kQuickGenericJNITrampoline,
- kQuickIMTConflictTrampoline,
- kQuickResolutionTrampoline,
- kQuickToInterpreterBridge,
- kNterpTrampoline,
- kLast = kNterpTrampoline,
- };
- friend std::ostream& operator<<(std::ostream& stream, StubType stub_type);
-
static constexpr size_t kBinBits =
MinimumBitsToStore<uint32_t>(static_cast<size_t>(Bin::kMirrorCount) - 1);
// uint32 = typeof(lockword_)
@@ -462,6 +451,23 @@
REQUIRES_SHARED(Locks::mutator_lock_);
void CalculateObjectBinSlots(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Undo the changes of CalculateNewObjectOffsets.
+ void ResetObjectOffsets() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Reset and calculate new offsets with dirty objects optimization.
+ // Does nothing if dirty object offsets don't match with current offsets.
+ void TryRecalculateOffsetsWithDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Dirty object data from dirty-image-objects.
+ struct DirtyEntry {
+ uint32_t descriptor_hash = 0;
+ bool is_class = false;
+ };
+ // Parse dirty-image-objects into (offset->entry) map. Returns nullopt on parse error.
+ static std::optional<HashMap<uint32_t, DirtyEntry>> ParseDirtyObjectOffsets(
+ const HashSet<std::string>& dirty_image_objects) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Get all objects that match dirty_entries by offset. Returns nullopt if there is a mismatch.
+ std::optional<HashSet<mirror::Object*>> MatchDirtyObjectOffsets(
+ const HashMap<uint32_t, DirtyEntry>& dirty_entries) REQUIRES_SHARED(Locks::mutator_lock_);
// Creates the contiguous image in memory and adjusts pointers.
void CopyAndFixupNativeData(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -685,9 +691,14 @@
// Map of dex files to the indexes of oat files that they were compiled into.
const HashMap<const DexFile*, size_t>& dex_file_oat_index_map_;
- // Set of objects known to be dirty in the image. Can be nullptr if there are none.
+ // Set of classes/objects known to be dirty in the image. Can be nullptr if there are none.
+ // For old dirty-image-objects format this set contains descriptors of dirty classes.
+ // For new format -- a set of dirty object offsets and descriptor hashes.
const HashSet<std::string>* dirty_image_objects_;
+ // Dirty object instances parsed from dirty_image_object_
+ HashSet<mirror::Object*> dirty_objects_;
+
// Objects are guaranteed to not cross the region size boundary.
size_t region_size_ = 0u;
@@ -712,7 +723,6 @@
std::ostream& operator<<(std::ostream& stream, ImageWriter::Bin bin);
std::ostream& operator<<(std::ostream& stream, ImageWriter::NativeObjectRelocationType type);
-std::ostream& operator<<(std::ostream& stream, ImageWriter::StubType stub_type);
} // namespace linker
} // namespace art
diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc
index 2a05816..a393eb8 100644
--- a/dex2oat/linker/multi_oat_relative_patcher_test.cc
+++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc
@@ -16,8 +16,8 @@
#include "multi_oat_relative_patcher.h"
-#include "compiled_method.h"
#include "debug/method_debug_info.h"
+#include "driver/compiled_method.h"
#include "gtest/gtest.h"
#include "linker/linker_patch.h"
#include "stream/vector_output_stream.h"
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 19e77de..cf86006 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -37,7 +37,6 @@
#include "class_linker.h"
#include "class_table-inl.h"
#include "code_info_table_deduper.h"
-#include "compiled_method-inl.h"
#include "debug/method_debug_info.h"
#include "dex/art_dex_file_loader.h"
#include "dex/class_accessor-inl.h"
@@ -49,6 +48,7 @@
#include "dex/verification_results.h"
#include "dex_container.h"
#include "dexlayout.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "gc/space/image_space.h"
@@ -384,6 +384,7 @@
<< "file_offset=" << file_offset << " offset_=" << offset_
OatWriter::OatWriter(const CompilerOptions& compiler_options,
+ const VerificationResults* verification_results,
TimingLogger* timings,
ProfileCompilationInfo* info,
CompactDexLevel compact_dex_level)
@@ -395,6 +396,7 @@
zipped_dex_file_locations_(),
compiler_driver_(nullptr),
compiler_options_(compiler_options),
+ verification_results_(verification_results),
image_writer_(nullptr),
extract_dex_files_into_vdex_(true),
vdex_begin_(nullptr),
@@ -1013,7 +1015,7 @@
ClassStatus status;
bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status);
if (!found) {
- const VerificationResults* results = writer_->compiler_options_.GetVerificationResults();
+ const VerificationResults* results = writer_->verification_results_;
if (results != nullptr && results->IsClassRejected(class_ref)) {
// The oat class status is used only for verification of resolved classes,
// so use ClassStatus::kErrorResolved whether the class was resolved or unresolved
@@ -1351,7 +1353,7 @@
ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
uint32_t code_size = quick_code.size() * sizeof(uint8_t);
- uint32_t thumb_offset = compiled_method->CodeDelta();
+ uint32_t thumb_offset = compiled_method->GetEntryPointAdjustment();
// Deduplicate code arrays if we are not producing debuggable code.
bool deduped = true;
@@ -1486,7 +1488,7 @@
offset_ = relative_patcher_->ReserveSpace(offset_, compiled_method, method_ref);
offset_ += CodeAlignmentSize(offset_, *compiled_method);
DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader),
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet()));
return offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
}
@@ -1797,9 +1799,10 @@
DCHECK_OFFSET_();
}
DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader),
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
- DCHECK_EQ(method_offsets.code_offset_,
- offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
+ GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet()));
+ DCHECK_EQ(
+ method_offsets.code_offset_,
+ offset_ + sizeof(OatQuickMethodHeader) + compiled_method->GetEntryPointAdjustment())
<< dex_file_->PrettyMethod(method_ref.index);
const OatQuickMethodHeader& method_header =
oat_class->method_headers_[method_offsets_index];
@@ -2396,22 +2399,22 @@
const bool generate_debug_info = GetCompilerOptions().GenerateAnyDebugInfo();
size_t adjusted_offset = offset;
- #define DO_TRAMPOLINE(field, fn_name) \
- /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \
- offset = CompiledCode::AlignCode(offset + 4, instruction_set); \
- adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set); \
- oat_header_->Set ## fn_name ## Offset(adjusted_offset); \
- (field) = compiler_driver_->Create ## fn_name(); \
- if (generate_debug_info) { \
- debug::MethodDebugInfo info = {}; \
- info.custom_name = #fn_name; \
- info.isa = instruction_set; \
- info.is_code_address_text_relative = true; \
- /* Use the code offset rather than the `adjusted_offset`. */ \
- info.code_address = offset - oat_header_->GetExecutableOffset(); \
- info.code_size = (field)->size(); \
- method_info_.push_back(std::move(info)); \
- } \
+ #define DO_TRAMPOLINE(field, fn_name) \
+ /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \
+ offset = CompiledCode::AlignCode(offset + 4, instruction_set); \
+ adjusted_offset = offset + GetInstructionSetEntryPointAdjustment(instruction_set); \
+ oat_header_->Set ## fn_name ## Offset(adjusted_offset); \
+ (field) = compiler_driver_->Create ## fn_name(); \
+ if (generate_debug_info) { \
+ debug::MethodDebugInfo info = {}; \
+ info.custom_name = #fn_name; \
+ info.isa = instruction_set; \
+ info.is_code_address_text_relative = true; \
+ /* Use the code offset rather than the `adjusted_offset`. */ \
+ info.code_address = offset - oat_header_->GetExecutableOffset(); \
+ info.code_size = (field)->size(); \
+ method_info_.push_back(std::move(info)); \
+ } \
offset += (field)->size();
DO_TRAMPOLINE(jni_dlsym_lookup_trampoline_, JniDlsymLookupTrampoline);
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index ebff7a1..bfacf60 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -49,6 +49,7 @@
class TimingLogger;
class TypeLookupTable;
class VdexFile;
+class VerificationResults;
class ZipEntry;
namespace debug {
@@ -115,6 +116,7 @@
class OatWriter {
public:
OatWriter(const CompilerOptions& compiler_options,
+ const VerificationResults* verification_results,
TimingLogger* timings,
ProfileCompilationInfo* info,
CompactDexLevel compact_dex_level);
@@ -391,6 +393,7 @@
const CompilerDriver* compiler_driver_;
const CompilerOptions& compiler_options_;
+ const VerificationResults* const verification_results_;
ImageWriter* image_writer_;
// Whether the dex files being compiled are going to be extracted to the vdex.
bool extract_dex_files_into_vdex_;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 6b2198d..6742cf7 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -24,7 +24,6 @@
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "common_compiler_driver_test.h"
-#include "compiled_method-inl.h"
#include "compiler.h"
#include "debug/method_debug_info.h"
#include "dex/class_accessor-inl.h"
@@ -32,6 +31,7 @@
#include "dex/quick_compiler_callbacks.h"
#include "dex/test_dex_file_builder.h"
#include "dex/verification_results.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -107,6 +107,7 @@
TimingLogger timings("WriteElf", false, false);
ClearBootImageOption();
OatWriter oat_writer(*compiler_options_,
+ verification_results_.get(),
&timings,
/*profile_compilation_info*/nullptr,
CompactDexLevel::kCompactDexLevelNone);
@@ -134,6 +135,7 @@
TimingLogger timings("WriteElf", false, false);
ClearBootImageOption();
OatWriter oat_writer(*compiler_options_,
+ verification_results_.get(),
&timings,
profile_compilation_info,
CompactDexLevel::kCompactDexLevelNone);
@@ -156,6 +158,7 @@
TimingLogger timings("WriteElf", false, false);
ClearBootImageOption();
OatWriter oat_writer(*compiler_options_,
+ verification_results_.get(),
&timings,
profile_compilation_info,
CompactDexLevel::kCompactDexLevelNone);
@@ -181,7 +184,7 @@
if (!oat_writer.WriteAndOpenDexFiles(
vdex_file,
verify,
- /*update_input_vdex=*/ false,
+ /*use_existing_vdex=*/ false,
copy,
&opened_dex_files_maps,
&opened_dex_files)) {
@@ -505,7 +508,7 @@
EXPECT_EQ(68U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(4U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(167 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ EXPECT_EQ(170 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index 56e0dc1..2900523 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -24,9 +24,9 @@
#include "base/array_ref.h"
#include "base/globals.h"
#include "base/macros.h"
-#include "compiled_method-inl.h"
#include "dex/method_reference.h"
#include "dex/string_reference.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiled_method_storage.h"
#include "linker/relative_patcher.h"
#include "oat_quick_method_header.h"
@@ -132,7 +132,7 @@
offset += alignment_size;
offset += sizeof(OatQuickMethodHeader);
- uint32_t quick_code_offset = offset + compiled_method->CodeDelta();
+ uint32_t quick_code_offset = offset + compiled_method->GetEntryPointAdjustment();
const auto code = compiled_method->GetQuickCode();
offset += code.size();
@@ -172,7 +172,8 @@
if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod());
uint32_t target_offset =
- result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta();
+ result.first ? result.second
+ : kTrampolineOffset + compiled_method->GetEntryPointAdjustment();
patcher_->PatchCall(&patched_code_,
patch.LiteralOffset(),
offset + patch.LiteralOffset(),
@@ -227,7 +228,7 @@
auto result = method_offset_map_.FindMethodOffset(method_ref);
CHECK(result.first); // Must have been linked.
- size_t offset = result.second - compiled_methods_[idx]->CodeDelta();
+ size_t offset = result.second - compiled_methods_[idx]->GetEntryPointAdjustment();
CHECK_LT(offset, output_.size());
CHECK_LE(offset + expected_code.size(), output_.size());
ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size());
diff --git a/dex2oat/linker/x86/relative_patcher_x86.cc b/dex2oat/linker/x86/relative_patcher_x86.cc
index e3b94b0..a444446 100644
--- a/dex2oat/linker/x86/relative_patcher_x86.cc
+++ b/dex2oat/linker/x86/relative_patcher_x86.cc
@@ -16,7 +16,6 @@
#include "linker/x86/relative_patcher_x86.h"
-#include "compiled_method.h"
#include "linker/linker_patch.h"
namespace art {
diff --git a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
index 0b9d07e..629affc 100644
--- a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
+++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
@@ -16,7 +16,6 @@
#include "linker/x86_64/relative_patcher_x86_64.h"
-#include "compiled_method.h"
#include "linker/linker_patch.h"
namespace art {
diff --git a/compiler/utils/swap_space.cc b/dex2oat/utils/swap_space.cc
similarity index 100%
rename from compiler/utils/swap_space.cc
rename to dex2oat/utils/swap_space.cc
diff --git a/dex2oat/utils/swap_space.h b/dex2oat/utils/swap_space.h
new file mode 100644
index 0000000..aba6485
--- /dev/null
+++ b/dex2oat/utils/swap_space.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_UTILS_SWAP_SPACE_H_
+#define ART_DEX2OAT_UTILS_SWAP_SPACE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <cstdlib>
+#include <list>
+#include <set>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+
+namespace art {
+
+// An arena pool that creates arenas backed by an mmaped file.
+class SwapSpace {
+ public:
+ SwapSpace(int fd, size_t initial_size);
+ ~SwapSpace();
+ void* Alloc(size_t size) REQUIRES(!lock_);
+ void Free(void* ptr, size_t size) REQUIRES(!lock_);
+
+ size_t GetSize() {
+ return size_;
+ }
+
+ private:
+ // Chunk of space.
+ struct SpaceChunk {
+ // We need mutable members as we keep these objects in a std::set<> (providing only const
+ // access) but we modify these members while carefully preserving the std::set<> ordering.
+ mutable uint8_t* ptr;
+ mutable size_t size;
+
+ uintptr_t Start() const {
+ return reinterpret_cast<uintptr_t>(ptr);
+ }
+ uintptr_t End() const {
+ return reinterpret_cast<uintptr_t>(ptr) + size;
+ }
+ };
+
+ class SortChunkByPtr {
+ public:
+ bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
+ return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
+ }
+ };
+
+ using FreeByStartSet = std::set<SpaceChunk, SortChunkByPtr>;
+
+ // Map size to an iterator to free_by_start_'s entry.
+ struct FreeBySizeEntry {
+ FreeBySizeEntry(size_t sz, FreeByStartSet::const_iterator entry)
+ : size(sz), free_by_start_entry(entry) { }
+
+ // We need mutable members as we keep these objects in a std::set<> (providing only const
+ // access) but we modify these members while carefully preserving the std::set<> ordering.
+ mutable size_t size;
+ mutable FreeByStartSet::const_iterator free_by_start_entry;
+ };
+ struct FreeBySizeComparator {
+ bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) const {
+ if (lhs.size != rhs.size) {
+ return lhs.size < rhs.size;
+ } else {
+ return lhs.free_by_start_entry->Start() < rhs.free_by_start_entry->Start();
+ }
+ }
+ };
+ using FreeBySizeSet = std::set<FreeBySizeEntry, FreeBySizeComparator>;
+
+ SpaceChunk NewFileChunk(size_t min_size) REQUIRES(lock_);
+
+ void RemoveChunk(FreeBySizeSet::const_iterator free_by_size_pos) REQUIRES(lock_);
+ void InsertChunk(const SpaceChunk& chunk) REQUIRES(lock_);
+
+ int fd_;
+ size_t size_;
+
+ // NOTE: Boost.Bimap would be useful for the two following members.
+
+ // Map start of a free chunk to its size.
+ FreeByStartSet free_by_start_ GUARDED_BY(lock_);
+ // Free chunks ordered by size.
+ FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
+
+ mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ DISALLOW_COPY_AND_ASSIGN(SwapSpace);
+};
+
+template <typename T> class SwapAllocator;
+
+template <>
+class SwapAllocator<void> {
+ public:
+ using value_type = void;
+ using pointer = void*;
+ using const_pointer = const void*;
+
+ template <typename U>
+ struct rebind {
+ using other = SwapAllocator<U>;
+ };
+
+ explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+ template <typename U>
+ SwapAllocator(const SwapAllocator<U>& other)
+ : swap_space_(other.swap_space_) {}
+
+ SwapAllocator(const SwapAllocator& other) = default;
+ SwapAllocator& operator=(const SwapAllocator& other) = default;
+ ~SwapAllocator() = default;
+
+ private:
+ SwapSpace* swap_space_;
+
+ template <typename U>
+ friend class SwapAllocator;
+
+ template <typename U>
+ friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
+};
+
+template <typename T>
+class SwapAllocator {
+ public:
+ using value_type = T;
+ using pointer = T*;
+ using reference = T&;
+ using const_pointer = const T*;
+ using const_reference = const T&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ template <typename U>
+ struct rebind {
+ using other = SwapAllocator<U>;
+ };
+
+ explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+ template <typename U>
+ SwapAllocator(const SwapAllocator<U>& other)
+ : swap_space_(other.swap_space_) {}
+
+ SwapAllocator(const SwapAllocator& other) = default;
+ SwapAllocator& operator=(const SwapAllocator& other) = default;
+ ~SwapAllocator() = default;
+
+ size_type max_size() const {
+ return static_cast<size_type>(-1) / sizeof(T);
+ }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
+ DCHECK_LE(n, max_size());
+ if (swap_space_ == nullptr) {
+ T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
+ CHECK_IMPLIES(result == nullptr, n == 0u); // Abort if malloc() fails.
+ return result;
+ } else {
+ return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
+ }
+ }
+ void deallocate(pointer p, size_type n) {
+ if (swap_space_ == nullptr) {
+ free(p);
+ } else {
+ swap_space_->Free(p, n * sizeof(T));
+ }
+ }
+
+ void construct(pointer p, const_reference val) {
+ new (static_cast<void*>(p)) value_type(val);
+ }
+ template <class U, class... Args>
+ void construct(U* p, Args&&... args) {
+ ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+ }
+ void destroy(pointer p) {
+ p->~value_type();
+ }
+
+ inline bool operator==(SwapAllocator const& other) {
+ return swap_space_ == other.swap_space_;
+ }
+ inline bool operator!=(SwapAllocator const& other) {
+ return !operator==(other);
+ }
+
+ private:
+ SwapSpace* swap_space_;
+
+ template <typename U>
+ friend class SwapAllocator;
+
+ template <typename U>
+ friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
+};
+
+template <typename T>
+inline bool operator==(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
+ return lhs.swap_space_ == rhs.swap_space_;
+}
+
+template <typename T>
+inline bool operator!=(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename T>
+using SwapVector = std::vector<T, SwapAllocator<T>>;
+template <typename T, typename Comparator>
+using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
+
+} // namespace art
+
+#endif // ART_DEX2OAT_UTILS_SWAP_SPACE_H_
diff --git a/dex2oat/utils/swap_space_test.cc b/dex2oat/utils/swap_space_test.cc
new file mode 100644
index 0000000..eb79cfd
--- /dev/null
+++ b/dex2oat/utils/swap_space_test.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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 "utils/swap_space.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdio>
+
+#include "gtest/gtest.h"
+
+#include "base/common_art_test.h"
+#include "base/os.h"
+#include "base/unix_file/fd_file.h"
+
+namespace art {
+
+class SwapSpaceTest : public CommonArtTest {};
+
+static void SwapTest(bool use_file) {
+ ScratchFile scratch;
+ int fd = scratch.GetFd();
+ unlink(scratch.GetFilename().c_str());
+
+ SwapSpace pool(fd, 1 * MB);
+ SwapAllocator<void> alloc(use_file ? &pool : nullptr);
+
+ SwapVector<int32_t> v(alloc);
+ v.reserve(1000000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v.push_back(i);
+ EXPECT_EQ(i, v[i]);
+ }
+
+ SwapVector<int32_t> v2(alloc);
+ v2.reserve(1000000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v2.push_back(i);
+ EXPECT_EQ(i, v2[i]);
+ }
+
+ SwapVector<int32_t> v3(alloc);
+ v3.reserve(500000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v3.push_back(i);
+ EXPECT_EQ(i, v2[i]);
+ }
+
+ // Verify contents.
+ for (int32_t i = 0; i < 1000000; ++i) {
+ EXPECT_EQ(i, v[i]);
+ EXPECT_EQ(i, v2[i]);
+ EXPECT_EQ(i, v3[i]);
+ }
+
+ scratch.Close();
+}
+
+TEST_F(SwapSpaceTest, Memory) {
+ SwapTest(false);
+}
+
+TEST_F(SwapSpaceTest, Swap) {
+ SwapTest(true);
+}
+
+} // namespace art
diff --git a/dex2oat/verifier_deps_test.cc b/dex2oat/verifier_deps_test.cc
index 6a2deba..00593f5 100644
--- a/dex2oat/verifier_deps_test.cc
+++ b/dex2oat/verifier_deps_test.cc
@@ -502,6 +502,7 @@
std::vector<std::unique_ptr<const DexFile>> first_dex_files = OpenTestDexFiles("VerifierDeps");
std::vector<std::unique_ptr<const DexFile>> second_dex_files = OpenTestDexFiles("MultiDex");
std::vector<const DexFile*> dex_files;
+ dex_files.reserve(first_dex_files.size() + second_dex_files.size());
for (auto& dex_file : first_dex_files) {
dex_files.push_back(dex_file.get());
}
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 66ca84b..c819c67 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -20,7 +20,7 @@
#define ART_DEXLAYOUT_DEX_IR_H_
#include <stdint.h>
-
+#include <unordered_map>
#include <vector>
#include "base/iteration_range.h"
@@ -262,13 +262,21 @@
// Sort the vector by copying pointers over.
template <typename MapType>
void SortByMapOrder(const MapType& map) {
- auto it = map.begin();
CHECK_EQ(map.size(), Size());
+
+ // Move all pointers to a temporary map owning the pointers.
+ std::unordered_map<T*, ElementType> pointers_map;
+ pointers_map.reserve(Size());
+ for (std::unique_ptr<T>& element : collection_) {
+ pointers_map[element.get()] = std::move(element);
+ }
+
+ // Move back the pointers to the original vector according to the map order.
+ auto it = map.begin();
for (size_t i = 0; i < Size(); ++i) {
- // There are times when the array will temporarily contain the same pointer twice, doing the
- // release here sure there is no double free errors.
- collection_[i].release();
- collection_[i].reset(it->second);
+ auto element_it = pointers_map.find(it->second);
+ DCHECK(element_it != pointers_map.end());
+ collection_[i] = std::move(element_it->second);
++it;
}
}
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index 7f05ae8..e7473c0 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -30,8 +30,6 @@
namespace art {
-constexpr uint32_t DexWriter::kDataSectionAlignment;
-
static size_t EncodeIntValue(int32_t value, uint8_t* buffer) {
size_t length = 0;
if (value >= 0) {
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 1e37b8d..82f0c1a 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -83,8 +83,12 @@
art_cc_defaults {
name: "art_dexoptanalyzer_tests_defaults",
+ static_libs: [
+ "libziparchive",
+ ],
shared_libs: [
- "libbacktrace",
+ "libunwindstack",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
],
data: [
":art-gtest-jars-LinkageTest",
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 0cc2cdb..ef43157 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -122,9 +122,6 @@
UsageError(" print a colon-separated list of its dex files to standard output. Dexopt");
UsageError(" needed analysis is not performed when this option is set.");
UsageError("");
- UsageError(" --validate-bcp: validates the boot class path files (.art, .oat, .vdex).");
- UsageError(" Requires --isa and --image options to locate artifacts.");
- UsageError("");
UsageError("Return code:");
UsageError(" To make it easier to integrate with the internal tools this command will make");
UsageError(" available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
@@ -147,10 +144,7 @@
class DexoptAnalyzer final {
public:
- DexoptAnalyzer() :
- only_flatten_context_(false),
- only_validate_bcp_(false),
- downgrade_(false) {}
+ DexoptAnalyzer() : only_flatten_context_(false), downgrade_(false) {}
void ParseArgs(int argc, char **argv) {
original_argc = argc;
@@ -236,8 +230,6 @@
}
} else if (option == "--flatten-class-loader-context") {
only_flatten_context_ = true;
- } else if (option == "--validate-bcp") {
- only_validate_bcp_ = true;
} else {
Usage("Unknown argument '%s'", raw_option);
}
@@ -324,6 +316,7 @@
class_loader_context.get(),
/*load_executable=*/ false,
/*only_load_trusted_executable=*/ false,
+ /*runtime_options=*/ nullptr,
vdex_fd_,
oat_fd_,
zip_fd_);
@@ -362,82 +355,6 @@
}
}
- // Validates the boot classpath and boot classpath extensions by checking the image checksums,
- // the oat files and the vdex files.
- //
- // Returns `ReturnCode::kNoDexOptNeeded` when all the files are up-to-date,
- // `ReturnCode::kDex2OatFromScratch` if any of the files are missing or out-of-date, and
- // `ReturnCode::kErrorCannotCreateRuntime` if the files could not be tested due to problem
- // creating a runtime.
- ReturnCode ValidateBcp() const {
- using ImageSpace = gc::space::ImageSpace;
-
- if (!CreateRuntime()) {
- return ReturnCode::kErrorCannotCreateRuntime;
- }
- std::unique_ptr<Runtime> runtime(Runtime::Current());
-
- auto dex_files = ArrayRef<const DexFile* const>(runtime->GetClassLinker()->GetBootClassPath());
- auto boot_image_spaces = ArrayRef<ImageSpace* const>(runtime->GetHeap()->GetBootImageSpaces());
- const std::string checksums = ImageSpace::GetBootClassPathChecksums(boot_image_spaces,
- dex_files);
-
- std::string error_msg;
- const std::vector<std::string>& bcp = runtime->GetBootClassPath();
- const std::vector<std::string>& bcp_locations = runtime->GetBootClassPathLocations();
- const std::vector<int>& bcp_fds = runtime->GetBootClassPathFds();
- const std::vector<std::string>& image_locations = runtime->GetImageLocations();
- const std::string bcp_locations_path = android::base::Join(bcp_locations, ':');
- if (!ImageSpace::VerifyBootClassPathChecksums(checksums,
- bcp_locations_path,
- ArrayRef<const std::string>(image_locations),
- ArrayRef<const std::string>(bcp_locations),
- ArrayRef<const std::string>(bcp),
- ArrayRef<const int>(bcp_fds),
- runtime->GetInstructionSet(),
- &error_msg)) {
- LOG(INFO) << "Failed to verify boot class path checksums: " << error_msg;
- return ReturnCode::kDex2OatFromScratch;
- }
-
- const auto& image_spaces = runtime->GetHeap()->GetBootImageSpaces();
- size_t bcp_component_count = 0;
- for (const auto& image_space : image_spaces) {
- if (!image_space->GetImageHeader().IsValid()) {
- LOG(INFO) << "Image header is not valid: " << image_space->GetImageFilename();
- return ReturnCode::kDex2OatFromScratch;
- }
- const OatFile* oat_file = image_space->GetOatFile();
- if (oat_file == nullptr) {
- const std::string oat_path = ReplaceFileExtension(image_space->GetImageFilename(), "oat");
- LOG(INFO) << "Oat file missing: " << oat_path;
- return ReturnCode::kDex2OatFromScratch;
- }
- if (!oat_file->GetOatHeader().IsValid() ||
- !ImageSpace::ValidateOatFile(*oat_file, &error_msg)) {
- LOG(INFO) << "Oat file is not valid: " << oat_file->GetLocation() << " " << error_msg;
- return ReturnCode::kDex2OatFromScratch;
- }
- const VdexFile* vdex_file = oat_file->GetVdexFile();
- if (vdex_file == nullptr || !vdex_file->IsValid()) {
- LOG(INFO) << "Vdex file is not valid : " << oat_file->GetLocation();
- return ReturnCode::kDex2OatFromScratch;
- }
- bcp_component_count += image_space->GetComponentCount();
- }
-
- // If the number of components encountered in the image spaces does not match the number
- // of components expected from the boot classpath locations then something is missing.
- if (bcp_component_count != bcp_locations.size()) {
- for (size_t i = bcp_component_count; i < bcp_locations.size(); ++i) {
- LOG(INFO) << "Missing image file for " << bcp_locations[i];
- }
- return ReturnCode::kDex2OatFromScratch;
- }
-
- return ReturnCode::kNoDexOptNeeded;
- }
-
ReturnCode FlattenClassLoaderContext() const {
DCHECK(only_flatten_context_);
if (context_str_.empty()) {
@@ -449,15 +366,13 @@
Usage("Invalid --class-loader-context '%s'", context_str_.c_str());
}
- std::cout << context->FlattenDexPaths() << std::flush;
+ std::cout << android::base::Join(context->FlattenDexPaths(), ':') << std::flush;
return ReturnCode::kFlattenClassLoaderContextSuccess;
}
ReturnCode Run() const {
if (only_flatten_context_) {
return FlattenClassLoaderContext();
- } else if (only_validate_bcp_) {
- return ValidateBcp();
} else {
return GetDexOptNeeded();
}
@@ -469,7 +384,6 @@
CompilerFilter::Filter compiler_filter_;
std::string context_str_;
bool only_flatten_context_;
- bool only_validate_bcp_;
ProfileAnalysisResult profile_analysis_result_;
bool downgrade_;
std::string image_;
diff --git a/disassembler/Android.bp b/disassembler/Android.bp
index 4b55673..b7f758f 100644
--- a/disassembler/Android.bp
+++ b/disassembler/Android.bp
@@ -104,7 +104,6 @@
],
},
},
-
apex_available: [
"com.android.art",
"com.android.art.debug",
@@ -132,3 +131,38 @@
"com.android.art",
],
}
+
+art_cc_defaults {
+ name: "art_disassembler_tests_defaults",
+ codegen: {
+ arm64: {
+ srcs: ["disassembler_arm64_test.cc"],
+ },
+ },
+}
+
+// Version of ART gtest `art_disassembler_tests` bundled with the ART APEX on target.
+// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
+art_cc_test {
+ name: "art_disassembler_tests",
+ defaults: [
+ "art_gtest_defaults",
+ "art_disassembler_tests_defaults",
+ ],
+ static_libs: [
+ "libvixld",
+ ],
+}
+
+// Standalone version of ART gtest `art_disassembler_tests`,
+// not bundled with the ART APEX on target.
+art_cc_test {
+ name: "art_standalone_disassembler_tests",
+ defaults: [
+ "art_standalone_gtest_defaults",
+ "art_disassembler_tests_defaults",
+ ],
+ static_libs: [
+ "libvixl",
+ ],
+}
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index 0d51cfd..23472a8 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -18,6 +18,8 @@
#include <inttypes.h>
+#include <regex>
+
#include <sstream>
#include "android-base/logging.h"
@@ -58,9 +60,34 @@
Disassembler::AppendRegisterNameToOutput(instr, reg);
}
-void CustomDisassembler::VisitLoadLiteral(const Instruction* instr) {
- Disassembler::VisitLoadLiteral(instr);
+void CustomDisassembler::Visit(vixl::aarch64::Metadata* metadata, const Instruction* instr) {
+ vixl::aarch64::Disassembler::Visit(metadata, instr);
+ const std::string& form = (*metadata)["form"];
+ // These regexs are long, but it is an attempt to match the mapping entry keys in the
+ // #define DEFAULT_FORM_TO_VISITOR_MAP(VISITORCLASS) in the file
+ // external/vixl/src/aarch64/decoder-visitor-map-aarch64.h
+ // for the ::VisitLoadLiteralInstr, ::VisitLoadStoreUnsignedOffset or ::VisitUnconditionalBranch
+ // function addresess key values.
+ // N.B. the mapping are many to one.
+ if (std::regex_match(form, std::regex("(ldrsw|ldr|prfm)_(32|64|d|b|h|q|s)_loadlit"))) {
+ VisitLoadLiteralInstr(instr);
+ return;
+ }
+
+ if (std::regex_match(form, std::regex(
+ "(ldrb|ldrh|ldrsb|ldrsh|ldrsw|ldr|prfm|strb|strh|str)_(32|64|d|b|h|q|s)_ldst_pos"))) {
+ VisitLoadStoreUnsignedOffsetInstr(instr);
+ return;
+ }
+
+ if (std::regex_match(form, std::regex("(bl|b)_only_branch_imm"))) {
+ VisitUnconditionalBranchInstr(instr);
+ return;
+ }
+}
+
+void CustomDisassembler::VisitLoadLiteralInstr(const Instruction* instr) {
if (!read_literals_) {
return;
}
@@ -69,6 +96,7 @@
// avoid trying to fetch invalid literals (we can encounter this when
// interpreting raw data as instructions).
void* data_address = instr->GetLiteralAddress<void*>();
+
if (data_address < base_address_ || data_address >= end_address_) {
AppendToOutput(" (?)");
return;
@@ -97,17 +125,13 @@
}
}
-void CustomDisassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) {
- Disassembler::VisitLoadStoreUnsignedOffset(instr);
-
+void CustomDisassembler::VisitLoadStoreUnsignedOffsetInstr(const Instruction* instr) {
if (instr->GetRn() == TR) {
AppendThreadOfsetName(instr);
}
}
-void CustomDisassembler::VisitUnconditionalBranch(const Instruction* instr) {
- Disassembler::VisitUnconditionalBranch(instr);
-
+void CustomDisassembler::VisitUnconditionalBranchInstr(const Instruction* instr) {
if (instr->Mask(UnconditionalBranchMask) == BL) {
const Instruction* target = instr->GetImmPCOffsetTarget();
if (target >= base_address_ &&
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index a895dfe..d0443d2 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -47,16 +47,20 @@
void AppendRegisterNameToOutput(const vixl::aarch64::Instruction* instr,
const vixl::aarch64::CPURegister& reg) override;
- // Improve the disassembly of literal load instructions.
- void VisitLoadLiteral(const vixl::aarch64::Instruction* instr) override;
-
- // Improve the disassembly of thread offset.
- void VisitLoadStoreUnsignedOffset(const vixl::aarch64::Instruction* instr) override;
-
- // Improve the disassembly of branch to thunk jumping to pointer from thread entrypoint.
- void VisitUnconditionalBranch(const vixl::aarch64::Instruction* instr) override;
+ // Intercepts the instruction flow captured by the parent method,
+ // to specially instrument for particular instruction types.
+ void Visit(vixl::aarch64::Metadata* metadata, const vixl::aarch64::Instruction* instr) override;
private:
+ // Improve the disassembly of literal load instructions.
+ void VisitLoadLiteralInstr(const vixl::aarch64::Instruction* instr);
+
+ // Improve the disassembly of thread offset.
+ void VisitLoadStoreUnsignedOffsetInstr(const vixl::aarch64::Instruction* instr);
+
+ // Improve the disassembly of branch to thunk jumping to pointer from thread entrypoint.
+ void VisitUnconditionalBranchInstr(const vixl::aarch64::Instruction* instr);
+
void AppendThreadOfsetName(const vixl::aarch64::Instruction* instr);
// Indicate if the disassembler should read data loaded from literal pools.
diff --git a/disassembler/disassembler_arm64_test.cc b/disassembler/disassembler_arm64_test.cc
new file mode 100644
index 0000000..81c067a
--- /dev/null
+++ b/disassembler/disassembler_arm64_test.cc
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 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 <inttypes.h>
+
+#include <regex>
+
+#include <sstream>
+
+#include "base/common_art_test.h"
+#include "disassembler_arm64.h"
+#include "thread.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "aarch64/disasm-aarch64.h"
+#include "aarch64/macro-assembler-aarch64.h"
+#pragma GCC diagnostic pop
+
+
+using namespace vixl::aarch64; // NOLINT(build/namespaces)
+
+namespace art {
+namespace arm64 {
+
+/**
+ * Fixture class for the ArtDisassemblerTest tests.
+ */
+class ArtDisassemblerTest : public CommonArtTest {
+ public:
+ ArtDisassemblerTest() {
+ }
+
+ void SetupAssembly(uint64_t end_address) {
+ masm.GetCPUFeatures()->Combine(vixl::CPUFeatures::All());
+
+ disamOptions.reset(new DisassemblerOptions(/* absolute_addresses= */ true,
+ reinterpret_cast<uint8_t*>(0x0),
+ reinterpret_cast<uint8_t*>(end_address),
+ /* can_read_literals_= */ true,
+ &Thread::DumpThreadOffset<PointerSize::k64>));
+ disasm.reset(new CustomDisassembler(&*disamOptions));
+ decoder.AppendVisitor(disasm.get());
+ masm.SetGenerateSimulatorCode(false);
+ }
+
+ static constexpr size_t kMaxSizeGenerated = 1024;
+
+ template <typename LamdaType>
+ void ImplantInstruction(LamdaType fn) {
+ vixl::ExactAssemblyScope guard(&masm,
+ kMaxSizeGenerated,
+ vixl::ExactAssemblyScope::kMaximumSize);
+ fn();
+ }
+
+ // Appends an instruction to the existing buffer and then
+ // attempts to match the output of that instructions disassembly
+ // against a regex expression. Fails if no match is found.
+ template <typename LamdaType>
+ void CompareInstruction(LamdaType fn, const char* EXP) {
+ ImplantInstruction(fn);
+ masm.FinalizeCode();
+
+ // This gets the last instruction in the buffer.
+ // The end address of the buffer is at the end of the last instruction.
+ // sizeof(Instruction) is 1 byte as it in an empty class.
+ // Therefore we need to go back kInstructionSize * sizeof(Instruction) bytes
+ // in order to get to the start of the last instruction.
+ const Instruction* targetInstruction =
+ masm.GetBuffer()->GetEndAddress<Instruction*>()->
+ GetInstructionAtOffset(-static_cast<signed>(kInstructionSize));
+
+ decoder.Decode(targetInstruction);
+
+ const char* disassembly = disasm->GetOutput();
+
+ if (!std::regex_match(disassembly, std::regex(EXP))) {
+ const uint32_t encoding = static_cast<uint32_t>(targetInstruction->GetInstructionBits());
+
+ printf("\nEncoding: %08" PRIx32 "\nExpected: %s\nFound: %s\n",
+ encoding,
+ EXP,
+ disassembly);
+
+ ADD_FAILURE();
+ }
+ printf("----\n%s\n", disassembly);
+ }
+
+ std::unique_ptr<CustomDisassembler> disasm;
+ std::unique_ptr<DisassemblerOptions> disamOptions;
+ Decoder decoder;
+ MacroAssembler masm;
+};
+
+#define IMPLANT(fn) \
+ do { \
+ ImplantInstruction([&]() { this->masm.fn; }); \
+ } while (0)
+
+#define COMPARE(fn, output) \
+ do { \
+ CompareInstruction([&]() { this->masm.fn; }, (output)); \
+ } while (0)
+
+// These tests map onto the named per instruction instrumentation functions in:
+// ART/art/disassembler/disassembler_arm.cc
+// Context can be found in the logic conditional on incoming instruction types and sequences in the
+// ART disassembler. As of writing the functionality we are testing for that of additional
+// diagnostic info being appended to the end of the ART disassembly output.
+TEST_F(ArtDisassemblerTest, LoadLiteralVisitBadAddress) {
+ SetupAssembly(0xffffff);
+
+ // Check we append an erroneous hint "(?)" for literal load instructions with
+ // out of scope literal pool value addresses.
+ COMPARE(ldr(x0, vixl::aarch64::Assembler::ImmLLiteral(1000)),
+ "ldr x0, pc\\+128000 \\(addr -?0x[0-9a-fA-F]+\\) \\(\\?\\)");
+}
+
+TEST_F(ArtDisassemblerTest, LoadLiteralVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ // Test that we do not append anything for ineligible instruction.
+ COMPARE(ldr(x0, MemOperand(x18, 0)), "ldr x0, \\[x18\\]$");
+
+ // Check we do append some extra info in the right text format for valid literal load instruction.
+ COMPARE(ldr(w0, vixl::aarch64::Assembler::ImmLLiteral(0)),
+ "ldr w0, pc\\+0 \\(addr -?0x[0-9a-f]+\\) \\(0x18000000 / 402653184\\)");
+ // We don't compare with exact value even though it's a known literal (the encoding of the
+ // instruction itself) since the precision of printed floating point values could change.
+ COMPARE(ldr(s0, vixl::aarch64::Assembler::ImmLLiteral(0)),
+ "ldr s0, pc\\+0 \\(addr -?0x[0-9a-f]+\\) \\([0-9]+.[0-9]+e(\\+|-)[0-9]+\\)");
+}
+
+TEST_F(ArtDisassemblerTest, LoadStoreUnsignedOffsetVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ // Test that we do not append anything for ineligible instruction.
+ COMPARE(ldr(x0, MemOperand(x18, 8)), "ldr x0, \\[x18, #8\\]$");
+ // Test that we do append the function name if the instruction is a load from the address
+ // stored in the TR register.
+ COMPARE(ldr(x0, MemOperand(x19, 8)), "ldr x0, \\[tr, #8\\] ; thin_lock_thread_id");
+}
+
+TEST_F(ArtDisassemblerTest, UnconditionalBranchNoAppendVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ vixl::aarch64::Label destination;
+ masm.Bind(&destination);
+
+ IMPLANT(ldr(x16, MemOperand(x18, 0)));
+
+ // Test that we do not append anything for ineligible instruction.
+ COMPARE(bl(&destination),
+ "bl #-0x4 \\(addr -?0x[0-9a-f]+\\)$");
+}
+
+TEST_F(ArtDisassemblerTest, UnconditionalBranchVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ vixl::aarch64::Label destination;
+ masm.Bind(&destination);
+
+ IMPLANT(ldr(x16, MemOperand(x19, 0)));
+ IMPLANT(br(x16));
+
+ // Test that we do append the function name if the instruction is a branch
+ // to a load that reads data from the address in the TR register, into the IPO register
+ // followed by a BR branching using the IPO register.
+ COMPARE(bl(&destination),
+ "bl #-0x8 \\(addr -?0x[0-9a-f]+\\) ; state_and_flags");
+}
+
+
+} // namespace arm64
+} // namespace art
diff --git a/dt_fd_forward/Android.bp b/dt_fd_forward/Android.bp
index 772b55e..e0507d5 100644
--- a/dt_fd_forward/Android.bp
+++ b/dt_fd_forward/Android.bp
@@ -39,10 +39,10 @@
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-GPL-with-classpath-exception",
+ "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
],
license_text: [
- "NOTICE",
+ "LICENSE",
],
}
diff --git a/dt_fd_forward/NOTICE b/dt_fd_forward/LICENSE
similarity index 100%
rename from dt_fd_forward/NOTICE
rename to dt_fd_forward/LICENSE
diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp
index afd86a0..a0354ab 100644
--- a/imgdiag/Android.bp
+++ b/imgdiag/Android.bp
@@ -84,6 +84,7 @@
"libartd",
"libartbased",
"libartd-compiler",
+ "libdexfiled",
],
apex_available: [
"com.android.art.debug",
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 745475e..e3310e9 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -17,11 +17,10 @@
#include <stdio.h>
#include <stdlib.h>
-#include <fstream>
#include <functional>
-#include <iostream>
#include <map>
#include <optional>
+#include <ostream>
#include <set>
#include <string>
#include <unordered_set>
@@ -73,6 +72,16 @@
kImageAndZygote
};
+std::ostream& operator<<(std::ostream& os, RemoteProcesses remotes) {
+ switch (remotes) {
+ case RemoteProcesses::kImageOnly: os << "ImageOnly"; break;
+ case RemoteProcesses::kZygoteOnly: os << "ZygoteOnly"; break;
+ case RemoteProcesses::kImageAndZygote: os << "ImageAndZygote"; break;
+ default: UNREACHABLE();
+ }
+ return os;
+}
+
struct MappingData {
// The count of pages that are considered dirty by the OS.
size_t dirty_pages = 0;
@@ -90,6 +99,8 @@
size_t false_dirty_pages = 0;
// Set of the local virtual page indices that are dirty.
std::set<size_t> dirty_page_set;
+ // Private dirty page counts for each section of the image
+ std::array<size_t, ImageHeader::kSectionCount> private_dirty_pages_for_section = {};
};
static std::string GetClassDescriptor(mirror::Class* klass)
@@ -158,7 +169,7 @@
// Store value->key so that we can use the default sort from pair which
// sorts by value first and then key
std::vector<std::pair<V, K>> value_key_vector;
-
+ value_key_vector.reserve(map.size());
for (const auto& kv_pair : map) {
value_key_vector.push_back(std::make_pair(value_mapper(kv_pair.second), kv_pair.first));
}
@@ -209,14 +220,24 @@
return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr));
}
-template <typename T> size_t EntrySize(T* entry);
-template<> size_t EntrySize(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) {
+size_t EntrySize(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) {
return object->SizeOf();
}
-template<> size_t EntrySize(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) {
+size_t EntrySize(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) {
return sizeof(*art_method);
}
+// Print all pages the entry belongs to
+void PrintEntryPages(uintptr_t entry_address, size_t entry_size, std::ostream& os) {
+ const char* tabs = " ";
+ const uintptr_t first_page_idx = entry_address / kPageSize;
+ const uintptr_t last_page_idx = RoundUp(entry_address + entry_size,
+ kObjectAlignment) / kPageSize;
+ for (uintptr_t page_idx = first_page_idx; page_idx <= last_page_idx; ++page_idx) {
+ os << tabs << "page_idx=" << page_idx << "\n";
+ }
+}
+
// entry1 and entry2 might be relocated, this means we must use the runtime image's entry
// (image_entry) to avoid crashes.
template <typename T>
@@ -268,7 +289,6 @@
size_t GetDirtyEntryBytes() const { return dirty_entry_bytes_; }
size_t GetFalseDirtyEntryCount() const { return false_dirty_entries_.size(); }
size_t GetFalseDirtyEntryBytes() const { return false_dirty_entry_bytes_; }
- size_t GetZygoteDirtyEntryCount() const { return zygote_dirty_entries_.size(); }
protected:
bool IsEntryOnDirtyPage(T* entry, const std::set<size_t>& dirty_pages) const
@@ -289,10 +309,6 @@
return false;
}
- void AddZygoteDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) {
- zygote_dirty_entries_.insert(entry);
- }
-
void AddImageDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) {
image_dirty_entries_.insert(entry);
}
@@ -327,11 +343,6 @@
// If zygote_pid_only_ == false, these are private dirty entries in the application.
std::set<T*> image_dirty_entries_;
- // Zygote dirty entries (probably private dirty).
- // We only add entries here if they differed in both the image and the zygote, so
- // they are probably private dirty.
- std::set<T*> zygote_dirty_entries_;
-
std::map<off_t /* field offset */, size_t /* count */> field_dirty_count_;
private:
@@ -439,18 +450,24 @@
void DiffEntryContents(mirror::Object* entry,
uint8_t* remote_bytes,
const uint8_t* base_ptr,
- bool log_dirty_objects)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool log_dirty_objects,
+ size_t entry_offset) REQUIRES_SHARED(Locks::mutator_lock_) {
const char* tabs = " ";
// Attempt to find fields for all dirty bytes.
mirror::Class* klass = entry->GetClass();
+ std::string temp;
if (entry->IsClass()) {
os_ << tabs
<< "Class " << mirror::Class::PrettyClass(entry->AsClass()) << " " << entry << "\n";
+ os_ << tabs << "dirty_obj: " << entry_offset << " class "
+ << entry->AsClass()->DescriptorHash() << "\n";
} else {
os_ << tabs
<< "Instance of " << mirror::Class::PrettyClass(klass) << " " << entry << "\n";
+ os_ << tabs << "dirty_obj: " << entry_offset << " instance " << klass->DescriptorHash()
+ << "\n";
}
+ PrintEntryPages(reinterpret_cast<uintptr_t>(entry), EntrySize(entry), os_);
std::unordered_set<ArtField*> dirty_instance_fields;
std::unordered_set<ArtField*> dirty_static_fields;
@@ -764,10 +781,12 @@
void DiffEntryContents(ArtMethod* method,
uint8_t* remote_bytes,
const uint8_t* base_ptr,
- bool log_dirty_objects ATTRIBUTE_UNUSED)
+ bool log_dirty_objects ATTRIBUTE_UNUSED,
+ size_t entry_offset ATTRIBUTE_UNUSED)
REQUIRES_SHARED(Locks::mutator_lock_) {
const char* tabs = " ";
os_ << tabs << "ArtMethod " << ArtMethod::PrettyMethod(method) << "\n";
+ PrintEntryPages(reinterpret_cast<uintptr_t>(method), EntrySize(method), os_);
std::unordered_set<size_t> dirty_members;
// Examine the members comprising the ArtMethod, computing which members are dirty.
@@ -1009,24 +1028,11 @@
os_ << " Application dirty entries (unknown whether private or shared dirty): ";
break;
}
- DiffDirtyEntries(ProcessType::kRemote,
+ DiffDirtyEntries(RegionCommon<T>::image_dirty_entries_,
begin_image_ptr,
RegionCommon<T>::remote_contents_,
base_ptr,
/*log_dirty_objects=*/true);
- // Print shared dirty after since it's less important.
- if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) {
- // We only reach this point if both pids were specified. Furthermore,
- // entries are only displayed here if they differed in both the image
- // and the zygote, so they are probably private dirty.
- CHECK(remotes == RemoteProcesses::kImageAndZygote);
- os_ << "\n" << " Zygote dirty entries (probably shared dirty): ";
- DiffDirtyEntries(ProcessType::kZygote,
- begin_image_ptr,
- RegionCommon<T>::zygote_contents_,
- begin_image_ptr,
- /*log_dirty_objects=*/false);
- }
RegionSpecializedBase<T>::DumpDirtyObjects();
RegionSpecializedBase<T>::DumpDirtyEntries();
RegionSpecializedBase<T>::DumpFalseDirtyEntries();
@@ -1036,25 +1042,19 @@
private:
std::ostream& os_;
- void DiffDirtyEntries(ProcessType process_type,
+ void DiffDirtyEntries(const std::set<T*>& entries,
const uint8_t* begin_image_ptr,
ArrayRef<uint8_t> contents,
const uint8_t* base_ptr,
bool log_dirty_objects)
REQUIRES_SHARED(Locks::mutator_lock_) {
os_ << RegionCommon<T>::dirty_entries_.size() << "\n";
- const std::set<T*>& entries =
- (process_type == ProcessType::kZygote) ?
- RegionCommon<T>::zygote_dirty_entries_:
- RegionCommon<T>::image_dirty_entries_;
for (T* entry : entries) {
uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry);
ptrdiff_t offset = entry_bytes - begin_image_ptr;
uint8_t* remote_bytes = &contents[offset];
- RegionSpecializedBase<T>::DiffEntryContents(entry,
- remote_bytes,
- &base_ptr[offset],
- log_dirty_objects);
+ RegionSpecializedBase<T>::DiffEntryContents(
+ entry, remote_bytes, &base_ptr[offset], log_dirty_objects, static_cast<size_t>(offset));
}
}
@@ -1077,17 +1077,11 @@
// Test private dirty first.
bool is_dirty = false;
if (have_zygote) {
- bool private_dirty = EntriesDiffer(entry, entry_zygote, entry_remote);
- if (private_dirty) {
+ if (EntriesDiffer(entry, entry_zygote, entry_remote)) {
// Private dirty, app vs zygote.
is_dirty = true;
RegionCommon<T>::AddImageDirtyEntry(entry);
}
- if (EntriesDiffer(entry, entry_zygote, entry)) {
- // Shared dirty, zygote vs image.
- is_dirty = true;
- RegionCommon<T>::AddZygoteDirtyEntry(entry);
- }
} else if (EntriesDiffer(entry, entry_remote, entry)) {
// Shared or private dirty, app vs image.
is_dirty = true;
@@ -1127,8 +1121,10 @@
bool Init() {
std::ostream& os = *os_;
- if (image_diff_pid_ < 0 && zygote_diff_pid_ < 0) {
- os << "Either --image-diff-pid or --zygote-diff-pid (or both) must be specified.\n";
+ if (image_diff_pid_ < 0 || zygote_diff_pid_ < 0) {
+ // TODO: ComputeDirtyBytes must be modified
+ // to support single app/zygote to bootimage comparison
+ os << "Both --image-diff-pid and --zygote-diff-pid must be specified.\n";
return false;
}
@@ -1203,69 +1199,14 @@
}
}
- std::unique_ptr<File> clean_pagemap_file;
std::unique_ptr<File> kpageflags_file;
std::unique_ptr<File> kpagecount_file;
- if (!open_file("/proc/self/pagemap", &clean_pagemap_file) ||
- !open_file("/proc/kpageflags", &kpageflags_file) ||
+ if (!open_file("/proc/kpageflags", &kpageflags_file) ||
!open_file("/proc/kpagecount", &kpagecount_file)) {
return false;
}
- // Note: the boot image is not really clean but close enough.
- // For now, log pages found to be dirty.
// TODO: Rewrite imgdiag to load boot image without creating a runtime.
- // FIXME: The following does not reliably detect dirty pages.
- Runtime* runtime = Runtime::Current();
- CHECK(!runtime->ShouldRelocate());
- size_t total_dirty_pages = 0u;
- for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) {
- const ImageHeader& image_header = space->GetImageHeader();
- const uint8_t* image_begin = image_header.GetImageBegin();
- const uint8_t* image_end = AlignUp(image_begin + image_header.GetImageSize(), kPageSize);
- size_t virtual_page_idx_begin = reinterpret_cast<uintptr_t>(image_begin) / kPageSize;
- size_t virtual_page_idx_end = reinterpret_cast<uintptr_t>(image_end) / kPageSize;
- size_t num_virtual_pages = virtual_page_idx_end - virtual_page_idx_begin;
-
- std::string error_msg;
- std::vector<uint64_t> page_frame_numbers(num_virtual_pages);
- if (!GetPageFrameNumbers(clean_pagemap_file.get(),
- virtual_page_idx_begin,
- ArrayRef<uint64_t>(page_frame_numbers),
- &error_msg)) {
- os << "Failed to get page frame numbers for image space " << space->GetImageLocation()
- << ", error: " << error_msg;
- return false;
- }
-
- std::vector<uint64_t> page_flags(num_virtual_pages);
- if (!GetPageFlagsOrCounts(kpageflags_file.get(),
- ArrayRef<const uint64_t>(page_frame_numbers),
- ArrayRef<uint64_t>(page_flags),
- &error_msg)) {
- os << "Failed to get page flags for image space " << space->GetImageLocation()
- << ", error: " << error_msg;
- return false;
- }
-
- size_t num_dirty_pages = 0u;
- std::optional<size_t> first_dirty_page;
- for (size_t i = 0u, size = page_flags.size(); i != size; ++i) {
- if (UNLIKELY((page_flags[i] & kPageFlagsDirtyMask) != 0u)) {
- ++num_dirty_pages;
- if (!first_dirty_page.has_value()) {
- first_dirty_page = i;
- }
- }
- }
- if (num_dirty_pages != 0u) {
- DCHECK(first_dirty_page.has_value());
- os << "Found " << num_dirty_pages << " dirty pages for " << space->GetImageLocation()
- << ", first dirty page: " << first_dirty_page.value_or(0u);
- total_dirty_pages += num_dirty_pages;
- }
- }
- os << "Found " << total_dirty_pages << " dirty pages in total ";
// Commit the mappings and files.
image_proc_maps_ = std::move(image_proc_maps);
@@ -1276,7 +1217,6 @@
zygote_mem_file_ = std::move(*zygote_mem_file);
zygote_pagemap_file_ = std::move(*zygote_pagemap_file);
}
- clean_pagemap_file_ = std::move(*clean_pagemap_file);
kpageflags_file_ = std::move(*kpageflags_file);
kpagecount_file_ = std::move(*kpagecount_file);
@@ -1313,128 +1253,110 @@
}
bool ComputeDirtyBytes(const ImageHeader& image_header,
- const uint8_t* image_begin,
const android::procinfo::MapInfo& boot_map,
ArrayRef<uint8_t> remote_contents,
- MappingData* mapping_data /*out*/) {
- std::ostream& os = *os_;
-
- size_t virtual_page_idx = 0; // Virtual page number (for an absolute memory address)
- size_t page_idx = 0; // Page index relative to 0
- size_t previous_page_idx = 0; // Previous page index relative to 0
-
-
+ ArrayRef<uint8_t> zygote_contents,
+ MappingData* mapping_data /*out*/,
+ std::string* error_msg /*out*/) {
// Iterate through one page at a time. Boot map begin/end already implicitly aligned.
for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += kPageSize) {
- ptrdiff_t offset = begin - boot_map.start;
+ const ptrdiff_t offset = begin - boot_map.start;
// We treat the image header as part of the memory map for now
// If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
// But it might still be interesting to see if any of the ImageHeader data mutated
- const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + offset;
+ const uint8_t* zygote_ptr = &zygote_contents[offset];
const uint8_t* remote_ptr = &remote_contents[offset];
- if (memcmp(local_ptr, remote_ptr, kPageSize) != 0) {
+ if (memcmp(zygote_ptr, remote_ptr, kPageSize) != 0) {
mapping_data->different_pages++;
// Count the number of 32-bit integers that are different.
for (size_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) {
const uint32_t* remote_ptr_int32 = reinterpret_cast<const uint32_t*>(remote_ptr);
- const uint32_t* local_ptr_int32 = reinterpret_cast<const uint32_t*>(local_ptr);
+ const uint32_t* zygote_ptr_int32 = reinterpret_cast<const uint32_t*>(zygote_ptr);
- if (remote_ptr_int32[i] != local_ptr_int32[i]) {
+ if (remote_ptr_int32[i] != zygote_ptr_int32[i]) {
mapping_data->different_int32s++;
}
}
+ // Count the number of bytes that are different.
+ for (size_t i = 0; i < kPageSize; ++i) {
+ if (remote_ptr[i] != zygote_ptr[i]) {
+ mapping_data->different_bytes++;
+ }
+ }
}
}
- std::vector<size_t> private_dirty_pages_for_section(ImageHeader::kSectionCount, 0u);
-
- // Iterate through one byte at a time.
- ptrdiff_t page_off_begin = image_header.GetImageBegin() - image_begin;
- for (uintptr_t begin = boot_map.start; begin != boot_map.end; ++begin) {
- previous_page_idx = page_idx;
+ for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += kPageSize) {
ptrdiff_t offset = begin - boot_map.start;
- // We treat the image header as part of the memory map for now
- // If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
- // But it might still be interesting to see if any of the ImageHeader data mutated
- const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + offset;
- const uint8_t* remote_ptr = &remote_contents[offset];
+ // Virtual page number (for an absolute memory address)
+ size_t virtual_page_idx = begin / kPageSize;
- virtual_page_idx = reinterpret_cast<uintptr_t>(local_ptr) / kPageSize;
-
- // Calculate the page index, relative to the 0th page where the image begins
- page_idx = (offset + page_off_begin) / kPageSize;
- if (*local_ptr != *remote_ptr) {
- // Track number of bytes that are different
- mapping_data->different_bytes++;
+ uint64_t page_count = 0xC0FFEE;
+ // TODO: virtual_page_idx needs to be from the same process
+ int dirtiness = (IsPageDirty(&image_pagemap_file_, // Image-diff-pid procmap
+ &zygote_pagemap_file_, // Zygote procmap
+ &kpageflags_file_,
+ &kpagecount_file_,
+ virtual_page_idx, // compare same page in image
+ virtual_page_idx, // and zygote
+ &page_count,
+ error_msg));
+ if (dirtiness < 0) {
+ return false;
+ } else if (dirtiness > 0) {
+ mapping_data->dirty_pages++;
+ mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx);
}
- // Independently count the # of dirty pages on the remote side
- size_t remote_virtual_page_idx = begin / kPageSize;
- if (previous_page_idx != page_idx) {
- uint64_t page_count = 0xC0FFEE;
- // TODO: virtual_page_idx needs to be from the same process
- std::string error_msg;
- int dirtiness = (IsPageDirty(&image_pagemap_file_, // Image-diff-pid procmap
- &clean_pagemap_file_, // Self procmap
- &kpageflags_file_,
- &kpagecount_file_,
- remote_virtual_page_idx, // potentially "dirty" page
- virtual_page_idx, // true "clean" page
- &page_count,
- &error_msg));
- if (dirtiness < 0) {
- os << error_msg;
- return false;
- } else if (dirtiness > 0) {
- mapping_data->dirty_pages++;
- mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx);
- }
+ const bool is_dirty = dirtiness > 0;
+ const bool is_private = page_count == 1;
- bool is_dirty = dirtiness > 0;
- bool is_private = page_count == 1;
+ if (is_private) {
+ mapping_data->private_pages++;
+ }
- if (page_count == 1) {
- mapping_data->private_pages++;
- }
-
- if (is_dirty && is_private) {
- mapping_data->private_dirty_pages++;
- for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
- const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
- if (image_header.GetImageSection(section).Contains(offset)) {
- ++private_dirty_pages_for_section[i];
- }
+ if (is_dirty && is_private) {
+ mapping_data->private_dirty_pages++;
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
+ if (image_header.GetImageSection(section).Contains(offset)) {
+ mapping_data->private_dirty_pages_for_section[i] += 1;
}
}
}
}
mapping_data->false_dirty_pages = mapping_data->dirty_pages - mapping_data->different_pages;
+
+ return true;
+ }
+
+ void PrintMappingData(const MappingData& mapping_data, const ImageHeader& image_header) {
+ std::ostream& os = *os_;
// Print low-level (bytes, int32s, pages) statistics.
- os << mapping_data->different_bytes << " differing bytes,\n "
- << mapping_data->different_int32s << " differing int32s,\n "
- << mapping_data->different_pages << " differing pages,\n "
- << mapping_data->dirty_pages << " pages are dirty;\n "
- << mapping_data->false_dirty_pages << " pages are false dirty;\n "
- << mapping_data->private_pages << " pages are private;\n "
- << mapping_data->private_dirty_pages << " pages are Private_Dirty\n "
+ os << mapping_data.different_bytes << " differing bytes,\n "
+ << mapping_data.different_int32s << " differing int32s,\n "
+ << mapping_data.different_pages << " differing pages,\n "
+ << mapping_data.dirty_pages << " pages are dirty;\n "
+ << mapping_data.false_dirty_pages << " pages are false dirty;\n "
+ << mapping_data.private_pages << " pages are private;\n "
+ << mapping_data.private_dirty_pages << " pages are Private_Dirty\n "
<< "\n";
- size_t total_private_dirty_pages = std::accumulate(private_dirty_pages_for_section.begin(),
- private_dirty_pages_for_section.end(),
- 0u);
+ size_t total_private_dirty_pages = std::accumulate(
+ mapping_data.private_dirty_pages_for_section.begin(),
+ mapping_data.private_dirty_pages_for_section.end(),
+ 0u);
os << "Image sections (total private dirty pages " << total_private_dirty_pages << ")\n";
for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
os << section << " " << image_header.GetImageSection(section)
- << " private dirty pages=" << private_dirty_pages_for_section[i] << "\n";
+ << " private dirty pages=" << mapping_data.private_dirty_pages_for_section[i] << "\n";
}
os << "\n";
-
- return true;
}
// Look at /proc/$pid/mem and only diff the things from there
@@ -1575,13 +1497,7 @@
// For more validation should also check the ImageHeader from the file
}
- MappingData mapping_data;
- os << "Mapping at [" << reinterpret_cast<void*>(boot_map.start) << ", "
- << reinterpret_cast<void*>(boot_map.end) << ") had:\n ";
- if (!ComputeDirtyBytes(image_header, image_begin, boot_map, remote_contents, &mapping_data)) {
- return false;
- }
RemoteProcesses remotes;
if (zygote_pid_only_) {
remotes = RemoteProcesses::kZygoteOnly;
@@ -1591,6 +1507,23 @@
remotes = RemoteProcesses::kImageOnly;
}
+ // Only app vs zygote is supported at the moment
+ CHECK_EQ(remotes, RemoteProcesses::kImageAndZygote);
+
+ MappingData mapping_data;
+ if (!ComputeDirtyBytes(image_header,
+ boot_map,
+ remote_contents,
+ zygote_contents,
+ &mapping_data,
+ &error_msg)) {
+ os << error_msg;
+ return false;
+ }
+ os << "Mapping at [" << reinterpret_cast<void*>(boot_map.start) << ", "
+ << reinterpret_cast<void*>(boot_map.end) << ") had:\n ";
+ PrintMappingData(mapping_data, image_header);
+
// Check all the mirror::Object entries in the image.
RegionData<mirror::Object> object_region_data(os_,
remote_contents,
@@ -1815,8 +1748,6 @@
// A File for reading /proc/<zygote_diff_pid_>/pagemap.
File zygote_pagemap_file_;
- // A File for reading /proc/self/pagemap.
- File clean_pagemap_file_;
// A File for reading /proc/kpageflags.
File kpageflags_file_;
// A File for reading /proc/kpagecount.
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 80c63c6..84b797b 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -39,7 +39,6 @@
"base/file_utils.cc",
"base/flags.cc",
"base/hex_dump.cc",
- "base/hiddenapi_flags.cc",
"base/logging.cc",
"base/malloc_arena_pool.cc",
"base/membarrier.cc",
@@ -66,7 +65,11 @@
"base/globals_unix.cc",
"base/mem_map_unix.cc",
],
+ static: {
+ cflags: ["-DART_STATIC_LIBARTBASE"],
+ },
static_libs: [
+ "libcap",
// ZipArchive support, the order matters here to get all symbols.
"libziparchive",
],
@@ -80,6 +83,9 @@
"libbase",
],
export_shared_lib_headers: ["libbase"], // ART's macros.h depends on libbase's macros.h.
+ export_static_lib_headers: [
+ "libcap",
+ ],
},
not_windows: {
srcs: [
@@ -101,6 +107,14 @@
],
export_shared_lib_headers: ["libbase"], // ART's macros.h depends on libbase's macros.h.
},
+ linux_glibc: {
+ static_libs: [
+ "libcap",
+ ],
+ export_static_lib_headers: [
+ "libcap",
+ ],
+ },
windows: {
srcs: [
"base/mem_map_windows.cc",
@@ -137,6 +151,18 @@
"libz",
"libziparchive",
],
+ target: {
+ android: {
+ whole_static_libs: [
+ "libcap",
+ ],
+ },
+ linux_glibc: {
+ whole_static_libs: [
+ "libcap",
+ ],
+ },
+ },
}
cc_defaults {
@@ -184,13 +210,11 @@
"com.android.art.debug",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- export_shared_lib_headers: ["libbase"],
target: {
windows: {
+ // Control the enabled property here rather than in
+ // libartbase_defaults, to ensure it overrides properties inherited
+ // from other defaults.
enabled: true,
shared: {
enabled: false,
@@ -209,13 +233,13 @@
apex_available: [
"com.android.art.debug",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- export_shared_lib_headers: ["libbase"],
+
target: {
windows: {
+ // Control the enabled property here rather than in
+ // libartbase_defaults, to ensure it overrides properties inherited
+ // from other defaults, in particular any inherited via
+ // art_debug_defaults.
enabled: true,
shared: {
enabled: false,
@@ -231,7 +255,7 @@
],
shared_libs: [
"libbase",
- "libbacktrace",
+ "libunwindstack",
],
header_libs: [
"libnativehelper_header_only",
@@ -239,11 +263,13 @@
static: {
whole_static_libs: [
"libc++fs",
+ "libcap",
],
},
shared: {
static_libs: [
"libc++fs",
+ "libcap",
],
},
}
diff --git a/libartbase/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc
index 9ec66fe..811e723 100644
--- a/libartbase/arch/instruction_set.cc
+++ b/libartbase/arch/instruction_set.cc
@@ -17,6 +17,8 @@
#include "instruction_set.h"
#include "android-base/logging.h"
+#include "android-base/properties.h"
+#include "android-base/stringprintf.h"
#include "base/bit_utils.h"
#include "base/globals.h"
@@ -27,6 +29,7 @@
case InstructionSet::kArm:
case InstructionSet::kThumb2:
case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
case InstructionSet::kNone:
@@ -44,6 +47,8 @@
return "arm";
case InstructionSet::kArm64:
return "arm64";
+ case InstructionSet::kRiscv64:
+ return "riscv64";
case InstructionSet::kX86:
return "x86";
case InstructionSet::kX86_64:
@@ -62,6 +67,8 @@
return InstructionSet::kArm;
} else if (strcmp("arm64", isa_str) == 0) {
return InstructionSet::kArm64;
+ } else if (strcmp("riscv64", isa_str) == 0) {
+ return InstructionSet::kRiscv64;
} else if (strcmp("x86", isa_str) == 0) {
return InstructionSet::kX86;
} else if (strcmp("x86_64", isa_str) == 0) {
@@ -71,24 +78,45 @@
return InstructionSet::kNone;
}
-size_t GetInstructionSetAlignment(InstructionSet isa) {
- switch (isa) {
- case InstructionSet::kArm:
- // Fall-through.
- case InstructionSet::kThumb2:
- return kArmAlignment;
- case InstructionSet::kArm64:
- return kArm64Alignment;
- case InstructionSet::kX86:
- // Fall-through.
- case InstructionSet::kX86_64:
- return kX86Alignment;
- case InstructionSet::kNone:
- LOG(FATAL) << "ISA kNone does not have alignment.";
- UNREACHABLE();
+std::vector<InstructionSet> GetSupportedInstructionSets(std::string* error_msg) {
+ std::string zygote_kinds = android::base::GetProperty("ro.zygote", {});
+ if (zygote_kinds.empty()) {
+ *error_msg = "Unable to get Zygote kinds";
+ return {};
}
- LOG(FATAL) << "Unknown ISA " << isa;
- UNREACHABLE();
+
+ switch (kRuntimeISA) {
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ if (zygote_kinds == "zygote64_32" || zygote_kinds == "zygote32_64") {
+ return {InstructionSet::kArm64, InstructionSet::kArm};
+ } else if (zygote_kinds == "zygote64") {
+ return {InstructionSet::kArm64};
+ } else if (zygote_kinds == "zygote32") {
+ return {InstructionSet::kArm};
+ } else {
+ *error_msg = android::base::StringPrintf("Unknown Zygote kinds '%s'", zygote_kinds.c_str());
+ return {};
+ }
+ case InstructionSet::kRiscv64:
+ return {InstructionSet::kRiscv64};
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
+ if (zygote_kinds == "zygote64_32" || zygote_kinds == "zygote32_64") {
+ return {InstructionSet::kX86_64, InstructionSet::kX86};
+ } else if (zygote_kinds == "zygote64") {
+ return {InstructionSet::kX86_64};
+ } else if (zygote_kinds == "zygote32") {
+ return {InstructionSet::kX86};
+ } else {
+ *error_msg = android::base::StringPrintf("Unknown Zygote kinds '%s'", zygote_kinds.c_str());
+ return {};
+ }
+ default:
+ *error_msg = android::base::StringPrintf("Unknown runtime ISA '%s'",
+ GetInstructionSetString(kRuntimeISA));
+ return {};
+ }
}
namespace instruction_set_details {
diff --git a/libartbase/arch/instruction_set.h b/libartbase/arch/instruction_set.h
index faf881d..8d59f1b 100644
--- a/libartbase/arch/instruction_set.h
+++ b/libartbase/arch/instruction_set.h
@@ -19,6 +19,7 @@
#include <iosfwd>
#include <string>
+#include <vector>
#include "base/enums.h"
#include "base/macros.h"
@@ -30,6 +31,7 @@
kArm,
kArm64,
kThumb2,
+ kRiscv64,
kX86,
kX86_64,
kLast = kX86_64
@@ -40,6 +42,8 @@
static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm;
#elif defined(__aarch64__)
static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm64;
+#elif defined (__riscv)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kRiscv64;
#elif defined(__i386__)
static constexpr InstructionSet kRuntimeISA = InstructionSet::kX86;
#elif defined(__x86_64__)
@@ -51,25 +55,29 @@
// Architecture-specific pointer sizes
static constexpr PointerSize kArmPointerSize = PointerSize::k32;
static constexpr PointerSize kArm64PointerSize = PointerSize::k64;
+static constexpr PointerSize kRiscv64PointerSize = PointerSize::k64;
static constexpr PointerSize kX86PointerSize = PointerSize::k32;
static constexpr PointerSize kX86_64PointerSize = PointerSize::k64;
-// ARM instruction alignment. ARM processors require code to be 4-byte aligned,
-// but ARM ELF requires 8..
-static constexpr size_t kArmAlignment = 8;
-
-// ARM64 instruction alignment. This is the recommended alignment for maximum performance.
-static constexpr size_t kArm64Alignment = 16;
-
// ARM64 default SVE vector length.
static constexpr size_t kArm64DefaultSVEVectorLength = 256;
-// X86 instruction alignment. This is the recommended alignment for maximum performance.
-static constexpr size_t kX86Alignment = 16;
+// Code alignment (used for the first instruction of a subroutine, such as an entrypoint).
+// This is the recommended alignment for maximum performance.
+// ARM processors require code to be 4-byte aligned, but ARM ELF requires 8.
+static constexpr size_t kArmCodeAlignment = 8;
+static constexpr size_t kArm64CodeAlignment = 16;
+static constexpr size_t kRiscv64CodeAlignment = 16;
+static constexpr size_t kX86CodeAlignment = 16;
-// Different than code alignment since code alignment is only first instruction of method.
+// Instruction alignment (every instruction must be aligned at this boundary). This differs from
+// code alignment, which applies only to the first instruction of a subroutine.
+// Android requires the RISC-V compressed instruction extension, and that allows
+// *all* instructions (not just compressed ones) to be 2-byte aligned rather
+// than the usual 4-byte alignment requirement.
static constexpr size_t kThumb2InstructionAlignment = 2;
static constexpr size_t kArm64InstructionAlignment = 4;
+static constexpr size_t kRiscv64InstructionAlignment = 2;
static constexpr size_t kX86InstructionAlignment = 1;
static constexpr size_t kX86_64InstructionAlignment = 1;
@@ -89,6 +97,8 @@
return kArmPointerSize;
case InstructionSet::kArm64:
return kArm64PointerSize;
+ case InstructionSet::kRiscv64:
+ return kRiscv64PointerSize;
case InstructionSet::kX86:
return kX86PointerSize;
case InstructionSet::kX86_64:
@@ -100,30 +110,12 @@
InstructionSetAbort(isa);
}
-constexpr size_t GetInstructionSetInstructionAlignment(InstructionSet isa) {
- switch (isa) {
- case InstructionSet::kArm:
- // Fall-through.
- case InstructionSet::kThumb2:
- return kThumb2InstructionAlignment;
- case InstructionSet::kArm64:
- return kArm64InstructionAlignment;
- case InstructionSet::kX86:
- return kX86InstructionAlignment;
- case InstructionSet::kX86_64:
- return kX86_64InstructionAlignment;
-
- case InstructionSet::kNone:
- break;
- }
- InstructionSetAbort(isa);
-}
-
constexpr bool IsValidInstructionSet(InstructionSet isa) {
switch (isa) {
case InstructionSet::kArm:
case InstructionSet::kThumb2:
case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
return true;
@@ -134,7 +126,68 @@
return false;
}
-size_t GetInstructionSetAlignment(InstructionSet isa);
+constexpr size_t GetInstructionSetInstructionAlignment(InstructionSet isa) {
+ switch (isa) {
+ case InstructionSet::kArm:
+ // Fall-through.
+ case InstructionSet::kThumb2:
+ return kThumb2InstructionAlignment;
+ case InstructionSet::kArm64:
+ return kArm64InstructionAlignment;
+ case InstructionSet::kRiscv64:
+ return kRiscv64InstructionAlignment;
+ case InstructionSet::kX86:
+ return kX86InstructionAlignment;
+ case InstructionSet::kX86_64:
+ return kX86_64InstructionAlignment;
+
+ case InstructionSet::kNone:
+ break;
+ }
+ InstructionSetAbort(isa);
+}
+
+constexpr size_t GetInstructionSetCodeAlignment(InstructionSet isa) {
+ switch (isa) {
+ case InstructionSet::kArm:
+ // Fall-through.
+ case InstructionSet::kThumb2:
+ return kArmCodeAlignment;
+ case InstructionSet::kArm64:
+ return kArm64CodeAlignment;
+ case InstructionSet::kRiscv64:
+ return kRiscv64CodeAlignment;
+ case InstructionSet::kX86:
+ // Fall-through.
+ case InstructionSet::kX86_64:
+ return kX86CodeAlignment;
+
+ case InstructionSet::kNone:
+ break;
+ }
+ InstructionSetAbort(isa);
+}
+
+// Returns the difference between the code address and a usable PC.
+// Mainly to cope with `kThumb2` where the lower bit must be set.
+constexpr size_t GetInstructionSetEntryPointAdjustment(InstructionSet isa) {
+ switch (isa) {
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
+ return 0;
+ case InstructionSet::kThumb2: {
+ // +1 to set the low-order bit so a BLX will switch to Thumb mode
+ return 1;
+ }
+
+ case InstructionSet::kNone:
+ break;
+ }
+ InstructionSetAbort(isa);
+}
constexpr bool Is64BitInstructionSet(InstructionSet isa) {
switch (isa) {
@@ -144,6 +197,7 @@
return false;
case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86_64:
return true;
@@ -165,6 +219,8 @@
return 4;
case InstructionSet::kArm64:
return 8;
+ case InstructionSet::kRiscv64:
+ return 8;
case InstructionSet::kX86:
return 4;
case InstructionSet::kX86_64:
@@ -184,6 +240,8 @@
return 4;
case InstructionSet::kArm64:
return 8;
+ case InstructionSet::kRiscv64:
+ return 8;
case InstructionSet::kX86:
return 8;
case InstructionSet::kX86_64:
@@ -195,17 +253,22 @@
InstructionSetAbort(isa);
}
+// Returns the instruction sets supported by the device, or an empty list on failure.
+std::vector<InstructionSet> GetSupportedInstructionSets(std::string* error_msg);
+
namespace instruction_set_details {
#if !defined(ART_STACK_OVERFLOW_GAP_arm) || !defined(ART_STACK_OVERFLOW_GAP_arm64) || \
+ !defined(ART_STACK_OVERFLOW_GAP_riscv64) || \
!defined(ART_STACK_OVERFLOW_GAP_x86) || !defined(ART_STACK_OVERFLOW_GAP_x86_64)
#error "Missing defines for stack overflow gap"
#endif
-static constexpr size_t kArmStackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm;
-static constexpr size_t kArm64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm64;
-static constexpr size_t kX86StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86;
-static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
+static constexpr size_t kArmStackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm;
+static constexpr size_t kArm64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm64;
+static constexpr size_t kRiscv64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_riscv64;
+static constexpr size_t kX86StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86;
+static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
NO_RETURN void GetStackOverflowReservedBytesFailure(const char* error_msg);
@@ -221,6 +284,9 @@
case InstructionSet::kArm64:
return instruction_set_details::kArm64StackOverflowReservedBytes;
+ case InstructionSet::kRiscv64:
+ return instruction_set_details::kRiscv64StackOverflowReservedBytes;
+
case InstructionSet::kX86:
return instruction_set_details::kX86StackOverflowReservedBytes;
diff --git a/libartbase/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc
index 69c8d0b..d38a64e 100644
--- a/libartbase/base/arena_allocator.cc
+++ b/libartbase/base/arena_allocator.cc
@@ -73,6 +73,7 @@
"LSE ",
"CFRE ",
"LICM ",
+ "WBE ",
"LoopOpt ",
"SsaLiveness ",
"SsaPhiElim ",
diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h
index 3dfeebe..c4f713a 100644
--- a/libartbase/base/arena_allocator.h
+++ b/libartbase/base/arena_allocator.h
@@ -84,6 +84,7 @@
kArenaAllocLSE,
kArenaAllocCFRE,
kArenaAllocLICM,
+ kArenaAllocWBE,
kArenaAllocLoopOptimization,
kArenaAllocSsaLiveness,
kArenaAllocSsaPhiElimination,
diff --git a/libartbase/base/array_slice.h b/libartbase/base/array_slice.h
index a58ff44..067d9f2 100644
--- a/libartbase/base/array_slice.h
+++ b/libartbase/base/array_slice.h
@@ -65,9 +65,9 @@
lpa != nullptr ? lpa->size() : 0,
element_size) {}
ArraySlice(const ArraySlice<T>&) = default;
- ArraySlice(ArraySlice<T>&&) = default;
+ ArraySlice(ArraySlice<T>&&) noexcept = default;
ArraySlice<T>& operator=(const ArraySlice<T>&) = default;
- ArraySlice<T>& operator=(ArraySlice<T>&&) = default;
+ ArraySlice<T>& operator=(ArraySlice<T>&&) noexcept = default;
// Iterators.
iterator begin() { return iterator(&AtUnchecked(0), element_size_); }
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index c5224a5..baac2f5 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -324,8 +324,37 @@
size_t bit_size_ = 0;
};
-constexpr uint32_t kVarintBits = 4; // Minimum number of bits used for varint.
-constexpr uint32_t kVarintMax = 11; // Maximum value which is stored "inline".
+// Minimum number of bits used for varint. A varint represents either a value stored "inline" or
+// the number of bytes that are required to encode the value.
+constexpr uint32_t kVarintBits = 4;
+// Maximum value which is stored "inline". We use the rest of the values to encode the number of
+// bytes required to encode the value when the value is greater than kVarintMax.
+// We encode any value less than or equal to 11 inline. We use 12, 13, 14 and 15
+// to represent that the value is encoded in 1, 2, 3 and 4 bytes respectively.
+//
+// For example if we want to encode 1, 15, 16, 7, 11, 256:
+//
+// Low numbers (1, 7, 11) are encoded inline. 15 and 12 are set with 12 to show
+// we need to load one byte for each to have their real values (15 and 12), and
+// 256 is set with 13 to show we need to load two bytes. This is done to
+// compress the values in the bit array and keep the size down. Where the actual value
+// is read from depends on the use case.
+//
+// Values greater than kVarintMax could be encoded as a separate list referred
+// to as InterleavedVarints (see ReadInterleavedVarints / WriteInterleavedVarints).
+// This is used when there are fixed number of fields like CodeInfo headers.
+// In our example the interleaved encoding looks like below:
+//
+// Meaning: 1--- 15-- 12-- 7--- 11-- 256- 15------- 12------- 256----------------
+// Bits: 0001 1100 1100 0111 1011 1101 0000 1111 0000 1100 0000 0001 0000 0000
+//
+// In other cases the value is recorded just following the size encoding. This is
+// referred as consecutive encoding (See ReadVarint / WriteVarint). In our
+// example the consecutively encoded varints looks like below:
+//
+// Meaning: 1--- 15-- 15------- 12-- 12------- 7--- 11-- 256- 256----------------
+// Bits: 0001 1100 0000 1100 1100 0000 1100 0111 1011 1101 0000 0001 0000 0000
+constexpr uint32_t kVarintMax = 11;
class BitMemoryReader {
public:
diff --git a/libartbase/base/casts.h b/libartbase/base/casts.h
index c88f589..70a7035 100644
--- a/libartbase/base/casts.h
+++ b/libartbase/base/casts.h
@@ -57,17 +57,7 @@
// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
// how do you know the pointer is really of type SubclassOfFoo? It
// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
-// when you downcast, you should use this macro. In debug mode, we
-// use dynamic_cast<> to double-check the downcast is legal (we die
-// if it's not). In normal mode, we do the efficient static_cast<>
-// instead. Thus, it's important to test in debug mode to make sure
-// the cast is legal!
-// This is the only place in the code we should use dynamic_cast<>.
-// In particular, you SHOULDN'T be using dynamic_cast<> in order to
-// do RTTI (eg code like this:
-// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
-// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
-// You should design the code some other way not to need this.
+// when you downcast, you should use this macro.
template<typename To, typename From> // use like this: down_cast<T*>(foo);
inline To down_cast(From* f) { // so we only accept pointers
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index bef5a39..8e634ff 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -22,14 +22,17 @@
#include <ftw.h>
#include <libgen.h>
#include <stdlib.h>
+#include <sys/capability.h>
#include <unistd.h>
#include <cstdio>
#include <filesystem>
+#include <functional>
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/process.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
@@ -41,6 +44,7 @@
#include "base/mutex.h"
#include "base/os.h"
#include "base/runtime_debug.h"
+#include "base/scoped_cap.h"
#include "base/stl_util.h"
#include "base/string_view_cpp20.h"
#include "base/testing.h"
@@ -137,6 +141,29 @@
CHECK_EQ(0, unlink_result);
}
+// Temporarily drops all root capabilities when the test is run as root. This is a noop otherwise.
+android::base::ScopeGuard<std::function<void()>> ScopedUnroot() {
+ ScopedCap old_cap(cap_get_proc());
+ CHECK_NE(old_cap.Get(), nullptr);
+ ScopedCap new_cap(cap_dup(old_cap.Get()));
+ CHECK_NE(new_cap.Get(), nullptr);
+ CHECK_EQ(cap_clear_flag(new_cap.Get(), CAP_EFFECTIVE), 0);
+ CHECK_EQ(cap_set_proc(new_cap.Get()), 0);
+ // `old_cap` is actually not shared with anyone else, but we have to wrap it with a `shared_ptr`
+ // because `std::function` requires captures to be copyable.
+ return android::base::make_scope_guard(
+ [old_cap = std::make_shared<ScopedCap>(std::move(old_cap))]() {
+ CHECK_EQ(cap_set_proc(old_cap->Get()), 0);
+ });
+}
+
+// Temporarily drops write permission on a file/directory.
+android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::string& path) {
+ std::filesystem::perms old_perms = std::filesystem::status(path).permissions();
+ std::filesystem::permissions(path, std::filesystem::perms::none);
+ return android::base::make_scope_guard([=]() { std::filesystem::permissions(path, old_perms); });
+}
+
std::string CommonArtTestImpl::GetAndroidBuildTop() {
CHECK(IsHost());
std::string android_build_top;
@@ -305,7 +332,7 @@
android_system_ext_.append("/system_ext");
int mkdir_result = mkdir(android_system_ext_.c_str(), 0700);
ASSERT_EQ(mkdir_result, 0);
- setenv("ANDROID_SYSTEM_EXT", android_system_ext_.c_str(), 1);
+ setenv("SYSTEM_EXT_ROOT", android_system_ext_.c_str(), 1);
std::string system_ext_framework = android_system_ext_ + "/framework";
mkdir_result = mkdir(system_ext_framework.c_str(), 0700);
@@ -584,6 +611,7 @@
result.stage = ForkAndExecResult::kLink;
std::vector<const char*> c_args;
+ c_args.reserve(argv.size() + 1);
for (const std::string& str : argv) {
c_args.push_back(str.c_str());
}
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index bd06043..f4935cc 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -25,6 +25,7 @@
#include <vector>
#include "android-base/logging.h"
+#include "android-base/scopeguard.h"
#include "base/file_utils.h"
#include "base/globals.h"
#include "base/memory_tool.h"
@@ -122,6 +123,12 @@
DISALLOW_COPY_AND_ASSIGN(ScopedUnsetEnvironmentVariable);
};
+// Temporarily drops all root capabilities when the test is run as root. This is a noop otherwise.
+android::base::ScopeGuard<std::function<void()>> ScopedUnroot();
+
+// Temporarily drops all permissions on a file/directory.
+android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::string& path);
+
class CommonArtTestImpl {
public:
CommonArtTestImpl() = default;
diff --git a/libartbase/base/data_hash.h b/libartbase/base/data_hash.h
index 3399899..ccb8736 100644
--- a/libartbase/base/data_hash.h
+++ b/libartbase/base/data_hash.h
@@ -44,7 +44,7 @@
uint32_t hash = Murmur3Start();
const size_t nblocks = length_in_bytes / 4;
- typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+ using unaligned_uint32_t __attribute__((__aligned__(1))) = uint32_t;
const unaligned_uint32_t* blocks = reinterpret_cast<const unaligned_uint32_t*>(data);
for (size_t i = 0; i != nblocks; ++i) {
hash = Murmur3Update(hash, blocks[i]);
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 0b670e7..2396289 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -69,7 +69,7 @@
static constexpr const char* kClassesDex = "classes.dex";
static constexpr const char* kAndroidRootEnvVar = "ANDROID_ROOT";
static constexpr const char* kAndroidRootDefaultPath = "/system";
-static constexpr const char* kAndroidSystemExtRootEnvVar = "ANDROID_SYSTEM_EXT";
+static constexpr const char* kAndroidSystemExtRootEnvVar = "SYSTEM_EXT_ROOT";
static constexpr const char* kAndroidSystemExtRootDefaultPath = "/system_ext";
static constexpr const char* kAndroidDataEnvVar = "ANDROID_DATA";
static constexpr const char* kAndroidDataDefaultPath = "/data";
@@ -106,56 +106,6 @@
return "";
}
-std::string GetAndroidRootSafe(std::string* error_msg) {
-#ifdef _WIN32
- UNUSED(kAndroidRootEnvVar, kAndroidRootDefaultPath, GetRootContainingLibartbase);
- *error_msg = "GetAndroidRootSafe unsupported for Windows.";
- return "";
-#else
- // Prefer ANDROID_ROOT if it's set.
- const char* android_root_from_env = getenv(kAndroidRootEnvVar);
- if (android_root_from_env != nullptr) {
- if (!OS::DirectoryExists(android_root_from_env)) {
- *error_msg =
- StringPrintf("Failed to find %s directory %s", kAndroidRootEnvVar, android_root_from_env);
- return "";
- }
- return android_root_from_env;
- }
-
- // On host, libartbase is currently installed in "$ANDROID_ROOT/lib"
- // (e.g. something like "$ANDROID_BUILD_TOP/out/host/linux-x86/lib". Use this
- // information to infer the location of the Android Root (on host only).
- //
- // Note that this could change in the future, if we decided to install ART
- // artifacts in a different location, e.g. within an "ART APEX" directory.
- if (!kIsTargetBuild) {
- std::string root_containing_libartbase = GetRootContainingLibartbase();
- if (!root_containing_libartbase.empty()) {
- return root_containing_libartbase;
- }
- }
-
- // Try the default path.
- if (!OS::DirectoryExists(kAndroidRootDefaultPath)) {
- *error_msg =
- StringPrintf("Failed to find default Android Root directory %s", kAndroidRootDefaultPath);
- return "";
- }
- return kAndroidRootDefaultPath;
-#endif
-}
-
-std::string GetAndroidRoot() {
- std::string error_msg;
- std::string ret = GetAndroidRootSafe(&error_msg);
- if (ret.empty()) {
- LOG(FATAL) << error_msg;
- UNREACHABLE();
- }
- return ret;
-}
-
static const char* GetAndroidDirSafe(const char* env_var,
const char* default_dir,
bool must_exist,
@@ -189,6 +139,62 @@
}
}
+std::string GetAndroidRootSafe(std::string* error_msg) {
+#ifdef _WIN32
+ UNUSED(kAndroidRootEnvVar, kAndroidRootDefaultPath, GetRootContainingLibartbase);
+ *error_msg = "GetAndroidRootSafe unsupported for Windows.";
+ return "";
+#else
+ std::string local_error_msg;
+ const char* dir = GetAndroidDirSafe(kAndroidRootEnvVar, kAndroidRootDefaultPath,
+ /*must_exist=*/ true, &local_error_msg);
+ if (dir == nullptr) {
+ // On host, libartbase is currently installed in "$ANDROID_ROOT/lib"
+ // (e.g. something like "$ANDROID_BUILD_TOP/out/host/linux-x86/lib". Use this
+ // information to infer the location of the Android Root (on host only).
+ //
+ // Note that this could change in the future, if we decided to install ART
+ // artifacts in a different location, e.g. within an "ART APEX" directory.
+ if (!kIsTargetBuild) {
+ std::string root_containing_libartbase = GetRootContainingLibartbase();
+ if (!root_containing_libartbase.empty()) {
+ return root_containing_libartbase;
+ }
+ }
+ *error_msg = std::move(local_error_msg);
+ return "";
+ }
+
+ return dir;
+#endif
+}
+
+std::string GetAndroidRoot() {
+ std::string error_msg;
+ std::string ret = GetAndroidRootSafe(&error_msg);
+ CHECK(!ret.empty()) << error_msg;
+ return ret;
+}
+
+std::string GetSystemExtRootSafe(std::string* error_msg) {
+#ifdef _WIN32
+ UNUSED(kAndroidSystemExtRootEnvVar, kAndroidSystemExtRootDefaultPath);
+ *error_msg = "GetSystemExtRootSafe unsupported for Windows.";
+ return "";
+#else
+ const char* dir = GetAndroidDirSafe(kAndroidSystemExtRootEnvVar, kAndroidSystemExtRootDefaultPath,
+ /*must_exist=*/ true, error_msg);
+ return dir ? dir : "";
+#endif
+}
+
+std::string GetSystemExtRoot() {
+ std::string error_msg;
+ std::string ret = GetSystemExtRootSafe(&error_msg);
+ CHECK(!ret.empty()) << error_msg;
+ return ret;
+}
+
static std::string GetArtRootSafe(bool must_exist, /*out*/ std::string* error_msg) {
#ifdef _WIN32
UNUSED(kAndroidArtRootEnvVar, kAndroidArtApexDefaultPath, GetRootContainingLibartbase);
@@ -365,6 +371,12 @@
return GetDefaultBootImageLocation(android_root, /*deny_art_apex_data_files=*/false);
}
+std::string GetJitZygoteBootImageLocation() {
+ // Intentionally use a non-existing location so that the runtime will fail to find the boot image
+ // and JIT bootclasspath with the given profiles.
+ return "/nonx/boot.art!/apex/com.android.art/etc/boot-image.prof!/system/etc/boot-image.prof";
+}
+
static /*constinit*/ std::string_view dalvik_cache_sub_dir = "dalvik-cache";
void OverrideDalvikCacheSubDirectory(std::string sub_dir) {
@@ -679,8 +691,27 @@
#endif
}
+bool LocationIsOnSystemExt(const std::string& location) {
+#ifdef _WIN32
+ UNUSED(location);
+ LOG(FATAL) << "LocationIsOnSystemExt is unsupported on Windows.";
+ return false;
+#else
+ return IsLocationOn(location,
+ kAndroidSystemExtRootEnvVar,
+ kAndroidSystemExtRootDefaultPath) ||
+ // When the 'system_ext' partition is not present, builds will create
+ // '/system/system_ext' instead.
+ IsLocationOn(location,
+ kAndroidRootEnvVar,
+ kAndroidRootDefaultPath,
+ /* subdir= */ "system_ext/");
+#endif
+}
+
bool LocationIsTrusted(const std::string& location, bool trust_art_apex_data_files) {
- if (LocationIsOnSystem(location) || LocationIsOnArtModule(location)) {
+ if (LocationIsOnSystem(location) || LocationIsOnSystemExt(location)
+ || LocationIsOnArtModule(location)) {
return true;
}
return LocationIsOnArtApexData(location) & trust_art_apex_data_files;
@@ -704,7 +735,7 @@
#if defined(__linux__)
return fcntl(fd, F_DUPFD_CLOEXEC, 0);
#else
- return dup(fd);
+ return dup(fd); // NOLINT
#endif
}
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index c1c45bc..f539f5f 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -47,6 +47,11 @@
// Find $ANDROID_ROOT, /system, or return an empty string.
std::string GetAndroidRootSafe(/*out*/ std::string* error_msg);
+// Find $SYSTEM_EXT_ROOT, /system_ext, or abort.
+std::string GetSystemExtRoot();
+// Find $SYSTEM_EXT_ROOT, /system_ext, or return an empty string.
+std::string GetSystemExtRootSafe(/*out*/ std::string* error_msg);
+
// These methods return the ART Root, which is the location of the (activated)
// ART APEX module. On target, this is normally "/apex/com.android.art". On
// host, this is usually a subdirectory of the Android Root, e.g.
@@ -81,6 +86,9 @@
std::string GetDefaultBootImageLocation(const std::string& android_root,
bool deny_art_apex_data_files);
+// Returns the boot image location that forces the runtime to run in JIT Zygote mode.
+std::string GetJitZygoteBootImageLocation();
+
// Allows the name to be used for the dalvik cache directory (normally "dalvik-cache") to be
// overridden with a new value.
void OverrideDalvikCacheSubDirectory(std::string sub_dir);
@@ -160,6 +168,9 @@
// Return whether the location is on system (i.e. android root).
bool LocationIsOnSystem(const std::string& location);
+// Return whether the location is on system_ext
+bool LocationIsOnSystemExt(const std::string& location);
+
// Return whether the location is on system/framework (i.e. $ANDROID_ROOT/framework).
bool LocationIsOnSystemFramework(std::string_view location);
diff --git a/libartbase/base/flags.h b/libartbase/base/flags.h
index fc568b2..4c38fba 100644
--- a/libartbase/base/flags.h
+++ b/libartbase/base/flags.h
@@ -236,8 +236,8 @@
//
// Flag<int> WriteMetricsToLog{"my-feature-test.flag", 42, FlagType::kDeviceConfig};
//
-// This creates a boolean flag that can be read through gFlags.WriteMetricsToLog(). The default
-// value is false. Note that the default value can be left unspecified, in which the value of the
+// This creates an integer flag that can be read through gFlags.WriteMetricsToLog(). The default
+// value is 42. Note that the default value can be left unspecified, in which case the value of the
// type's default constructor will be used.
//
// The flag can be set through the following generated means:
diff --git a/libartbase/base/hash_set.h b/libartbase/base/hash_set.h
index c4af1b6..3f3c8f2 100644
--- a/libartbase/base/hash_set.h
+++ b/libartbase/base/hash_set.h
@@ -139,6 +139,17 @@
}
};
+template <>
+class DefaultEmptyFn<std::string> {
+ public:
+ void MakeEmpty(std::string& item) const {
+ item = std::string();
+ }
+ bool IsEmpty(const std::string& item) const {
+ return item.empty();
+ }
+};
+
template <class T>
using DefaultHashFn = std::conditional_t<std::is_same_v<T, std::string>, DataHash, std::hash<T>>;
diff --git a/libartbase/base/hiddenapi_flags.cc b/libartbase/base/hiddenapi_flags.cc
deleted file mode 100644
index ea57cb7..0000000
--- a/libartbase/base/hiddenapi_flags.cc
+++ /dev/null
@@ -1,27 +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.
- */
-
-#include "hiddenapi_flags.h"
-
-namespace art {
-namespace hiddenapi {
-
-constexpr const char* ApiList::kValueNames[ApiList::kValueCount];
-constexpr const char* ApiList::kDomainApiNames[ApiList::kDomainApiCount];
-constexpr SdkVersion ApiList::kMaxSdkVersions[ApiList::kValueCount];
-
-} // namespace hiddenapi
-} // namespace art
diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h
index 7a421a4..ef03894 100644
--- a/libartbase/base/logging.h
+++ b/libartbase/base/logging.h
@@ -56,7 +56,7 @@
bool verifier;
bool verifier_debug; // Only works in debug builds.
bool image;
- bool systrace_lock_logging; // Enabled with "-verbose:sys-locks".
+ bool systrace_lock_logging; // Enabled with "-verbose:systrace-locks".
bool agents;
bool dex; // Some dex access output etc.
bool plugin; // Used by some plugins.
diff --git a/libartbase/base/macros.h b/libartbase/base/macros.h
index eec73cb..13e87d7 100644
--- a/libartbase/base/macros.h
+++ b/libartbase/base/macros.h
@@ -75,6 +75,8 @@
#define FLATTEN __attribute__ ((flatten))
#endif
+#define NO_STACK_PROTECTOR __attribute__ ((no_stack_protector))
+
// clang doesn't like attributes on lambda functions. It would be nice to say:
// #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE
#define ALWAYS_INLINE_LAMBDA
diff --git a/libartbase/base/mem_map_windows.cc b/libartbase/base/mem_map_windows.cc
index 84e14ea..dfa75a1 100644
--- a/libartbase/base/mem_map_windows.cc
+++ b/libartbase/base/mem_map_windows.cc
@@ -31,7 +31,6 @@
namespace art {
-using android::base::MappedFile;
using android::base::StringPrintf;
static off_t allocation_granularity;
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index 9d92ed9..ee98c04 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -117,26 +117,27 @@
};
// Names come from PackageManagerServiceCompilerMapping.java
-#define REASON_NAME_LIST(V) \
- V(kError, "error") \
- V(kUnknown, "unknown") \
- V(kFirstBoot, "first-boot") \
- V(kBootAfterOTA, "boot-after-ota") \
- V(kPostBoot, "post-boot") \
- V(kInstall, "install") \
- V(kInstallFast, "install-fast") \
- V(kInstallBulk, "install-bulk") \
- V(kInstallBulkSecondary, "install-bulk-secondary") \
- V(kInstallBulkDowngraded, "install-bulk-downgraded") \
+#define REASON_NAME_LIST(V) \
+ V(kError, "error") \
+ V(kUnknown, "unknown") \
+ V(kFirstBoot, "first-boot") \
+ V(kBootAfterOTA, "boot-after-ota") \
+ V(kPostBoot, "post-boot") \
+ V(kInstall, "install") \
+ V(kInstallFast, "install-fast") \
+ V(kInstallBulk, "install-bulk") \
+ V(kInstallBulkSecondary, "install-bulk-secondary") \
+ V(kInstallBulkDowngraded, "install-bulk-downgraded") \
V(kInstallBulkSecondaryDowngraded, "install-bulk-secondary-downgraded") \
- V(kBgDexopt, "bg-dexopt") \
- V(kABOTA, "ab-ota") \
- V(kInactive, "inactive") \
- V(kShared, "shared") \
- V(kInstallWithDexMetadata, "install-with-dex-metadata") \
- V(kPrebuilt, "prebuilt") \
- V(kCmdLine, "cmdline") \
- V(kVdex, "vdex")
+ V(kBgDexopt, "bg-dexopt") \
+ V(kABOTA, "ab-ota") \
+ V(kInactive, "inactive") \
+ V(kShared, "shared") \
+ V(kInstallWithDexMetadata, "install-with-dex-metadata") \
+ V(kPrebuilt, "prebuilt") \
+ V(kCmdLine, "cmdline") \
+ V(kVdex, "vdex") \
+ V(kBootAfterMainlineUpdate, "boot-after-mainline-update")
// We log compilation reasons as part of the metadata we report. Since elsewhere compilation reasons
// are specified as a string, we define them as an enum here which indicates the reasons that we
@@ -150,7 +151,7 @@
#define REASON_NAME(kind, kind_name) \
case CompilationReason::kind: return kind_name;
#define REASON_FROM_NAME(kind, kind_name) \
- if (name == kind_name) { return CompilationReason::kind; }
+ if (name == (kind_name)) { return CompilationReason::kind; }
constexpr const char* CompilationReasonName(CompilationReason reason) {
switch (reason) {
@@ -164,7 +165,7 @@
}
#undef REASON_NAME
-#undef ReasonFromName
+#undef REASON_FROM_NAME
#define COMPILER_FILTER_REPORTING_LIST(V) \
V(kError, "error") /* Error (invalid value) condition */ \
@@ -191,7 +192,7 @@
#define FILTER_NAME(kind, kind_name) \
case CompilerFilterReporting::kind: return kind_name;
#define FILTER_FROM_NAME(kind, kind_name) \
- if (name == kind_name) { return CompilerFilterReporting::kind; }
+ if (name == (kind_name)) { return CompilerFilterReporting::kind; }
constexpr const char* CompilerFilterReportingName(CompilerFilterReporting filter) {
switch (filter) {
diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc
index 2732088..6c4aa95 100644
--- a/libartbase/base/metrics/metrics_common.cc
+++ b/libartbase/base/metrics/metrics_common.cc
@@ -321,6 +321,11 @@
CompilationReason::kPrebuilt);
static_assert(CompilationReasonFromName(CompilationReasonName(CompilationReason::kCmdLine)) ==
CompilationReason::kCmdLine);
+static_assert(CompilationReasonFromName(CompilationReasonName(CompilationReason::kVdex)) ==
+ CompilationReason::kVdex);
+static_assert(
+ CompilationReasonFromName(CompilationReasonName(CompilationReason::kBootAfterMainlineUpdate)) ==
+ CompilationReason::kBootAfterMainlineUpdate);
} // namespace metrics
} // namespace art
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index 7dc3f40..b17acf5 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -737,6 +737,10 @@
CompilationReason::kCmdLine);
ASSERT_EQ(CompilationReasonFromName("error"),
CompilationReason::kError);
+ ASSERT_EQ(CompilationReasonFromName("vdex"),
+ CompilationReason::kVdex);
+ ASSERT_EQ(CompilationReasonFromName("boot-after-mainline-update"),
+ CompilationReason::kBootAfterMainlineUpdate);
}
TEST(CompilerReason, Name) {
@@ -776,6 +780,10 @@
"cmdline");
ASSERT_EQ(CompilationReasonName(CompilationReason::kError),
"error");
+ ASSERT_EQ(CompilationReasonName(CompilationReason::kVdex),
+ "vdex");
+ ASSERT_EQ(CompilationReasonName(CompilationReason::kBootAfterMainlineUpdate),
+ "boot-after-mainline-update");
}
} // namespace metrics
} // namespace art
diff --git a/libartbase/base/safe_copy.cc b/libartbase/base/safe_copy.cc
index ad75aa7..7b0b895 100644
--- a/libartbase/base/safe_copy.cc
+++ b/libartbase/base/safe_copy.cc
@@ -56,10 +56,10 @@
}
src_iovs[iovecs_used].iov_base = const_cast<char*>(cur);
- if (!IsAlignedParam(cur, PAGE_SIZE)) {
- src_iovs[iovecs_used].iov_len = AlignUp(cur, PAGE_SIZE) - cur;
+ if (!IsAlignedParam(cur, kPageSize)) {
+ src_iovs[iovecs_used].iov_len = AlignUp(cur, kPageSize) - cur;
} else {
- src_iovs[iovecs_used].iov_len = PAGE_SIZE;
+ src_iovs[iovecs_used].iov_len = kPageSize;
}
src_iovs[iovecs_used].iov_len = std::min(src_iovs[iovecs_used].iov_len, len);
diff --git a/libartbase/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc
index 9f7d409..01ed7cd 100644
--- a/libartbase/base/safe_copy_test.cc
+++ b/libartbase/base/safe_copy_test.cc
@@ -31,7 +31,7 @@
#if defined(__linux__)
TEST(SafeCopyTest, smoke) {
- DCHECK_EQ(kPageSize, static_cast<decltype(kPageSize)>(PAGE_SIZE));
+ DCHECK_EQ(kPageSize, static_cast<size_t>(sysconf(_SC_PAGE_SIZE)));
// Map four pages, mark the second one as PROT_NONE, unmap the last one.
void* map = mmap(nullptr, kPageSize * 4, PROT_READ | PROT_WRITE,
@@ -79,7 +79,7 @@
}
TEST(SafeCopyTest, alignment) {
- DCHECK_EQ(kPageSize, static_cast<decltype(kPageSize)>(PAGE_SIZE));
+ DCHECK_EQ(kPageSize, static_cast<size_t>(sysconf(_SC_PAGE_SIZE)));
// Copy the middle of a mapping to the end of another one.
void* src_map = mmap(nullptr, kPageSize * 3, PROT_READ | PROT_WRITE,
diff --git a/libartbase/base/safe_map.h b/libartbase/base/safe_map.h
index 7ae85d4..fa13fe0 100644
--- a/libartbase/base/safe_map.h
+++ b/libartbase/base/safe_map.h
@@ -49,8 +49,9 @@
SafeMap() = default;
SafeMap(const SafeMap&) = default;
SafeMap(SafeMap&&) noexcept = default;
+ explicit SafeMap(const allocator_type& allocator) : map_(allocator) {}
explicit SafeMap(const key_compare& cmp, const allocator_type& allocator = allocator_type())
- : map_(cmp, allocator) {
+ : map_(cmp, allocator) {
}
Self& operator=(const Self& rhs) {
@@ -149,7 +150,7 @@
template <typename CreateFn>
V& GetOrCreate(const K& k, CreateFn create) {
- static_assert(std::is_same_v<V, std::result_of_t<CreateFn()>>,
+ static_assert(std::is_same_v<V, std::invoke_result_t<CreateFn>>,
"Argument `create` should return a value of type V.");
auto lb = lower_bound(k);
if (lb != end() && !key_comp()(k, lb->first)) {
diff --git a/libartbase/base/scoped_cap.h b/libartbase/base/scoped_cap.h
new file mode 100644
index 0000000..c369821
--- /dev/null
+++ b/libartbase/base/scoped_cap.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_SCOPED_CAP_H_
+#define ART_LIBARTBASE_BASE_SCOPED_CAP_H_
+
+#include <sys/capability.h>
+
+#include <utility>
+
+#include "android-base/logging.h"
+
+namespace art {
+
+// A wrapper of `cap_t` that automatically calls `cap_free`.
+class ScopedCap {
+ public:
+ explicit ScopedCap(cap_t cap) : cap_(cap) {}
+
+ ScopedCap(const ScopedCap&) = delete;
+ ScopedCap& operator=(const ScopedCap&) = delete;
+ ScopedCap(ScopedCap&& other) noexcept : cap_(std::exchange(other.cap_, nullptr)) {}
+
+ ~ScopedCap() {
+ if (cap_ != nullptr) {
+ if (cap_free(cap_) != 0) {
+ PLOG(ERROR) << "Failed to call cap_free";
+ }
+ }
+ }
+
+ cap_t Get() const { return cap_; }
+
+ private:
+ cap_t cap_;
+};
+
+} // namespace art
+
+#endif // ART_LIBARTBASE_BASE_SCOPED_CAP_H_
diff --git a/libartbase/base/scoped_flock.cc b/libartbase/base/scoped_flock.cc
index b16a45a..3f44e25 100644
--- a/libartbase/base/scoped_flock.cc
+++ b/libartbase/base/scoped_flock.cc
@@ -27,7 +27,7 @@
namespace art {
-using android::base::StringPrintf;
+using android::base::StringPrintf; // NOLINT - StringPrintf is actually used
/* static */ ScopedFlock LockedFile::Open(const char* filename, std::string* error_msg) {
return Open(filename, O_CREAT | O_RDWR, true, error_msg);
diff --git a/libartbase/base/sdk_version.h b/libartbase/base/sdk_version.h
index 07c3c2c..d39aa95 100644
--- a/libartbase/base/sdk_version.h
+++ b/libartbase/base/sdk_version.h
@@ -36,7 +36,8 @@
kQ = 29u,
kR = 30u,
kS = 31u,
- kT = 32u,
+ kS_V2 = 32u,
+ kT = 33u,
kMax = std::numeric_limits<uint32_t>::max(),
};
diff --git a/libartbase/base/stl_util.h b/libartbase/base/stl_util.h
index 0ae4fd2..2c9547f 100644
--- a/libartbase/base/stl_util.h
+++ b/libartbase/base/stl_util.h
@@ -278,11 +278,10 @@
std::optional<RealIter> end_;
};
-template <typename Iter, typename Filter>
-static inline IterationRange<FilterIterator<Iter, Filter>> Filter(
- IterationRange<Iter> it, Filter cond) {
- auto end = it.end();
- auto start = std::find_if(it.begin(), end, cond);
+template <typename BaseRange, typename Filter>
+static inline auto Filter(BaseRange&& range, Filter cond) {
+ auto end = range.end();
+ auto start = std::find_if(range.begin(), end, cond);
return MakeIterationRange(FilterIterator(start, cond, std::make_optional(end)),
FilterIterator(end, cond, std::make_optional(end)));
}
diff --git a/libartbase/base/stride_iterator.h b/libartbase/base/stride_iterator.h
index 67c0d38..6a7e4be 100644
--- a/libartbase/base/stride_iterator.h
+++ b/libartbase/base/stride_iterator.h
@@ -30,9 +30,9 @@
typename std::iterator<std::random_access_iterator_tag, T>::difference_type;
StrideIterator(const StrideIterator&) = default;
- StrideIterator(StrideIterator&&) = default;
+ StrideIterator(StrideIterator&&) noexcept = default;
StrideIterator& operator=(const StrideIterator&) = default;
- StrideIterator& operator=(StrideIterator&&) = default;
+ StrideIterator& operator=(StrideIterator&&) noexcept = default;
StrideIterator(T* ptr, size_t stride)
: ptr_(reinterpret_cast<uintptr_t>(ptr)),
diff --git a/libartbase/base/string_view_cpp20.h b/libartbase/base/string_view_cpp20.h
index 2c11a2f..9bd29d5 100644
--- a/libartbase/base/string_view_cpp20.h
+++ b/libartbase/base/string_view_cpp20.h
@@ -21,18 +21,23 @@
namespace art {
-// Replacement functions for std::string_view::starts_with(), ends_with()
-// which shall be available in C++20.
-#if __cplusplus >= 202000L
-#error "When upgrading to C++20, remove this error and file a bug to remove this workaround."
-#endif
+// When this code is only compiled on C++20+, these wrappers can be removed and
+// calling code changed to call the string_view methods directly.
inline bool StartsWith(std::string_view sv, std::string_view prefix) {
+#if !defined(__cpp_lib_starts_ends_with) || __cpp_lib_starts_ends_with < 201711L
return sv.substr(0u, prefix.size()) == prefix;
+#else
+ return sv.starts_with(prefix);
+#endif
}
inline bool EndsWith(std::string_view sv, std::string_view suffix) {
+#if !defined(__cpp_lib_starts_ends_with) || __cpp_lib_starts_ends_with < 201711L
return sv.size() >= suffix.size() && sv.substr(sv.size() - suffix.size()) == suffix;
+#else
+ return sv.ends_with(suffix);
+#endif
}
} // namespace art
diff --git a/libartbase/base/transform_iterator.h b/libartbase/base/transform_iterator.h
index 062c88b..552f31f 100644
--- a/libartbase/base/transform_iterator.h
+++ b/libartbase/base/transform_iterator.h
@@ -160,7 +160,7 @@
}
template <typename BaseRange, typename Function>
-auto MakeTransformRange(BaseRange& range, Function f) {
+auto MakeTransformRange(BaseRange&& range, Function f) {
return MakeIterationRange(MakeTransformIterator(range.begin(), f),
MakeTransformIterator(range.end(), f));
}
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index 661dfc4..bff9f45 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -63,7 +63,7 @@
namespace art {
-using android::base::ReadFileToString;
+using android::base::ReadFileToString; // NOLINT - ReadFileToString is actually used
using android::base::StringPrintf;
#if defined(__arm__)
@@ -90,7 +90,7 @@
CHECK_LT(start, limit);
CHECK_EQ(RoundDown(start, kPageSize), RoundDown(limit - 1, kPageSize)) << "range spans pages";
// Declare a volatile variable so the compiler does not elide reads from the page being touched.
- volatile uint8_t v = 0;
+ [[maybe_unused]] volatile uint8_t v = 0;
for (size_t i = 0; i < attempts; ++i) {
// Touch page to maximize chance page is resident.
v = *reinterpret_cast<uint8_t*>(start);
diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp
index 37464f0..c488532 100644
--- a/libartpalette/Android.bp
+++ b/libartpalette/Android.bp
@@ -66,7 +66,7 @@
runtime_libs: ["libartpalette-system"],
srcs: ["apex/palette.cc"],
shared_libs: ["liblog"],
- version_script: "libartpalette.map.txt",
+ version_script: "libartpalette.map",
},
host_linux: {
header_libs: ["libbase_headers"],
@@ -77,7 +77,7 @@
"liblog",
],
},
- version_script: "libartpalette.map.txt",
+ version_script: "libartpalette.map",
},
// Targets without support for dlopen just use the sources for
// the system library which actually implements functionality.
diff --git a/libartpalette/apex/palette.cc b/libartpalette/apex/palette.cc
index 75a3878..9a50683 100644
--- a/libartpalette/apex/palette.cc
+++ b/libartpalette/apex/palette.cc
@@ -110,6 +110,8 @@
extern "C" {
+// Methods in version 1 API, corresponding to SDK level 31.
+
palette_status_t PaletteSchedSetPriority(int32_t tid, int32_t java_priority) {
PaletteSchedSetPriorityMethod m = PaletteLoader::Instance().GetPaletteSchedSetPriorityMethod();
return m(tid, java_priority);
@@ -218,6 +220,8 @@
return m(env);
}
+// Methods in version 2 API, corresponding to SDK level 33.
+
palette_status_t PaletteReportLockContention(JNIEnv* env,
int32_t wait_ms,
const char* filename,
diff --git a/libartpalette/apex/palette_test.cc b/libartpalette/apex/palette_test.cc
index 0bcc093..9585d93 100644
--- a/libartpalette/apex/palette_test.cc
+++ b/libartpalette/apex/palette_test.cc
@@ -21,6 +21,7 @@
#include <sys/syscall.h>
#include <unistd.h>
+#include "base/common_art_test.h"
#include "gtest/gtest.h"
namespace {
@@ -69,23 +70,27 @@
#endif
}
-TEST_F(PaletteClientTest, JniInvocation) {
-#ifndef ART_TARGET_ANDROID
- // On host we need to set up a boot classpath and pass it in here. Let's not
- // bother since this test is only for native API coverage on target.
- GTEST_SKIP() << "Will only spin up a VM on Android";
-#else
+class PaletteClientJniTest : public art::CommonArtTest {};
+
+TEST_F(PaletteClientJniTest, JniInvocation) {
bool enabled;
EXPECT_EQ(PALETTE_STATUS_OK, PaletteShouldReportJniInvocations(&enabled));
- JavaVMInitArgs vm_args;
+ std::string boot_class_path_string =
+ GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames());
+ std::string boot_class_path_locations_string =
+ GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations());
+
JavaVMOption options[] = {
- {.optionString = "-verbose:jni", .extraInfo = nullptr},
+ {.optionString = boot_class_path_string.c_str(), .extraInfo = nullptr},
+ {.optionString = boot_class_path_locations_string.c_str(), .extraInfo = nullptr},
};
- vm_args.version = JNI_VERSION_1_6;
- vm_args.nOptions = std::size(options);
- vm_args.options = options;
- vm_args.ignoreUnrecognized = JNI_TRUE;
+ JavaVMInitArgs vm_args = {
+ .version = JNI_VERSION_1_6,
+ .nOptions = std::size(options),
+ .options = options,
+ .ignoreUnrecognized = JNI_TRUE,
+ };
JavaVM* jvm = nullptr;
JNIEnv* env = nullptr;
@@ -96,5 +101,4 @@
PaletteNotifyEndJniInvocation(env);
EXPECT_EQ(JNI_OK, jvm->DestroyJavaVM());
-#endif
}
diff --git a/libartpalette/include/palette/palette_method_list.h b/libartpalette/include/palette/palette_method_list.h
index 066f24f..6db389c 100644
--- a/libartpalette/include/palette/palette_method_list.h
+++ b/libartpalette/include/palette/palette_method_list.h
@@ -23,8 +23,8 @@
#include "jni.h"
-// Methods in version 1 API
#define PALETTE_METHOD_LIST(M) \
+ /* Methods in version 1 API, corresponding to SDK level 31. */ \
M(PaletteSchedSetPriority, int32_t tid, int32_t java_priority) \
M(PaletteSchedGetPriority, int32_t tid, /*out*/int32_t* java_priority) \
M(PaletteWriteCrashThreadStacks, const char* stacks, size_t stacks_len) \
@@ -53,6 +53,7 @@
M(PaletteShouldReportJniInvocations, bool*) \
M(PaletteNotifyBeginJniInvocation, JNIEnv* env) \
M(PaletteNotifyEndJniInvocation, JNIEnv* env) \
+ /* Methods in version 2 API, corresponding to SDK level 33. */ \
M(PaletteReportLockContention, JNIEnv* env, \
int32_t wait_ms, \
const char* filename, \
diff --git a/libartpalette/include/palette/palette_types.h b/libartpalette/include/palette/palette_types.h
index 905a341..3c02544 100644
--- a/libartpalette/include/palette/palette_types.h
+++ b/libartpalette/include/palette/palette_types.h
@@ -23,7 +23,7 @@
extern "C" {
#endif // __cplusplus
-typedef int32_t palette_status_t;
+using palette_status_t = int32_t;
// Palette function return value when the function completed successfully.
#define PALETTE_STATUS_OK ((palette_status_t) 0)
diff --git a/libartpalette/libartpalette.map b/libartpalette/libartpalette.map
new file mode 100644
index 0000000..be5d9b4
--- /dev/null
+++ b/libartpalette/libartpalette.map
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2019 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.
+#
+
+LIBARTPALETTE_1 { # introduced=31
+ global:
+ # --- VERSION 01 API ---
+ PaletteSchedSetPriority; # apex
+ PaletteSchedGetPriority; # apex
+ PaletteWriteCrashThreadStacks; # apex
+ PaletteTraceEnabled; # apex
+ PaletteTraceBegin; # apex
+ PaletteTraceEnd; # apex
+ PaletteTraceIntegerValue; # apex
+ PaletteAshmemCreateRegion; # apex
+ PaletteAshmemSetProtRegion; # apex
+ PaletteCreateOdrefreshStagingDirectory; # apex
+ PaletteShouldReportDex2oatCompilation; # apex
+ PaletteNotifyStartDex2oatCompilation; # apex
+ PaletteNotifyEndDex2oatCompilation; # apex
+ PaletteNotifyDexFileLoaded; # apex
+ PaletteNotifyOatFileLoaded; # apex
+ PaletteShouldReportJniInvocations; # apex
+ PaletteNotifyBeginJniInvocation; # apex
+ PaletteNotifyEndJniInvocation; # apex
+
+ local:
+ *;
+};
+
+LIBARTPALETTE_2 { # introduced=33
+ global:
+ # --- VERSION 02 API ---
+ PaletteReportLockContention; # apex
+} LIBARTPALETTE_1;
diff --git a/libartpalette/libartpalette.map.txt b/libartpalette/libartpalette.map.txt
deleted file mode 100644
index 6401010..0000000
--- a/libartpalette/libartpalette.map.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-
-LIBARTPALETTE_1 {
- global:
- # --- VERSION 01 API ---
- PaletteSchedSetPriority; # apex
- PaletteSchedGetPriority; # apex
- PaletteWriteCrashThreadStacks; #apex
- PaletteTraceEnabled; # apex
- PaletteTraceBegin; # apex
- PaletteTraceEnd; # apex
- PaletteTraceIntegerValue; # apex
- PaletteAshmemCreateRegion; # apex
- PaletteAshmemSetProtRegion; # apex
- PaletteCreateOdrefreshStagingDirectory; # apex
- PaletteShouldReportDex2oatCompilation; #apex
- PaletteNotifyStartDex2oatCompilation; #apex
- PaletteNotifyEndDex2oatCompilation; #apex
- PaletteNotifyDexFileLoaded; #apex
- PaletteNotifyOatFileLoaded; #apex
- PaletteShouldReportJniInvocations; #apex
- PaletteNotifyBeginJniInvocation; #apex
- PaletteNotifyEndJniInvocation; #apex
- PaletteReportLockContention; #apex
-
- local:
- *;
-};
diff --git a/libartpalette/system/palette_fake.cc b/libartpalette/system/palette_fake.cc
index bbf8f2d..e1e05c9 100644
--- a/libartpalette/system/palette_fake.cc
+++ b/libartpalette/system/palette_fake.cc
@@ -25,6 +25,8 @@
#include "palette_system.h"
+// Methods in version 1 API, corresponding to SDK level 31.
+
// Cached thread priority for testing. No thread priorities are ever affected.
static std::mutex g_tid_priority_map_mutex;
static std::map<int32_t, int32_t> g_tid_priority_map;
@@ -129,6 +131,8 @@
return PALETTE_STATUS_OK;
}
+// Methods in version 2 API, corresponding to SDK level 33.
+
palette_status_t PaletteReportLockContention(JNIEnv* env ATTRIBUTE_UNUSED,
int32_t wait_ms ATTRIBUTE_UNUSED,
const char* filename ATTRIBUTE_UNUSED,
diff --git a/libartservice/Android.bp b/libartservice/Android.bp
deleted file mode 100644
index 9fac53b..0000000
--- a/libartservice/Android.bp
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2021 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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "art_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["art_license"],
-}
-
-cc_library {
- // This native library contains JNI support code for the ART Service Java
- // Language library.
-
- name: "libartservice",
- defaults: ["art_defaults"],
- host_supported: true,
- srcs: [
- "service/native/service.cc",
- ],
- export_include_dirs: ["."],
- apex_available: [
- "com.android.art",
- "com.android.art.debug",
- ],
- shared_libs: [
- "libbase",
- ],
- export_shared_lib_headers: ["libbase"],
-}
-
-// Provides the API and implementation of the ART Service class that will be
-// loaded by the System Server.
-java_sdk_library {
- // This target is named 'service-art' to conform to the naming conventions
- // for JAR files in the System Server.
- name: "service-art",
- defaults: ["framework-system-server-module-defaults"],
- permitted_packages: ["com.android.server.art"],
- visibility: [
- "//art:__subpackages__",
- "//frameworks/base/services/core",
- ],
- impl_library_visibility: [
- "//art/libartservice/tests",
- ],
- apex_available: [
- "com.android.art",
- "com.android.art.debug",
- ],
- sdk_version: "system_server_current",
- min_sdk_version: "31",
- srcs: [
- "service/java/**/*.java",
- ],
- static_libs: [
- ],
- plugins: ["java_api_finder"],
- jarjar_rules: "jarjar-rules.txt",
-}
-
-art_cc_defaults {
- name: "art_libartservice_tests_defaults",
- srcs: [
- "service/native/service_test.cc",
- ],
- shared_libs: [
- "libbase",
- "libartservice",
- ],
-}
-
-// Version of ART gtest `art_libartservice_tests` bundled with the ART APEX on target.
-// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
-art_cc_test {
- name: "art_libartservice_tests",
- defaults: [
- "art_gtest_defaults",
- "art_libartservice_tests_defaults",
- ],
-}
-
-// Standalone version of ART gtest `art_libartservice_tests`, not bundled with the ART APEX on
-// target.
-art_cc_test {
- name: "art_standalone_libartservice_tests",
- defaults: [
- "art_standalone_gtest_defaults",
- "art_libartservice_tests_defaults",
- ],
-}
diff --git a/libartservice/api/system-server-current.txt b/libartservice/api/system-server-current.txt
deleted file mode 100644
index c7844e0..0000000
--- a/libartservice/api/system-server-current.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-// Signature format: 2.0
-package com.android.server.art {
-
- public final class ArtManagerLocal {
- ctor public ArtManagerLocal();
- }
-
-}
-
diff --git a/libartservice/jarjar-rules.txt b/libartservice/jarjar-rules.txt
deleted file mode 100644
index c7d39e6..0000000
--- a/libartservice/jarjar-rules.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# Repackages static libraries to make them private to ART Services.
-rule com.android.modules.utils.** com.android.server.art.jarjar.@0
diff --git a/libartservice/service/Android.bp b/libartservice/service/Android.bp
new file mode 100644
index 0000000..a34e9e8
--- /dev/null
+++ b/libartservice/service/Android.bp
@@ -0,0 +1,238 @@
+// Copyright (C) 2021 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// This native library contains JNI support code for the ART Service Java
+// Language library.
+cc_defaults {
+ name: "libartservice_defaults",
+ defaults: ["art_defaults"],
+ host_supported: true,
+ srcs: [
+ "native/service.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libartservice",
+ defaults: ["libartservice_defaults"],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+ shared_libs: [
+ ],
+}
+
+cc_library {
+ name: "libartserviced",
+ defaults: [
+ "libartservice_defaults",
+ "art_debug_defaults",
+ ],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+ shared_libs: [
+ ],
+}
+
+java_defaults {
+ name: "service-art-defaults",
+ defaults: [
+ "framework-system-server-module-defaults",
+ ],
+ sdk_version: "system_server_current",
+ min_sdk_version: "31",
+ srcs: [
+ "java/**/*.java",
+ ],
+ libs: [
+ "androidx.annotation_annotation",
+ "auto_value_annotations",
+ // TODO(b/256866172): Transitive dependency, for r8 only.
+ "framework-statsd.stubs.module_lib",
+ // TODO(b/256866172): Transitive dependency, for r8 only. This module
+ // always refers to the jar in prebuilts/sdk. We can't use
+ // "framework-connectivity.stubs.module_lib" here because it's not
+ // available on master-art.
+ "sdk_module-lib_current_framework-connectivity",
+ ],
+ static_libs: [
+ "art-statslog-art-java",
+ "artd-aidl-java",
+ "modules-utils-package-state",
+ "modules-utils-shell-command-handler",
+ "service-art-proto-java",
+ ],
+ plugins: [
+ "auto_value_plugin",
+ ],
+}
+
+// Used by tests to allow tests to mock the right classes.
+java_library {
+ name: "service-art-pre-jarjar",
+ defaults: ["service-art-defaults"],
+ installable: false,
+ visibility: [
+ "//visibility:override",
+ "//visibility:private",
+ ],
+}
+
+// Provides the API and implementation of the ART Service class that will be
+// loaded by the System Server.
+java_sdk_library {
+ // This target is named 'service-art' to conform to the naming conventions
+ // for JAR files in the System Server.
+ name: "service-art",
+ defaults: [
+ "service-art-defaults",
+ "framework-system-server-module-optimize-defaults",
+ ],
+ permitted_packages: ["com.android.server.art"],
+ visibility: [
+ "//art:__subpackages__",
+ "//frameworks/base/services/core",
+ ],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+ jarjar_rules: "jarjar-rules.txt",
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
+}
+
+java_library {
+ name: "service-art-proto-java",
+ proto: {
+ type: "lite",
+ },
+ srcs: [
+ "proto/**/*.proto",
+ ],
+ sdk_version: "system_server_current",
+ min_sdk_version: "31",
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
+
+java_library {
+ name: "art-statslog-art-java",
+ srcs: [
+ ":art-statslog-art-java-gen",
+ ],
+ libs: [
+ "framework-statsd.stubs.module_lib",
+ ],
+ sdk_version: "system_server_current",
+ min_sdk_version: "31",
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
+
+genrule {
+ name: "art-statslog-art-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module art --javaPackage com.android.server.art --javaClass ArtStatsLog",
+ out: ["java/com/android/server/art/ArtStatsLog.java"],
+}
+
+art_cc_defaults {
+ name: "art_libartservice_tests_defaults",
+ defaults: ["libartservice_defaults"],
+ srcs: [
+ "native/service_test.cc",
+ ],
+}
+
+// Version of ART gtest `art_libartservice_tests` bundled with the ART APEX on target.
+// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
+art_cc_test {
+ name: "art_libartservice_tests",
+ defaults: [
+ "art_gtest_defaults",
+ "art_libartservice_tests_defaults",
+ ],
+}
+
+// Standalone version of ART gtest `art_libartservice_tests`, not bundled with the ART APEX on
+// target.
+art_cc_test {
+ name: "art_standalone_libartservice_tests",
+ defaults: [
+ "art_standalone_gtest_defaults",
+ "art_libartservice_tests_defaults",
+ ],
+}
+
+android_test {
+ name: "ArtServiceTests",
+
+ // Include all test java files.
+ srcs: [
+ "javatests/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
+ "androidx.test.runner",
+ "artd-aidl-java",
+ "framework-annotations-lib",
+ // We need ExtendedMockito to mock static methods.
+ "mockito-target-extended-minus-junit4",
+ "modules-utils-package-state",
+ "service-art-pre-jarjar",
+ // Statically link against system server to allow us to mock system
+ // server APIs. This won't work on master-art, but it's fine because we
+ // don't run this test on master-art.
+ "services.core",
+ ],
+
+ jni_libs: [
+ // The two libraries below are required by ExtendedMockito.
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ compile_multilib: "both",
+
+ // TODO: This module should move to sdk_version: "system_server_current" when possible,
+ // as this will restrict the APIs available to just that expected system API. For now,
+ // a compileOnly / runtimeOnly split for dependencies doesn't exist in the build system
+ // and so it's not possible to enforce.
+ min_sdk_version: "31",
+
+ test_suites: ["general-tests"],
+ test_config: "ArtServiceTests.xml",
+}
diff --git a/libartservice/service/AndroidManifest.xml b/libartservice/service/AndroidManifest.xml
new file mode 100644
index 0000000..1c13fc6
--- /dev/null
+++ b/libartservice/service/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.art.tests">
+
+ <!-- android:debuggable is required by ExtendedMockito. -->
+ <application android:label="ArtServiceTests" android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.art.tests"
+ android:label="Tests for ART Serices" />
+</manifest>
diff --git a/libartservice/service/ArtServiceTests.xml b/libartservice/service/ArtServiceTests.xml
new file mode 100644
index 0000000..7a47ca3
--- /dev/null
+++ b/libartservice/service/ArtServiceTests.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Config for ART Services test cases">
+ <option name="test-suite-tag" value="apct" />
+
+ <!-- This test needs access to system APIs for mainline modules. -->
+ <option name="hidden-api-checks" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ArtServiceTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.art.tests"/>
+ </test>
+
+ <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+ <!-- TODO(jiakaiz): Change this to U once `ro.build.version.sdk` is bumped. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
+</configuration>
diff --git a/libartservice/api/current.txt b/libartservice/service/api/current.txt
similarity index 100%
rename from libartservice/api/current.txt
rename to libartservice/service/api/current.txt
diff --git a/libartservice/api/removed.txt b/libartservice/service/api/removed.txt
similarity index 100%
rename from libartservice/api/removed.txt
rename to libartservice/service/api/removed.txt
diff --git a/libartservice/service/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
new file mode 100644
index 0000000..31a56da
--- /dev/null
+++ b/libartservice/service/api/system-server-current.txt
@@ -0,0 +1,186 @@
+// Signature format: 2.0
+package com.android.server.art {
+
+ public final class ArtManagerLocal {
+ ctor @Deprecated public ArtManagerLocal();
+ ctor public ArtManagerLocal(@NonNull android.content.Context);
+ method public void addDexoptDoneCallback(boolean, @NonNull java.util.concurrent.Executor, @NonNull com.android.server.art.ArtManagerLocal.DexoptDoneCallback);
+ method public void cancelBackgroundDexoptJob();
+ method @NonNull public void clearAppProfiles(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String);
+ method public void clearBatchDexoptStartCallback();
+ method public void clearScheduleBackgroundDexoptJobCallback();
+ method @NonNull public com.android.server.art.model.DeleteResult deleteDexoptArtifacts(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String);
+ method @NonNull public com.android.server.art.model.DexoptResult dexoptPackage(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, @NonNull com.android.server.art.model.DexoptParams);
+ method @NonNull public com.android.server.art.model.DexoptResult dexoptPackage(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, @NonNull com.android.server.art.model.DexoptParams, @NonNull android.os.CancellationSignal);
+ method public void dump(@NonNull java.io.PrintWriter, @NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot);
+ method public void dumpPackage(@NonNull java.io.PrintWriter, @NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String);
+ method @NonNull public com.android.server.art.model.DexoptStatus getDexoptStatus(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String);
+ method @NonNull public com.android.server.art.model.DexoptStatus getDexoptStatus(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, int);
+ method public int handleShellCommand(@NonNull android.os.Binder, @NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull android.os.ParcelFileDescriptor, @NonNull String[]);
+ method public void onBoot(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<com.android.server.art.model.OperationProgress>);
+ method public void printShellCommandHelp(@NonNull java.io.PrintWriter);
+ method public void removeDexoptDoneCallback(@NonNull com.android.server.art.ArtManagerLocal.DexoptDoneCallback);
+ method public int scheduleBackgroundDexoptJob();
+ method public void setBatchDexoptStartCallback(@NonNull java.util.concurrent.Executor, @NonNull com.android.server.art.ArtManagerLocal.BatchDexoptStartCallback);
+ method public void setScheduleBackgroundDexoptJobCallback(@NonNull java.util.concurrent.Executor, @NonNull com.android.server.art.ArtManagerLocal.ScheduleBackgroundDexoptJobCallback);
+ method @NonNull public android.os.ParcelFileDescriptor snapshotAppProfile(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, @Nullable String) throws com.android.server.art.ArtManagerLocal.SnapshotProfileException;
+ method @NonNull public android.os.ParcelFileDescriptor snapshotBootImageProfile(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot) throws com.android.server.art.ArtManagerLocal.SnapshotProfileException;
+ method public void startBackgroundDexoptJob();
+ method public void unscheduleBackgroundDexoptJob();
+ }
+
+ public static interface ArtManagerLocal.BatchDexoptStartCallback {
+ method public void onBatchDexoptStart(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull com.android.server.art.model.BatchDexoptParams.Builder, @NonNull android.os.CancellationSignal);
+ }
+
+ public static interface ArtManagerLocal.DexoptDoneCallback {
+ method public void onDexoptDone(@NonNull com.android.server.art.model.DexoptResult);
+ }
+
+ public static interface ArtManagerLocal.ScheduleBackgroundDexoptJobCallback {
+ method public void onOverrideJobInfo(@NonNull android.app.job.JobInfo.Builder);
+ }
+
+ public static class ArtManagerLocal.SnapshotProfileException extends java.lang.Exception {
+ ctor public ArtManagerLocal.SnapshotProfileException(@NonNull Throwable);
+ }
+
+ public class ArtModuleServiceInitializer {
+ method public static void setArtModuleServiceManager(@NonNull android.os.ArtModuleServiceManager);
+ }
+
+ public class DexUseManagerLocal {
+ method @NonNull public static com.android.server.art.DexUseManagerLocal createInstance(@NonNull android.content.Context);
+ method @NonNull public java.util.List<com.android.server.art.model.DexContainerFileUseInfo> getSecondaryDexContainerFileUseInfo(@NonNull String);
+ method public void notifyDexContainersLoaded(@NonNull com.android.server.pm.PackageManagerLocal.FilteredSnapshot, @NonNull String, @NonNull java.util.Map<java.lang.String,java.lang.String>);
+ method public void systemReady();
+ }
+
+ public class ReasonMapping {
+ field public static final String REASON_BG_DEXOPT = "bg-dexopt";
+ field public static final String REASON_BOOT_AFTER_MAINLINE_UPDATE = "boot-after-mainline-update";
+ field public static final String REASON_BOOT_AFTER_OTA = "boot-after-ota";
+ field public static final String REASON_CMDLINE = "cmdline";
+ field public static final String REASON_FIRST_BOOT = "first-boot";
+ field public static final String REASON_INACTIVE = "inactive";
+ field public static final String REASON_INSTALL = "install";
+ field public static final String REASON_INSTALL_BULK = "install-bulk";
+ field public static final String REASON_INSTALL_BULK_DOWNGRADED = "install-bulk-downgraded";
+ field public static final String REASON_INSTALL_BULK_SECONDARY = "install-bulk-secondary";
+ field public static final String REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = "install-bulk-secondary-downgraded";
+ field public static final String REASON_INSTALL_FAST = "install-fast";
+ }
+
+}
+
+package com.android.server.art.model {
+
+ public class ArtFlags {
+ method public static int defaultGetStatusFlags();
+ field public static final int FLAG_FORCE = 16; // 0x10
+ field public static final int FLAG_FOR_PRIMARY_DEX = 1; // 0x1
+ field public static final int FLAG_FOR_SECONDARY_DEX = 2; // 0x2
+ field public static final int FLAG_FOR_SINGLE_SPLIT = 32; // 0x20
+ field public static final int FLAG_SHOULD_DOWNGRADE = 8; // 0x8
+ field public static final int FLAG_SHOULD_INCLUDE_DEPENDENCIES = 4; // 0x4
+ field public static final int FLAG_SKIP_IF_STORAGE_LOW = 64; // 0x40
+ field public static final int PRIORITY_BACKGROUND = 40; // 0x28
+ field public static final int PRIORITY_BOOT = 100; // 0x64
+ field public static final int PRIORITY_INTERACTIVE = 60; // 0x3c
+ field public static final int PRIORITY_INTERACTIVE_FAST = 80; // 0x50
+ field public static final int SCHEDULE_DISABLED_BY_SYSPROP = 2; // 0x2
+ field public static final int SCHEDULE_JOB_SCHEDULER_FAILURE = 1; // 0x1
+ field public static final int SCHEDULE_SUCCESS = 0; // 0x0
+ }
+
+ public abstract class BatchDexoptParams {
+ method @NonNull public abstract com.android.server.art.model.DexoptParams getDexoptParams();
+ method @NonNull public abstract java.util.List<java.lang.String> getPackages();
+ }
+
+ public static final class BatchDexoptParams.Builder {
+ method @NonNull public com.android.server.art.model.BatchDexoptParams build();
+ method @NonNull public com.android.server.art.model.BatchDexoptParams.Builder setDexoptParams(@NonNull com.android.server.art.model.DexoptParams);
+ method @NonNull public com.android.server.art.model.BatchDexoptParams.Builder setPackages(@NonNull java.util.List<java.lang.String>);
+ }
+
+ public abstract class DeleteResult {
+ method public abstract long getFreedBytes();
+ }
+
+ public abstract class DexContainerFileUseInfo {
+ method @NonNull public abstract String getDexContainerFile();
+ method @NonNull public abstract java.util.Set<java.lang.String> getLoadingPackages();
+ method @NonNull public abstract android.os.UserHandle getUserHandle();
+ }
+
+ public class DexoptParams {
+ method @NonNull public String getCompilerFilter();
+ method public int getFlags();
+ method public int getPriorityClass();
+ method @NonNull public String getReason();
+ method @Nullable public String getSplitName();
+ field public static final String COMPILER_FILTER_NOOP = "skip";
+ }
+
+ public static final class DexoptParams.Builder {
+ ctor public DexoptParams.Builder(@NonNull String);
+ ctor public DexoptParams.Builder(@NonNull String, int);
+ method @NonNull public com.android.server.art.model.DexoptParams build();
+ method @NonNull public com.android.server.art.model.DexoptParams.Builder setCompilerFilter(@NonNull String);
+ method @NonNull public com.android.server.art.model.DexoptParams.Builder setFlags(int);
+ method @NonNull public com.android.server.art.model.DexoptParams.Builder setFlags(int, int);
+ method @NonNull public com.android.server.art.model.DexoptParams.Builder setPriorityClass(int);
+ method @NonNull public com.android.server.art.model.DexoptParams.Builder setSplitName(@Nullable String);
+ }
+
+ public abstract class DexoptResult {
+ method public int getFinalStatus();
+ method @NonNull public abstract java.util.List<com.android.server.art.model.DexoptResult.PackageDexoptResult> getPackageDexoptResults();
+ method @NonNull public abstract String getReason();
+ method @NonNull public abstract String getRequestedCompilerFilter();
+ field public static final int DEXOPT_CANCELLED = 40; // 0x28
+ field public static final int DEXOPT_FAILED = 30; // 0x1e
+ field public static final int DEXOPT_PERFORMED = 20; // 0x14
+ field public static final int DEXOPT_SKIPPED = 10; // 0xa
+ }
+
+ public abstract static class DexoptResult.DexContainerFileDexoptResult {
+ method @NonNull public abstract String getAbi();
+ method @NonNull public abstract String getActualCompilerFilter();
+ method public abstract long getDex2oatCpuTimeMillis();
+ method public abstract long getDex2oatWallTimeMillis();
+ method @NonNull public abstract String getDexContainerFile();
+ method public abstract long getSizeBeforeBytes();
+ method public abstract long getSizeBytes();
+ method public abstract int getStatus();
+ method public abstract boolean isPrimaryAbi();
+ }
+
+ public abstract static class DexoptResult.PackageDexoptResult {
+ method @NonNull public abstract java.util.List<com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult> getDexContainerFileDexoptResults();
+ method @NonNull public abstract String getPackageName();
+ method public int getStatus();
+ method public boolean hasUpdatedArtifacts();
+ }
+
+ public abstract class DexoptStatus {
+ method @NonNull public abstract java.util.List<com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus> getDexContainerFileDexoptStatuses();
+ }
+
+ public abstract static class DexoptStatus.DexContainerFileDexoptStatus {
+ method @NonNull public abstract String getAbi();
+ method @NonNull public abstract String getCompilationReason();
+ method @NonNull public abstract String getCompilerFilter();
+ method @NonNull public abstract String getDexContainerFile();
+ method @NonNull public abstract String getLocationDebugString();
+ method public abstract boolean isPrimaryAbi();
+ method public abstract boolean isPrimaryDex();
+ }
+
+ public abstract class OperationProgress {
+ method public int getPercentage();
+ }
+
+}
+
diff --git a/libartservice/api/system-server-removed.txt b/libartservice/service/api/system-server-removed.txt
similarity index 100%
rename from libartservice/api/system-server-removed.txt
rename to libartservice/service/api/system-server-removed.txt
diff --git a/libartservice/service/jarjar-rules.txt b/libartservice/service/jarjar-rules.txt
new file mode 100644
index 0000000..54ff0a1
--- /dev/null
+++ b/libartservice/service/jarjar-rules.txt
@@ -0,0 +1,3 @@
+# Repackages static libraries to make them private to ART Services.
+rule com.android.modules.utils.** com.android.server.art.jarjar.@0
+rule com.google.protobuf.** com.android.server.art.jarjar.@0
diff --git a/libartservice/service/java/com/android/server/art/AidlUtils.java b/libartservice/service/java/com/android/server/art/AidlUtils.java
new file mode 100644
index 0000000..f38000d
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/AidlUtils.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.OutputArtifacts.PermissionSettings;
+import static com.android.server.art.OutputArtifacts.PermissionSettings.SeContext;
+import static com.android.server.art.ProfilePath.PrebuiltProfilePath;
+import static com.android.server.art.ProfilePath.PrimaryCurProfilePath;
+import static com.android.server.art.ProfilePath.PrimaryRefProfilePath;
+import static com.android.server.art.ProfilePath.SecondaryCurProfilePath;
+import static com.android.server.art.ProfilePath.SecondaryRefProfilePath;
+import static com.android.server.art.ProfilePath.TmpProfilePath;
+import static com.android.server.art.ProfilePath.WritableProfilePath;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/** @hide */
+public final class AidlUtils {
+ private AidlUtils() {}
+
+ @NonNull
+ public static ArtifactsPath buildArtifactsPath(
+ @NonNull String dexPath, @NonNull String isa, boolean isInDalvikCache) {
+ var artifactsPath = new ArtifactsPath();
+ artifactsPath.dexPath = dexPath;
+ artifactsPath.isa = isa;
+ artifactsPath.isInDalvikCache = isInDalvikCache;
+ return artifactsPath;
+ }
+
+ @NonNull
+ public static FsPermission buildFsPermission(
+ int uid, int gid, boolean isOtherReadable, boolean isOtherExecutable) {
+ var fsPermission = new FsPermission();
+ fsPermission.uid = uid;
+ fsPermission.gid = gid;
+ fsPermission.isOtherReadable = isOtherReadable;
+ fsPermission.isOtherExecutable = isOtherExecutable;
+ return fsPermission;
+ }
+
+ @NonNull
+ public static FsPermission buildFsPermission(int uid, int gid, boolean isOtherReadable) {
+ return buildFsPermission(uid, gid, isOtherReadable, false /* isOtherExecutable */);
+ }
+
+ @NonNull
+ public static DexMetadataPath buildDexMetadataPath(@NonNull String dexPath) {
+ var dexMetadataPath = new DexMetadataPath();
+ dexMetadataPath.dexPath = dexPath;
+ return dexMetadataPath;
+ }
+
+ @NonNull
+ public static PermissionSettings buildPermissionSettings(@NonNull FsPermission dirFsPermission,
+ @NonNull FsPermission fileFsPermission, @Nullable SeContext seContext) {
+ var permissionSettings = new PermissionSettings();
+ permissionSettings.dirFsPermission = dirFsPermission;
+ permissionSettings.fileFsPermission = fileFsPermission;
+ permissionSettings.seContext = seContext;
+ return permissionSettings;
+ }
+
+ @NonNull
+ public static OutputArtifacts buildOutputArtifacts(@NonNull String dexPath, @NonNull String isa,
+ boolean isInDalvikCache, @NonNull PermissionSettings permissionSettings) {
+ var outputArtifacts = new OutputArtifacts();
+ outputArtifacts.artifactsPath = buildArtifactsPath(dexPath, isa, isInDalvikCache);
+ outputArtifacts.permissionSettings = permissionSettings;
+ return outputArtifacts;
+ }
+
+ @NonNull
+ public static PrimaryRefProfilePath buildPrimaryRefProfilePath(
+ @NonNull String packageName, @NonNull String profileName) {
+ var primaryRefProfilePath = new PrimaryRefProfilePath();
+ primaryRefProfilePath.packageName = packageName;
+ primaryRefProfilePath.profileName = profileName;
+ return primaryRefProfilePath;
+ }
+
+ @NonNull
+ public static SecondaryRefProfilePath buildSecondaryRefProfilePath(@NonNull String dexPath) {
+ var secondaryRefProfilePath = new SecondaryRefProfilePath();
+ secondaryRefProfilePath.dexPath = dexPath;
+ return secondaryRefProfilePath;
+ }
+
+ @NonNull
+ public static ProfilePath buildProfilePathForPrimaryRef(
+ @NonNull String packageName, @NonNull String profileName) {
+ return ProfilePath.primaryRefProfilePath(
+ buildPrimaryRefProfilePath(packageName, profileName));
+ }
+
+ @NonNull
+ public static ProfilePath buildProfilePathForPrebuilt(@NonNull String dexPath) {
+ var prebuiltProfilePath = new PrebuiltProfilePath();
+ prebuiltProfilePath.dexPath = dexPath;
+ return ProfilePath.prebuiltProfilePath(prebuiltProfilePath);
+ }
+
+ @NonNull
+ public static ProfilePath buildProfilePathForDm(@NonNull String dexPath) {
+ return ProfilePath.dexMetadataPath(buildDexMetadataPath(dexPath));
+ }
+
+ @NonNull
+ public static ProfilePath buildProfilePathForPrimaryCur(
+ int userId, @NonNull String packageName, @NonNull String profileName) {
+ var primaryCurProfilePath = new PrimaryCurProfilePath();
+ primaryCurProfilePath.userId = userId;
+ primaryCurProfilePath.packageName = packageName;
+ primaryCurProfilePath.profileName = profileName;
+ return ProfilePath.primaryCurProfilePath(primaryCurProfilePath);
+ }
+
+ @NonNull
+ public static ProfilePath buildProfilePathForSecondaryRef(@NonNull String dexPath) {
+ return ProfilePath.secondaryRefProfilePath(buildSecondaryRefProfilePath(dexPath));
+ }
+
+ @NonNull
+ public static ProfilePath buildProfilePathForSecondaryCur(@NonNull String dexPath) {
+ var secondaryCurProfilePath = new SecondaryCurProfilePath();
+ secondaryCurProfilePath.dexPath = dexPath;
+ return ProfilePath.secondaryCurProfilePath(secondaryCurProfilePath);
+ }
+
+ @NonNull
+ private static OutputProfile buildOutputProfile(
+ @NonNull WritableProfilePath finalPath, int uid, int gid, boolean isPublic) {
+ var outputProfile = new OutputProfile();
+ outputProfile.profilePath = new TmpProfilePath();
+ outputProfile.profilePath.finalPath = finalPath;
+ outputProfile.profilePath.id = ""; // Will be filled by artd.
+ outputProfile.profilePath.tmpPath = ""; // Will be filled by artd.
+ outputProfile.fsPermission = buildFsPermission(uid, gid, isPublic);
+ return outputProfile;
+ }
+
+ @NonNull
+ public static OutputProfile buildOutputProfileForPrimary(@NonNull String packageName,
+ @NonNull String profileName, int uid, int gid, boolean isPublic) {
+ return buildOutputProfile(WritableProfilePath.forPrimary(
+ buildPrimaryRefProfilePath(packageName, profileName)),
+ uid, gid, isPublic);
+ }
+
+ @NonNull
+ public static OutputProfile buildOutputProfileForSecondary(
+ @NonNull String dexPath, int uid, int gid, boolean isPublic) {
+ return buildOutputProfile(
+ WritableProfilePath.forSecondary(buildSecondaryRefProfilePath(dexPath)), uid, gid,
+ isPublic);
+ }
+
+ @NonNull
+ public static SeContext buildSeContext(@NonNull String seInfo, int uid) {
+ var seContext = new SeContext();
+ seContext.seInfo = seInfo;
+ seContext.uid = uid;
+ return seContext;
+ }
+
+ @NonNull
+ public static String toString(@NonNull PrimaryRefProfilePath profile) {
+ return String.format(
+ "[packageName = %s, profileName = %s]", profile.packageName, profile.profileName);
+ }
+
+ @NonNull
+ public static String toString(@NonNull SecondaryRefProfilePath profile) {
+ return String.format("[dexPath = %s]", profile.dexPath);
+ }
+
+ @NonNull
+ public static String toString(@NonNull WritableProfilePath profile) {
+ switch (profile.getTag()) {
+ case WritableProfilePath.forPrimary:
+ return toString(profile.getForPrimary());
+ case WritableProfilePath.forSecondary:
+ return toString(profile.getForSecondary());
+ default:
+ throw new IllegalStateException(
+ "Unknown WritableProfilePath tag " + profile.getTag());
+ }
+ }
+
+ @NonNull
+ public static String toString(@NonNull ProfilePath profile) {
+ switch (profile.getTag()) {
+ case ProfilePath.primaryRefProfilePath:
+ return toString(profile.getPrimaryRefProfilePath());
+ case ProfilePath.secondaryRefProfilePath:
+ return toString(profile.getSecondaryRefProfilePath());
+ default:
+ throw new UnsupportedOperationException(
+ "Only reference profile paths are supported to be converted to string, got "
+ + profile.getTag());
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index 64aec7b..5066a9e 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -16,16 +16,1092 @@
package com.android.server.art;
+import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo;
+import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo;
+import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
+import static com.android.server.art.ReasonMapping.BatchDexoptReason;
+import static com.android.server.art.ReasonMapping.BootReason;
+import static com.android.server.art.Utils.Abi;
+import static com.android.server.art.model.ArtFlags.GetStatusFlags;
+import static com.android.server.art.model.ArtFlags.ScheduleStatus;
+import static com.android.server.art.model.Config.Callback;
+import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
+
+import android.R;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.job.JobInfo;
+import android.apphibernation.AppHibernationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.BatchDexoptParams;
+import com.android.server.art.model.Config;
+import com.android.server.art.model.DeleteResult;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.model.DexoptStatus;
+import com.android.server.art.model.OperationProgress;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* This class provides a system API for functionality provided by the ART module.
*
+ * Note: Although this class is the entry point of ART services, this class is not a {@link
+ * SystemService}, and it does not publish a binder. Instead, it is a module loaded by the
+ * system_server process, registered in {@link LocalManagerRegistry}. {@link LocalManagerRegistry}
+ * specifies that in-process module interfaces should be named with the suffix {@code ManagerLocal}
+ * for consistency.
+ *
* @hide
*/
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public final class ArtManagerLocal {
private static final String TAG = "ArtService";
+ private static final String[] CLASSPATHS_FOR_BOOT_IMAGE_PROFILE = {
+ "BOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"};
- public ArtManagerLocal() {}
+ /** @hide */
+ @VisibleForTesting public static final long DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES = 500_000_000;
+
+ @NonNull private final Injector mInjector;
+
+ @Deprecated
+ public ArtManagerLocal() {
+ mInjector = new Injector(this, null /* context */);
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * Only {@code SystemServer} should create an instance and register it in {@link
+ * LocalManagerRegistry}. Other API users should obtain the instance from {@link
+ * LocalManagerRegistry}.
+ *
+ * @param context the system server context
+ * @throws NullPointerException if required dependencies are missing
+ */
+ public ArtManagerLocal(@NonNull Context context) {
+ mInjector = new Injector(this, context);
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public ArtManagerLocal(@NonNull Injector injector) {
+ mInjector = injector;
+ }
+
+ /**
+ * Handles ART Service commands, which is a subset of `cmd package` commands.
+ *
+ * Note: This method is not an override of {@link Binder#handleShellCommand} because ART
+ * services does not publish a binder. Instead, it handles the commands forwarded by the
+ * `package` service. The semantics of the parameters are the same as {@link
+ * Binder#handleShellCommand}.
+ *
+ * @return zero on success, non-zero on internal error (e.g., I/O error)
+ * @throws SecurityException if the caller is not root
+ * @throws IllegalArgumentException if the arguments are illegal
+ * @see ArtShellCommand#printHelp(PrintWriter)
+ */
+ public int handleShellCommand(@NonNull Binder target, @NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return new ArtShellCommand(
+ this, mInjector.getPackageManagerLocal(), mInjector.getDexUseManager())
+ .exec(target, in.getFileDescriptor(), out.getFileDescriptor(),
+ err.getFileDescriptor(), args);
+ }
+
+ /** Prints ART Service shell command help. */
+ public void printShellCommandHelp(@NonNull PrintWriter pw) {
+ ArtShellCommand.printHelp(pw);
+ }
+
+ /**
+ * Deletes dexopt artifacts of a package, including the artifacts for primary dex files and the
+ * ones for secondary dex files. This includes VDEX, ODEX, and ART files.
+ *
+ * @throws IllegalArgumentException if the package is not found or the flags are illegal
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ */
+ @NonNull
+ public DeleteResult deleteDexoptArtifacts(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
+ PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+
+ try {
+ long freedBytes = 0;
+
+ boolean isInDalvikCache = Utils.isInDalvikCache(pkgState);
+ for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) {
+ if (!dexInfo.hasCode()) {
+ continue;
+ }
+ for (Abi abi : Utils.getAllAbis(pkgState)) {
+ freedBytes += mInjector.getArtd().deleteArtifacts(AidlUtils.buildArtifactsPath(
+ dexInfo.dexPath(), abi.isa(), isInDalvikCache));
+ }
+ }
+
+ for (SecondaryDexInfo dexInfo :
+ mInjector.getDexUseManager().getSecondaryDexInfo(packageName)) {
+ for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) {
+ freedBytes += mInjector.getArtd().deleteArtifacts(AidlUtils.buildArtifactsPath(
+ dexInfo.dexPath(), abi.isa(), false /* isInDalvikCache */));
+ }
+ }
+
+ return DeleteResult.create(freedBytes);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("An error occurred when calling artd", e);
+ }
+ }
+
+ /**
+ * Returns the dexopt status of a package.
+ *
+ * Uses the default flags ({@link ArtFlags#defaultGetStatusFlags()}).
+ *
+ * @throws IllegalArgumentException if the package is not found or the flags are illegal
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ */
+ @NonNull
+ public DexoptStatus getDexoptStatus(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
+ return getDexoptStatus(snapshot, packageName, ArtFlags.defaultGetStatusFlags());
+ }
+
+ /**
+ * Same as above, but allows to specify flags.
+ *
+ * @see #getDexoptStatus(PackageManagerLocal.FilteredSnapshot, String)
+ */
+ @NonNull
+ public DexoptStatus getDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull String packageName, @GetStatusFlags int flags) {
+ if ((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0
+ && (flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) == 0) {
+ throw new IllegalArgumentException("Nothing to check");
+ }
+
+ PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+
+ try {
+ List<DexContainerFileDexoptStatus> statuses = new ArrayList<>();
+
+ if ((flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) != 0) {
+ for (DetailedPrimaryDexInfo dexInfo :
+ PrimaryDexUtils.getDetailedDexInfo(pkgState, pkg)) {
+ if (!dexInfo.hasCode()) {
+ continue;
+ }
+ for (Abi abi : Utils.getAllAbis(pkgState)) {
+ try {
+ GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus(
+ dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext());
+ statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(),
+ true /* isPrimaryDex */, abi.isPrimaryAbi(), abi.name(),
+ result.compilerFilter, result.compilationReason,
+ result.locationDebugString));
+ } catch (ServiceSpecificException e) {
+ statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(),
+ true /* isPrimaryDex */, abi.isPrimaryAbi(), abi.name(),
+ "error", "error", e.getMessage()));
+ }
+ }
+ }
+ }
+
+ if ((flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) != 0) {
+ for (SecondaryDexInfo dexInfo :
+ mInjector.getDexUseManager().getSecondaryDexInfo(packageName)) {
+ for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) {
+ try {
+ GetDexoptStatusResult result = mInjector.getArtd().getDexoptStatus(
+ dexInfo.dexPath(), abi.isa(), dexInfo.classLoaderContext());
+ statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(),
+ false /* isPrimaryDex */, abi.isPrimaryAbi(), abi.name(),
+ result.compilerFilter, result.compilationReason,
+ result.locationDebugString));
+ } catch (ServiceSpecificException e) {
+ statuses.add(DexContainerFileDexoptStatus.create(dexInfo.dexPath(),
+ false /* isPrimaryDex */, abi.isPrimaryAbi(), abi.name(),
+ "error", "error", e.getMessage()));
+ }
+ }
+ }
+ }
+
+ return DexoptStatus.create(statuses);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("An error occurred when calling artd", e);
+ }
+ }
+
+ /**
+ * Clears the profiles of the given app that are collected locally, including the profiles for
+ * primary dex files and the ones for secondary dex files. More specifically, it clears
+ * reference profiles and current profiles. External profiles (e.g., cloud profiles) will be
+ * kept.
+ *
+ * @throws IllegalArgumentException if the package is not found or the flags are illegal
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ */
+ @NonNull
+ public void clearAppProfiles(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
+ PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+
+ try {
+ for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) {
+ if (!dexInfo.hasCode()) {
+ continue;
+ }
+ mInjector.getArtd().deleteProfile(
+ PrimaryDexUtils.buildRefProfilePath(pkgState, dexInfo));
+ for (ProfilePath profile : PrimaryDexUtils.getCurProfiles(
+ mInjector.getUserManager(), pkgState, dexInfo)) {
+ mInjector.getArtd().deleteProfile(profile);
+ }
+ }
+
+ // This only deletes the profiles of known secondary dex files. If there are unknown
+ // secondary dex files, their profiles will be deleted by `cleanup`.
+ for (SecondaryDexInfo dexInfo :
+ mInjector.getDexUseManager().getSecondaryDexInfo(packageName)) {
+ mInjector.getArtd().deleteProfile(
+ AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath()));
+ mInjector.getArtd().deleteProfile(
+ AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath()));
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("An error occurred when calling artd", e);
+ }
+ }
+
+ /**
+ * Dexopts a package. The time this operation takes ranges from a few milliseconds to several
+ * minutes, depending on the params and the code size of the package.
+ *
+ * When this operation ends (either completed or cancelled), callbacks added by {@link
+ * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called.
+ *
+ * @throws IllegalArgumentException if the package is not found or the params are illegal
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ */
+ @NonNull
+ public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull String packageName, @NonNull DexoptParams params) {
+ var cancellationSignal = new CancellationSignal();
+ return dexoptPackage(snapshot, packageName, params, cancellationSignal);
+ }
+
+ /**
+ * Same as above, but supports cancellation.
+ *
+ * @see #dexoptPackage(PackageManagerLocal.FilteredSnapshot, String, DexoptParams)
+ */
+ @NonNull
+ public DexoptResult dexoptPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull String packageName, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ return mInjector.getDexoptHelper().dexopt(
+ snapshot, List.of(packageName), params, cancellationSignal, Runnable::run);
+ }
+
+ /**
+ * Resets the dexopt state of the package as if the package is newly installed.
+ *
+ * More specifically, it clears reference profiles, current profiles, and any code compiled from
+ * those local profiles. If there is an external profile (e.g., a cloud profile), the code
+ * compiled from that profile will be kept.
+ *
+ * For secondary dex files, it also clears all dexopt artifacts.
+ *
+ * @hide
+ */
+ @NonNull
+ public DexoptResult resetDexoptStatus(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull String packageName, @NonNull CancellationSignal cancellationSignal) {
+ // We must delete the artifacts for primary dex files beforehand rather than relying on
+ // `dexoptPackage` to replace them because:
+ // - If dexopt is not needed after the deletion, then we shouldn't run dexopt at all. For
+ // example, when we have a DM file that contains a VDEX file but doesn't contain a cloud
+ // profile, this happens. Note that this is more about correctness rather than
+ // performance.
+ // - We don't want the existing artifacts to affect dexopt. For example, the existing VDEX
+ // file should not be an input VDEX.
+ //
+ // We delete the artifacts for secondary dex files and `dexoptPackage` won't re-generate
+ // them because `dexoptPackage` for `REASON_INSTALL` is for primary dex only. This is
+ // intentional because secondary dex files are supposed to be unknown at install time.
+ deleteDexoptArtifacts(snapshot, packageName);
+ clearAppProfiles(snapshot, packageName);
+
+ // Re-generate artifacts for primary dex files if needed.
+ return dexoptPackage(snapshot, packageName,
+ new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(), cancellationSignal);
+ }
+
+ /**
+ * Runs batch dexopt for the given reason.
+ *
+ * This is called by ART Service automatically during boot / background dexopt.
+ *
+ * The list of packages and options are determined by {@code reason}, and can be overridden by
+ * {@link #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}.
+ *
+ * The dexopt is done in a thread pool. The number of packages being dexopted
+ * simultaneously can be configured by system property {@code pm.dexopt.<reason>.concurrency}
+ * (e.g., {@code pm.dexopt.bg-dexopt.concurrency=4}), and the number of threads for each {@code
+ * dex2oat} invocation can be configured by system property {@code dalvik.vm.*dex2oat-threads}
+ * (e.g., {@code dalvik.vm.background-dex2oat-threads=4}). I.e., the maximum number of
+ * concurrent threads is the product of the two system properties. Note that the physical core
+ * usage is always bound by {@code dalvik.vm.*dex2oat-cpu-set} regardless of the number of
+ * threads.
+ *
+ * When this operation ends (either completed or cancelled), callbacks added by {@link
+ * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} are called.
+ *
+ * If the storage is nearly low, and {@code reason} is {@link ReasonMapping#REASON_BG_DEXOPT},
+ * it may also downgrade some inactive packages to a less optimized compiler filter, specified
+ * by the system property {@code pm.dexopt.inactive} (typically "verify"), to free up some
+ * space. This feature is only enabled when the system property {@code
+ * pm.dexopt.downgrade_after_inactive_days} is set. The space threshold to trigger this feature
+ * is the Storage Manager's low space threshold plus {@link
+ * #DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES}. The concurrency can be configured by system property
+ * {@code pm.dexopt.bg-dexopt.concurrency}. The packages in the list provided by
+ * {@link BatchDexoptStartCallback} for {@link ReasonMapping#REASON_BG_DEXOPT} are never
+ * downgraded.
+ *
+ * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on
+ * @param reason determines the default list of packages and options
+ * @param cancellationSignal provides the ability to cancel this operation
+ * @param processCallbackExecutor the executor to call {@code progressCallback}
+ * @param progressCallback called repeatedly whenever there is an update on the progress
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error), or the callback set by {@link
+ * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} provides invalid
+ * params.
+ *
+ * @hide
+ */
+ @NonNull
+ public DexoptResult dexoptPackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull @BatchDexoptReason String reason,
+ @NonNull CancellationSignal cancellationSignal,
+ @Nullable @CallbackExecutor Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
+ List<String> defaultPackages =
+ Collections.unmodifiableList(getDefaultPackages(snapshot, reason));
+ DexoptParams defaultDexoptParams = new DexoptParams.Builder(reason).build();
+ var builder = new BatchDexoptParams.Builder(defaultPackages, defaultDexoptParams);
+ Callback<BatchDexoptStartCallback, Void> callback =
+ mInjector.getConfig().getBatchDexoptStartCallback();
+ if (callback != null) {
+ Utils.executeAndWait(callback.executor(), () -> {
+ callback.get().onBatchDexoptStart(
+ snapshot, reason, defaultPackages, builder, cancellationSignal);
+ });
+ }
+ BatchDexoptParams params = builder.build();
+ Utils.check(params.getDexoptParams().getReason().equals(reason));
+
+ ExecutorService dexoptExecutor =
+ Executors.newFixedThreadPool(ReasonMapping.getConcurrencyForReason(reason));
+ try {
+ if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
+ maybeDowngradePackages(snapshot,
+ new HashSet<>(params.getPackages()) /* excludedPackages */,
+ cancellationSignal, dexoptExecutor);
+ }
+ Log.i(TAG, "Dexopting packages");
+ return mInjector.getDexoptHelper().dexopt(snapshot, params.getPackages(),
+ params.getDexoptParams(), cancellationSignal, dexoptExecutor,
+ progressCallbackExecutor, progressCallback);
+ } finally {
+ dexoptExecutor.shutdown();
+ }
+ }
+
+ /**
+ * Overrides the default params for {@link #dexoptPackages}. This method is thread-safe.
+ *
+ * This method gives users the opportunity to change the behavior of {@link #dexoptPackages},
+ * which is called by ART Service automatically during boot / background dexopt.
+ *
+ * If this method is not called, the default list of packages and options determined by {@code
+ * reason} will be used.
+ */
+ public void setBatchDexoptStartCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull BatchDexoptStartCallback callback) {
+ mInjector.getConfig().setBatchDexoptStartCallback(executor, callback);
+ }
+
+ /**
+ * Clears the callback set by {@link
+ * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)}. This method is
+ * thread-safe.
+ */
+ public void clearBatchDexoptStartCallback() {
+ mInjector.getConfig().clearBatchDexoptStartCallback();
+ }
+
+ /**
+ * Schedules a background dexopt job. Does nothing if the job is already scheduled.
+ *
+ * Use this method if you want the system to automatically determine the best time to run
+ * dexopt.
+ *
+ * The job will be run by the job scheduler. The job scheduling configuration can be overridden
+ * by {@link
+ * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. By
+ * default, it runs periodically (at most once a day) when all the following constraints are
+ * meet.
+ *
+ * <ul>
+ * <li>The device is idling. (see {@link JobInfo.Builder#setRequiresDeviceIdle(boolean)})
+ * <li>The device is charging. (see {@link JobInfo.Builder#setRequiresCharging(boolean)})
+ * <li>The battery level is not low.
+ * (see {@link JobInfo.Builder#setRequiresBatteryNotLow(boolean)})
+ * </ul>
+ *
+ * When the job is running, it may be cancelled by the job scheduler immediately whenever one of
+ * the constraints above is no longer met or cancelled by the {@link
+ * #cancelBackgroundDexoptJob()} API. The job scheduler retries it in the next <i>maintenance
+ * window</i>. For information about <i>maintenance window</i>, see
+ * https://developer.android.com/training/monitoring-device-state/doze-standby.
+ *
+ * See {@link #dexoptPackages} for how to customize the behavior of the job.
+ *
+ * When the job ends (either completed or cancelled), the result is sent to the callbacks added
+ * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the
+ * reason {@link ReasonMapping#REASON_BG_DEXOPT}.
+ */
+ public @ScheduleStatus int scheduleBackgroundDexoptJob() {
+ return mInjector.getBackgroundDexoptJob().schedule();
+ }
+
+ /**
+ * Unschedules the background dexopt job scheduled by {@link #scheduleBackgroundDexoptJob()}.
+ * Does nothing if the job is not scheduled.
+ *
+ * Use this method if you no longer want the system to automatically run dexopt.
+ *
+ * If the job is already started by the job scheduler and is running, it will be cancelled
+ * immediately, and the result sent to the callbacks added by {@link
+ * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link
+ * DexoptResult#DEXOPT_CANCELLED}. Note that a job started by {@link
+ * #startBackgroundDexoptJob()} will not be cancelled by this method.
+ */
+ public void unscheduleBackgroundDexoptJob() {
+ mInjector.getBackgroundDexoptJob().unschedule();
+ }
+
+ /**
+ * Overrides the configuration of the background dexopt job. This method is thread-safe.
+ */
+ public void setScheduleBackgroundDexoptJobCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull ScheduleBackgroundDexoptJobCallback callback) {
+ mInjector.getConfig().setScheduleBackgroundDexoptJobCallback(executor, callback);
+ }
+
+ /**
+ * Clears the callback set by {@link
+ * #setScheduleBackgroundDexoptJobCallback(Executor, ScheduleBackgroundDexoptJobCallback)}. This
+ * method is thread-safe.
+ */
+ public void clearScheduleBackgroundDexoptJobCallback() {
+ mInjector.getConfig().clearScheduleBackgroundDexoptJobCallback();
+ }
+
+ /**
+ * Manually starts a background dexopt job. Does nothing if a job is already started by this
+ * method or by the job scheduler. This method is not blocking.
+ *
+ * Unlike the job started by job scheduler, the job started by this method does not respect
+ * constraints described in {@link #scheduleBackgroundDexoptJob()}, and hence will not be
+ * cancelled when they aren't met.
+ *
+ * See {@link #dexoptPackages} for how to customize the behavior of the job.
+ *
+ * When the job ends (either completed or cancelled), the result is sent to the callbacks added
+ * by {@link #addDexoptDoneCallback(Executor, DexoptDoneCallback)} with the
+ * reason {@link ReasonMapping#REASON_BG_DEXOPT}.
+ */
+ public void startBackgroundDexoptJob() {
+ mInjector.getBackgroundDexoptJob().start();
+ }
+
+ /**
+ * Cancels the running background dexopt job started by the job scheduler or by {@link
+ * #startBackgroundDexoptJob()}. Does nothing if the job is not running. This method is not
+ * blocking.
+ *
+ * The result sent to the callbacks added by {@link
+ * #addDexoptDoneCallback(Executor, DexoptDoneCallback)} will contain {@link
+ * DexoptResult#DEXOPT_CANCELLED}.
+ */
+ public void cancelBackgroundDexoptJob() {
+ mInjector.getBackgroundDexoptJob().cancel();
+ }
+
+ /**
+ * Adds a global listener that listens to any result of dexopting package(s), no matter run
+ * manually or automatically. Calling this method multiple times with different callbacks is
+ * allowed. Callbacks are executed in the same order as the one in which they were added. This
+ * method is thread-safe.
+ *
+ * @param onlyIncludeUpdates if true, the results passed to the callback will only contain
+ * packages that have any update, and the callback won't be called with results that
+ * don't have any update.
+ * @throws IllegalStateException if the same callback instance is already added
+ */
+ public void addDexoptDoneCallback(boolean onlyIncludeUpdates,
+ @NonNull @CallbackExecutor Executor executor, @NonNull DexoptDoneCallback callback) {
+ mInjector.getConfig().addDexoptDoneCallback(onlyIncludeUpdates, executor, callback);
+ }
+
+ /**
+ * Removes the listener added by {@link
+ * #addDexoptDoneCallback(Executor, DexoptDoneCallback)}. Does nothing if the
+ * callback was not added. This method is thread-safe.
+ */
+ public void removeDexoptDoneCallback(@NonNull DexoptDoneCallback callback) {
+ mInjector.getConfig().removeDexoptDoneCallback(callback);
+ }
+
+ /**
+ * Snapshots the profile of the given app split. The profile snapshot is the aggregation of all
+ * existing profiles of the app split (all current user profiles and the reference profile).
+ *
+ * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on
+ * @param packageName the name of the app that owns the profile
+ * @param splitName see {@link AndroidPackageSplit#getName()}
+ * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The
+ * caller is responsible for closing it. Note that the content may be empty.
+ * @throws IllegalArgumentException if the package or the split is not found
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ * @throws SnapshotProfileException if the operation encounters an error that the caller should
+ * handle (e.g., an I/O error, a sub-process crash).
+ */
+ @NonNull
+ public ParcelFileDescriptor snapshotAppProfile(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName,
+ @Nullable String splitName) throws SnapshotProfileException {
+ var options = new MergeProfileOptions();
+ options.forceMerge = true;
+ return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options);
+ }
+
+ /**
+ * Same as above, but outputs in text format.
+ *
+ * @hide
+ */
+ @NonNull
+ public ParcelFileDescriptor dumpAppProfile(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName,
+ @Nullable String splitName, boolean dumpClassesAndMethods)
+ throws SnapshotProfileException {
+ var options = new MergeProfileOptions();
+ options.dumpOnly = !dumpClassesAndMethods;
+ options.dumpClassesAndMethods = dumpClassesAndMethods;
+ return snapshotOrDumpAppProfile(snapshot, packageName, splitName, options);
+ }
+
+ @NonNull
+ private ParcelFileDescriptor snapshotOrDumpAppProfile(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName,
+ @Nullable String splitName, @NonNull MergeProfileOptions options)
+ throws SnapshotProfileException {
+ PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+ PrimaryDexInfo dexInfo = PrimaryDexUtils.getDexInfoBySplitName(pkg, splitName);
+
+ List<ProfilePath> profiles = new ArrayList<>();
+ profiles.add(PrimaryDexUtils.buildRefProfilePath(pkgState, dexInfo));
+ profiles.addAll(
+ PrimaryDexUtils.getCurProfiles(mInjector.getUserManager(), pkgState, dexInfo));
+
+ OutputProfile output = PrimaryDexUtils.buildOutputProfile(
+ pkgState, dexInfo, Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */);
+
+ return mergeProfilesAndGetFd(profiles, output, List.of(dexInfo.dexPath()), options);
+ }
+
+ /**
+ * Snapshots the boot image profile
+ * (https://source.android.com/docs/core/bootloader/boot-image-profiles). The profile snapshot
+ * is the aggregation of all existing profiles on the device (all current user profiles and
+ * reference profiles) of all apps and the system server filtered by applicable classpaths.
+ *
+ * @param snapshot the snapshot from {@link PackageManagerLocal} to operate on
+ * @return the file descriptor of the snapshot. It doesn't have any path associated with it. The
+ * caller is responsible for closing it. Note that the content may be empty.
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ * @throws SnapshotProfileException if the operation encounters an error that the caller should
+ * handle (e.g., an I/O error, a sub-process crash).
+ */
+ @NonNull
+ public ParcelFileDescriptor snapshotBootImageProfile(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot)
+ throws SnapshotProfileException {
+ List<ProfilePath> profiles = new ArrayList<>();
+
+ // System server profiles.
+ profiles.add(AidlUtils.buildProfilePathForPrimaryRef(
+ Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY));
+ for (UserHandle handle :
+ mInjector.getUserManager().getUserHandles(true /* excludeDying */)) {
+ profiles.add(AidlUtils.buildProfilePathForPrimaryCur(handle.getIdentifier(),
+ Utils.PLATFORM_PACKAGE_NAME, PrimaryDexUtils.PROFILE_PRIMARY));
+ }
+
+ // App profiles.
+ snapshot.getPackageStates().forEach((packageName, appPkgState) -> {
+ // Hibernating apps can still provide useful profile contents, so skip the hibernation
+ // check.
+ if (Utils.canDexoptPackage(appPkgState, null /* appHibernationManager */)) {
+ AndroidPackage appPkg = Utils.getPackageOrThrow(appPkgState);
+ for (PrimaryDexInfo appDexInfo : PrimaryDexUtils.getDexInfo(appPkg)) {
+ if (!appDexInfo.hasCode()) {
+ continue;
+ }
+ profiles.add(PrimaryDexUtils.buildRefProfilePath(appPkgState, appDexInfo));
+ profiles.addAll(PrimaryDexUtils.getCurProfiles(
+ mInjector.getUserManager(), appPkgState, appDexInfo));
+ }
+ }
+ });
+
+ OutputProfile output = AidlUtils.buildOutputProfileForPrimary(Utils.PLATFORM_PACKAGE_NAME,
+ PrimaryDexUtils.PROFILE_PRIMARY, Process.SYSTEM_UID, Process.SYSTEM_UID,
+ false /* isPublic */);
+
+ List<String> dexPaths = Arrays.stream(CLASSPATHS_FOR_BOOT_IMAGE_PROFILE)
+ .map(envVar -> Constants.getenv(envVar))
+ .filter(classpath -> !TextUtils.isEmpty(classpath))
+ .flatMap(classpath -> Arrays.stream(classpath.split(":")))
+ .collect(Collectors.toList());
+
+ var options = new MergeProfileOptions();
+ options.forceMerge = true;
+ options.forBootImage = true;
+ return mergeProfilesAndGetFd(profiles, output, dexPaths, options);
+ }
+
+ /**
+ * Notifies ART Service that this is a boot that falls into one of the categories listed in
+ * {@link BootReason}. The current behavior is that ART Service goes through all recently used
+ * packages and dexopts those that are not dexopted. This might change in the future.
+ *
+ * This method is blocking. It takes about 30 seconds to a few minutes. During execution, {@code
+ * progressCallback} is repeatedly called whenever there is an update on the progress.
+ *
+ * See {@link #dexoptPackages} for how to customize the behavior.
+ */
+ public void onBoot(@NonNull @BootReason String bootReason,
+ @Nullable @CallbackExecutor Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
+ try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
+ dexoptPackages(snapshot, bootReason, new CancellationSignal(), progressCallbackExecutor,
+ progressCallback);
+ }
+ }
+
+ /**
+ * Dumps the dexopt state of all packages in text format for debugging purposes.
+ *
+ * There are no stability guarantees for the output format.
+ *
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ */
+ public void dump(
+ @NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot) {
+ new DumpHelper(this).dump(pw, snapshot);
+ }
+
+ /**
+ * Dumps the dexopt state of the given package in text format for debugging purposes.
+ *
+ * There are no stability guarantees for the output format.
+ *
+ * @throws IllegalArgumentException if the package is not found
+ * @throws IllegalStateException if the operation encounters an error that should never happen
+ * (e.g., an internal logic error).
+ */
+ public void dumpPackage(@NonNull PrintWriter pw,
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
+ new DumpHelper(this).dumpPackage(
+ pw, snapshot, Utils.getPackageStateOrThrow(snapshot, packageName));
+ }
+
+ /**
+ * Should be used by {@link BackgroundDexoptJobService} ONLY.
+ *
+ * @hide
+ */
+ @NonNull
+ BackgroundDexoptJob getBackgroundDexoptJob() {
+ return mInjector.getBackgroundDexoptJob();
+ }
+
+ private void maybeDowngradePackages(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull Set<String> excludedPackages, @NonNull CancellationSignal cancellationSignal,
+ @NonNull Executor executor) {
+ if (shouldDowngrade()) {
+ List<String> packages = getDefaultPackages(snapshot, ReasonMapping.REASON_INACTIVE)
+ .stream()
+ .filter(pkg -> !excludedPackages.contains(pkg))
+ .collect(Collectors.toList());
+ if (!packages.isEmpty()) {
+ Log.i(TAG, "Storage is low. Downgrading inactive packages");
+ mInjector.getDexoptHelper().dexopt(snapshot, packages,
+ new DexoptParams.Builder(ReasonMapping.REASON_INACTIVE).build(),
+ cancellationSignal, executor, null /* processCallbackExecutor */,
+ null /* progressCallback */);
+ } else {
+ Log.i(TAG,
+ "Storage is low, but downgrading is disabled or there's nothing to "
+ + "downgrade");
+ }
+ }
+ }
+
+ private boolean shouldDowngrade() {
+ try {
+ return mInjector.getStorageManager().getAllocatableBytes(StorageManager.UUID_DEFAULT)
+ < DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES;
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to check storage. Assuming storage not low", e);
+ return false;
+ }
+ }
+
+ /** Returns the list of packages to process for the given reason. */
+ @NonNull
+ private List<String> getDefaultPackages(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String reason) {
+ // We probably won't have an app hibernation manager in the boot time compilation, because
+ // ArtManagerLocal.onBoot needs to run early to ensure apps are compiled before the system
+ // server fires them up. This means the boot time compilation will ignore the hibernation
+ // states of the packages.
+ //
+ // TODO(b/265782156): When hibernated packages get compiled this way, the file GC will
+ // delete them again in the next background dexopt run. That means they are likely to get
+ // recreated again in the next boot dexopt (i.e. for OTA or Mainline update).
+ var appHibernationManager = mInjector.getAppHibernationManager();
+ if (reason != ReasonMapping.REASON_FIRST_BOOT
+ && reason != ReasonMapping.REASON_BOOT_AFTER_OTA
+ && reason != ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE) {
+ // Check that it's present for other compilation reasons, to ensure we don't regress
+ // silently.
+ Objects.requireNonNull(appHibernationManager);
+ }
+
+ // Filter out hibernating packages even if the reason is REASON_INACTIVE. This is because
+ // artifacts for hibernating packages are already deleted.
+ Stream<PackageState> packages = snapshot.getPackageStates().values().stream().filter(
+ pkgState -> Utils.canDexoptPackage(pkgState, appHibernationManager));
+
+ switch (reason) {
+ case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE:
+ packages = packages.filter(
+ pkgState -> mInjector.isSystemUiPackage(pkgState.getPackageName()));
+ break;
+ case ReasonMapping.REASON_INACTIVE:
+ packages = filterAndSortByLastActiveTime(
+ packages, false /* keepRecent */, false /* descending */);
+ break;
+ default:
+ // Actually, the sorting is only needed for background dexopt, but we do it for all
+ // cases for simplicity.
+ packages = filterAndSortByLastActiveTime(
+ packages, true /* keepRecent */, true /* descending */);
+ }
+
+ return packages.map(PackageState::getPackageName).collect(Collectors.toList());
+ }
+
+ @NonNull
+ private Stream<PackageState> filterAndSortByLastActiveTime(
+ @NonNull Stream<PackageState> packages, boolean keepRecent, boolean descending) {
+ // "pm.dexopt.downgrade_after_inactive_days" is repurposed to also determine whether to
+ // dexopt a package.
+ long inactiveMs = TimeUnit.DAYS.toMillis(SystemProperties.getInt(
+ "pm.dexopt.downgrade_after_inactive_days", Integer.MAX_VALUE /* def */));
+ long currentTimeMs = mInjector.getCurrentTimeMillis();
+ long thresholdTimeMs = currentTimeMs - inactiveMs;
+ return packages
+ .map(pkgState
+ -> Pair.create(pkgState,
+ Utils.getPackageLastActiveTime(pkgState,
+ mInjector.getDexUseManager(), mInjector.getUserManager())))
+ .filter(keepRecent ? (pair -> pair.second > thresholdTimeMs)
+ : (pair -> pair.second <= thresholdTimeMs))
+ .sorted(descending ? Comparator.comparingLong(pair -> - pair.second)
+ : Comparator.comparingLong(pair -> pair.second))
+ .map(pair -> pair.first);
+ }
+
+ @NonNull
+ private ParcelFileDescriptor mergeProfilesAndGetFd(@NonNull List<ProfilePath> profiles,
+ @NonNull OutputProfile output, @NonNull List<String> dexPaths,
+ @NonNull MergeProfileOptions options) throws SnapshotProfileException {
+ try {
+ boolean hasContent = false;
+ try {
+ hasContent = mInjector.getArtd().mergeProfiles(
+ profiles, null /* referenceProfile */, output, dexPaths, options);
+ } catch (ServiceSpecificException e) {
+ throw new SnapshotProfileException(e);
+ }
+
+ String path = hasContent ? output.profilePath.tmpPath : "/dev/null";
+ ParcelFileDescriptor fd;
+ try {
+ fd = ParcelFileDescriptor.open(new File(path), ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ throw new IllegalStateException(
+ String.format("Failed to open profile snapshot '%s'", path), e);
+ }
+
+ if (hasContent) {
+ // This is done on the open file so that only the FD keeps a reference to its
+ // contents.
+ mInjector.getArtd().deleteProfile(ProfilePath.tmpProfilePath(output.profilePath));
+ }
+
+ return fd;
+ } catch (RemoteException e) {
+ throw new IllegalStateException("An error occurred when calling artd", e);
+ }
+ }
+
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ public interface BatchDexoptStartCallback {
+ /**
+ * Mutates {@code builder} to override the default params for {@link #dexoptPackages}. It
+ * must ignore unknown reasons because more reasons may be added in the future.
+ *
+ * This is called before the start of any automatic package dexopt (i.e., not
+ * including package dexopt initiated by the {@link #dexoptPackage} API call).
+ *
+ * If {@code builder.setPackages} is not called, {@code defaultPackages} will be used as the
+ * list of packages to dexopt.
+ *
+ * If {@code builder.setDexoptParams} is not called, the default params built from {@code
+ * new DexoptParams.Builder(reason)} will to used as the params for dexopting each
+ * package.
+ *
+ * Additionally, {@code cancellationSignal.cancel()} can be called to cancel this operation.
+ * If this operation is initiated by the job scheduler and the {@code reason} is {@link
+ * ReasonMapping#REASON_BG_DEXOPT}, the job will be retried in the next <i>maintenance
+ * window</i>. For information about <i>maintenance window</i>, see
+ * https://developer.android.com/training/monitoring-device-state/doze-standby.
+ *
+ * Changing the reason is not allowed. Doing so will result in {@link IllegalStateException}
+ * when {@link #dexoptPackages} is called.
+ */
+ void onBatchDexoptStart(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull @BatchDexoptReason String reason, @NonNull List<String> defaultPackages,
+ @NonNull BatchDexoptParams.Builder builder,
+ @NonNull CancellationSignal cancellationSignal);
+ }
+
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ public interface ScheduleBackgroundDexoptJobCallback {
+ /**
+ * Mutates {@code builder} to override the configuration of the background dexopt job.
+ *
+ * The default configuration described in {@link
+ * ArtManagerLocal#scheduleBackgroundDexoptJob()} is passed to the callback as the {@code
+ * builder} argument.
+ *
+ * Setting {@link JobInfo.Builder#setRequiresStorageNotLow(boolean)} is not allowed. Doing
+ * so will result in {@link IllegalStateException} when {@link
+ * #scheduleBackgroundDexoptJob()} is called. ART Service has its own storage check, which
+ * skips package dexopt when the storage is low. The storage check is enabled by
+ * default for background dexopt jobs. {@link
+ * #setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback)} can be used to disable
+ * the storage check by clearing the {@link ArtFlags#FLAG_SKIP_IF_STORAGE_LOW} flag.
+ */
+ void onOverrideJobInfo(@NonNull JobInfo.Builder builder);
+ }
+
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ public interface DexoptDoneCallback {
+ void onDexoptDone(@NonNull DexoptResult result);
+ }
+
+ /**
+ * Represents an error that happens when snapshotting profiles.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ public static class SnapshotProfileException extends Exception {
+ public SnapshotProfileException(@NonNull Throwable cause) {
+ super(cause);
+ }
+ }
+
+ /**
+ * Injector pattern for testing purpose.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Injector {
+ @Nullable private final Context mContext;
+ @Nullable private final PackageManagerLocal mPackageManagerLocal;
+ @Nullable private final Config mConfig;
+ @Nullable private final BackgroundDexoptJob mBgDexoptJob;
+
+ Injector(@NonNull ArtManagerLocal artManagerLocal, @Nullable Context context) {
+ mContext = context;
+ if (context != null) {
+ // We only need them on Android U and above, where a context is passed.
+ mPackageManagerLocal = Objects.requireNonNull(
+ LocalManagerRegistry.getManager(PackageManagerLocal.class));
+ mConfig = new Config();
+ mBgDexoptJob = new BackgroundDexoptJob(context, artManagerLocal, mConfig);
+
+ // Call the getters for the dependencies that aren't optional, to ensure correct
+ // initialization order.
+ getDexoptHelper();
+ getUserManager();
+ getDexUseManager();
+ getStorageManager();
+ ArtModuleServiceInitializer.getArtModuleServiceManager();
+ } else {
+ mPackageManagerLocal = null;
+ mConfig = null;
+ mBgDexoptJob = null;
+ }
+ }
+
+ @NonNull
+ public Context getContext() {
+ return Objects.requireNonNull(mContext);
+ }
+
+ @NonNull
+ public PackageManagerLocal getPackageManagerLocal() {
+ return Objects.requireNonNull(mPackageManagerLocal);
+ }
+
+ @NonNull
+ public IArtd getArtd() {
+ return Utils.getArtd();
+ }
+
+ @NonNull
+ public DexoptHelper getDexoptHelper() {
+ return new DexoptHelper(getContext(), getConfig());
+ }
+
+ @NonNull
+ public Config getConfig() {
+ return mConfig;
+ }
+
+ /**
+ * Returns the registered AppHibernationManager instance.
+ *
+ * It may be null because ArtManagerLocal needs to be available early to compile packages at
+ * boot with {@link onBoot}, before the hibernation manager has been initialized. It should
+ * not be null for other dexopt calls.
+ */
+ @Nullable
+ public AppHibernationManager getAppHibernationManager() {
+ return mContext.getSystemService(AppHibernationManager.class);
+ }
+
+ @NonNull
+ public BackgroundDexoptJob getBackgroundDexoptJob() {
+ return Objects.requireNonNull(mBgDexoptJob);
+ }
+
+ @NonNull
+ public UserManager getUserManager() {
+ return Objects.requireNonNull(mContext.getSystemService(UserManager.class));
+ }
+
+ @NonNull
+ public DexUseManagerLocal getDexUseManager() {
+ return Objects.requireNonNull(
+ LocalManagerRegistry.getManager(DexUseManagerLocal.class));
+ }
+
+ @NonNull
+ public boolean isSystemUiPackage(@NonNull String packageName) {
+ return packageName.equals(mContext.getString(R.string.config_systemUi));
+ }
+
+ public long getCurrentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ @NonNull
+ public StorageManager getStorageManager() {
+ return Objects.requireNonNull(mContext.getSystemService(StorageManager.class));
+ }
+ }
}
diff --git a/libartservice/service/java/com/android/server/art/ArtModuleServiceInitializer.java b/libartservice/service/java/com/android/server/art/ArtModuleServiceInitializer.java
new file mode 100644
index 0000000..e6d789a
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/ArtModuleServiceInitializer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.ArtModuleServiceManager;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+
+/**
+ * Class for performing registration for the ART mainline module.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public class ArtModuleServiceInitializer {
+ private ArtModuleServiceInitializer() {}
+
+ @NonNull private static Object sLock = new Object();
+ @GuardedBy("sLock") @Nullable private static ArtModuleServiceManager sArtModuleServiceManager;
+
+ /**
+ * Sets an instance of {@link ArtModuleServiceManager} that allows the ART mainline module to
+ * obtain ART binder services. This is called by the platform during the system server
+ * initialization.
+ */
+ public static void setArtModuleServiceManager(
+ @NonNull ArtModuleServiceManager artModuleServiceManager) {
+ synchronized (sLock) {
+ if (sArtModuleServiceManager != null) {
+ throw new IllegalStateException("ArtModuleServiceManager is already set");
+ }
+ sArtModuleServiceManager = artModuleServiceManager;
+ }
+ }
+
+ /** @hide */
+ @NonNull
+ public static ArtModuleServiceManager getArtModuleServiceManager() {
+ synchronized (sLock) {
+ return Objects.requireNonNull(sArtModuleServiceManager);
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/ArtShellCommand.java b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
new file mode 100644
index 0000000..6fd1dc5
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/ArtShellCommand.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static android.os.ParcelFileDescriptor.AutoCloseInputStream;
+
+import static com.android.server.art.ArtManagerLocal.SnapshotProfileException;
+import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
+import static com.android.server.art.model.ArtFlags.DexoptFlags;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.DexoptResultStatus;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
+import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.DeleteResult;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.model.DexoptStatus;
+import com.android.server.art.model.OperationProgress;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+
+import libcore.io.Streams;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+/**
+ * This class handles ART shell commands.
+ *
+ * @hide
+ */
+public final class ArtShellCommand extends BasicShellCommandHandler {
+ private static final String TAG = "ArtShellCommand";
+
+ /** The default location for profile dumps. */
+ private final static String PROFILE_DEBUG_LOCATION = "/data/misc/profman";
+
+ private final ArtManagerLocal mArtManagerLocal;
+ private final PackageManagerLocal mPackageManagerLocal;
+ private final DexUseManagerLocal mDexUseManager;
+
+ @GuardedBy("sCancellationSignalMap")
+ @NonNull
+ private static final Map<String, CancellationSignal> sCancellationSignalMap = new HashMap<>();
+
+ public ArtShellCommand(@NonNull ArtManagerLocal artManagerLocal,
+ @NonNull PackageManagerLocal packageManagerLocal,
+ @NonNull DexUseManagerLocal dexUseManager) {
+ mArtManagerLocal = artManagerLocal;
+ mPackageManagerLocal = packageManagerLocal;
+ mDexUseManager = dexUseManager;
+ }
+
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Override
+ public int onCommand(String cmd) {
+ PrintWriter pw = getOutPrintWriter();
+ try (var snapshot = mPackageManagerLocal.withFilteredSnapshot()) {
+ switch (cmd) {
+ case "compile":
+ case "reconcile-secondary-dex-files":
+ case "force-dex-opt":
+ case "bg-dexopt-job":
+ case "cancel-bg-dexopt-job":
+ case "delete-dexopt":
+ case "dump-profiles":
+ case "snapshot-profile":
+ // TODO(b/263247832): Implement this.
+ throw new UnsupportedOperationException();
+ case "art":
+ return handleArtCommand(pw, snapshot);
+ default:
+ // Can't happen. Only supported commands are forwarded to ART Service.
+ throw new IllegalArgumentException(
+ String.format("Unexpected command '%s' forwarded to ART Service", cmd));
+ }
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private int handleArtCommand(
+ @NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot) {
+ enforceRoot();
+ String subcmd = getNextArgRequired();
+ switch (subcmd) {
+ case "delete-dexopt-artifacts": {
+ DeleteResult result =
+ mArtManagerLocal.deleteDexoptArtifacts(snapshot, getNextArgRequired());
+ pw.printf("Freed %d bytes\n", result.getFreedBytes());
+ return 0;
+ }
+ case "get-dexopt-status": {
+ DexoptStatus dexoptStatus = mArtManagerLocal.getDexoptStatus(
+ snapshot, getNextArgRequired(), ArtFlags.defaultGetStatusFlags());
+ pw.println(dexoptStatus);
+ return 0;
+ }
+ case "dexopt-package": {
+ var paramsBuilder = new DexoptParams.Builder("cmdline");
+ String opt;
+ @DexoptFlags int scopeFlags = 0;
+ boolean forSingleSplit = false;
+ boolean reset = false;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-m":
+ paramsBuilder.setCompilerFilter(getNextArgRequired());
+ break;
+ case "-f":
+ paramsBuilder.setFlags(ArtFlags.FLAG_FORCE, ArtFlags.FLAG_FORCE);
+ break;
+ case "--primary-dex":
+ scopeFlags |= ArtFlags.FLAG_FOR_PRIMARY_DEX;
+ break;
+ case "--secondary-dex":
+ scopeFlags |= ArtFlags.FLAG_FOR_SECONDARY_DEX;
+ break;
+ case "--include-dependencies":
+ scopeFlags |= ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES;
+ break;
+ case "--split":
+ String splitName = getNextArgRequired();
+ forSingleSplit = true;
+ paramsBuilder
+ .setFlags(ArtFlags.FLAG_FOR_SINGLE_SPLIT,
+ ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName(!splitName.isEmpty() ? splitName : null);
+ break;
+ case "--reset":
+ reset = true;
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ if (forSingleSplit) {
+ if (scopeFlags != 0) {
+ pw.println("'--primary-dex', '--secondary-dex', and "
+ + "'--include-dependencies' must not be set when '--split' is "
+ + "set.");
+ return 1;
+ }
+ scopeFlags = ArtFlags.FLAG_FOR_PRIMARY_DEX;
+ }
+ if (scopeFlags != 0) {
+ paramsBuilder.setFlags(scopeFlags,
+ ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES);
+ }
+
+ DexoptResult result;
+ try (var signal = new WithCancellationSignal(pw)) {
+ if (reset) {
+ result = mArtManagerLocal.resetDexoptStatus(
+ snapshot, getNextArgRequired(), signal.get());
+ } else {
+ result = mArtManagerLocal.dexoptPackage(snapshot, getNextArgRequired(),
+ paramsBuilder.build(), signal.get());
+ }
+ }
+ printDexoptResult(pw, result);
+ return 0;
+ }
+ case "dexopt-packages": {
+ DexoptResult result;
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ try (var signal = new WithCancellationSignal(pw)) {
+ result = mArtManagerLocal.dexoptPackages(
+ snapshot, getNextArgRequired(), signal.get(), executor, progress -> {
+ pw.println(String.format(
+ "Dexopting apps: %d%%", progress.getPercentage()));
+ pw.flush();
+ });
+ Utils.executeAndWait(executor, () -> printDexoptResult(pw, result));
+ } finally {
+ executor.shutdown();
+ }
+ return 0;
+ }
+ case "cancel": {
+ String jobId = getNextArgRequired();
+ CancellationSignal signal;
+ synchronized (sCancellationSignalMap) {
+ signal = sCancellationSignalMap.getOrDefault(jobId, null);
+ }
+ if (signal == null) {
+ pw.println("Job not found");
+ return 1;
+ }
+ signal.cancel();
+ pw.println("Job cancelled");
+ return 0;
+ }
+ case "dex-use-notify": {
+ mDexUseManager.notifyDexContainersLoaded(snapshot, getNextArgRequired(),
+ Map.of(getNextArgRequired(), getNextArgRequired()));
+ return 0;
+ }
+ case "dump": {
+ String packageName = getNextArg();
+ if (packageName != null) {
+ mArtManagerLocal.dumpPackage(pw, snapshot, packageName);
+ } else {
+ mArtManagerLocal.dump(pw, snapshot);
+ }
+ return 0;
+ }
+ case "dex-use-dump": {
+ pw.println(mDexUseManager.dump());
+ return 0;
+ }
+ case "bg-dexopt-job": {
+ String opt = getNextOption();
+ if (opt == null) {
+ mArtManagerLocal.startBackgroundDexoptJob();
+ return 0;
+ }
+ switch (opt) {
+ case "--cancel": {
+ mArtManagerLocal.cancelBackgroundDexoptJob();
+ return 0;
+ }
+ case "--enable": {
+ // This operation requires the uid to be "system" (1000).
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ mArtManagerLocal.scheduleBackgroundDexoptJob();
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return 0;
+ }
+ case "--disable": {
+ // This operation requires the uid to be "system" (1000).
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ mArtManagerLocal.unscheduleBackgroundDexoptJob();
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ return 0;
+ }
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ case "snapshot-app-profile": {
+ String packageName = getNextArgRequired();
+ String splitName = getNextArg();
+ String outputRelativePath = String.format("%s%s.prof", packageName,
+ splitName != null ? String.format("-split_%s.apk", splitName) : "");
+ ParcelFileDescriptor fd;
+ try {
+ fd = mArtManagerLocal.snapshotAppProfile(snapshot, packageName, splitName);
+ } catch (SnapshotProfileException e) {
+ throw new RuntimeException(e);
+ }
+ writeProfileFdContentsToFile(fd, outputRelativePath);
+ return 0;
+ }
+ case "snapshot-boot-image-profile": {
+ String outputRelativePath = "android.prof";
+ ParcelFileDescriptor fd;
+ try {
+ fd = mArtManagerLocal.snapshotBootImageProfile(snapshot);
+ } catch (SnapshotProfileException e) {
+ throw new RuntimeException(e);
+ }
+ writeProfileFdContentsToFile(fd, outputRelativePath);
+ return 0;
+ }
+ case "dump-profiles": {
+ boolean dumpClassesAndMethods = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--dump-classes-and-methods": {
+ dumpClassesAndMethods = true;
+ break;
+ }
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ String packageName = getNextArgRequired();
+ PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+ for (PrimaryDexInfo dexInfo : PrimaryDexUtils.getDexInfo(pkg)) {
+ if (!dexInfo.hasCode()) {
+ continue;
+ }
+ String profileName = PrimaryDexUtils.getProfileName(dexInfo.splitName());
+ // The path is intentionally inconsistent with the one for
+ // "snapshot-app-profile". The is to match the behavior of the legacy PM shell
+ // command.
+ String outputRelativePath =
+ String.format("%s-%s.prof.txt", packageName, profileName);
+ ParcelFileDescriptor fd;
+ try {
+ fd = mArtManagerLocal.dumpAppProfile(
+ snapshot, packageName, dexInfo.splitName(), dumpClassesAndMethods);
+ } catch (SnapshotProfileException e) {
+ throw new RuntimeException(e);
+ }
+ writeProfileFdContentsToFile(fd, outputRelativePath);
+ }
+ return 0;
+ }
+ default:
+ pw.println(String.format("Unknown 'art' sub-command '%s'", subcmd));
+ pw.println("See 'cmd package help' for help");
+ return 1;
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ // No one should call this. The help text should be printed by the `onHelp` handler of `cmd
+ // package`.
+ throw new UnsupportedOperationException("Unexpected call to 'onHelp'");
+ }
+
+ public static void printHelp(@NonNull PrintWriter pw) {
+ // TODO(b/263247832): Write help text about root-level commands.
+ pw.println("art SUB_COMMAND [ARGS]...");
+ pw.println(" Run ART Service commands");
+ pw.println(" Note: The commands are used for internal debugging purposes only. There are");
+ pw.println(" no stability guarantees for them.");
+ pw.println();
+ pw.println(" Supported sub-commands:");
+ pw.println(" delete-dexopt-artifacts PACKAGE_NAME");
+ pw.println(" Delete the dexopt artifacts of both primary dex files and secondary");
+ pw.println(" dex files of a package.");
+ pw.println(" get-dexopt-status PACKAGE_NAME");
+ pw.println(" Print the dexopt status of both primary dex files and secondary dex");
+ pw.println(" files of a package.");
+ pw.println(" dexopt-package [-m COMPILER_FILTER] [-f] [--primary-dex]");
+ pw.println(" [--secondary-dex] [--include-dependencies] [--split SPLIT_NAME]");
+ pw.println(" PACKAGE_NAME");
+ pw.println(" Dexopt a package.");
+ pw.println(" If none of '--primary-dex', '--secondary-dex', and");
+ pw.println(" '--include-dependencies' is set, the command dexopts all of them.");
+ pw.println(" The command prints a job ID, which can be used to cancel the job using");
+ pw.println(" the 'cancel' command.");
+ pw.println(" Options:");
+ pw.println(" -m Set the compiler filter.");
+ pw.println(" -f Force compilation.");
+ pw.println(" --primary-dex Dexopt primary dex files.");
+ pw.println(" --secondary-dex Dexopt secondary dex files.");
+ pw.println(" --include-dependencies Include dependencies.");
+ pw.println(" --split SPLIT_NAME Only dexopt the given split. If SPLIT_NAME is an");
+ pw.println(" empty string, only dexopt the base APK. When this option is set,");
+ pw.println(" '--primary-dex', '--secondary-dex', and '--include-dependencies' must");
+ pw.println(" not be set.");
+ pw.println(" --reset Reset the dexopt state of the package as if the package");
+ pw.println(" is newly installed.");
+ pw.println(" More specifically, it clears reference profiles, current profiles,");
+ pw.println(" and any code compiled from those local profiles. If there is an");
+ pw.println(" external profile (e.g., a cloud profile), the code compiled from that");
+ pw.println(" profile will be kept.");
+ pw.println(" For secondary dex files, it also clears all dexopt artifacts.");
+ pw.println(" When this flag is set, all the other flags are ignored.");
+ pw.println(" dexopt-packages REASON");
+ pw.println(" Run batch dexopt for the given reason.");
+ pw.println(" The command prints a job ID, which can be used to cancel the job using");
+ pw.println(" the 'cancel' command.");
+ pw.println(" cancel JOB_ID");
+ pw.println(" Cancel a job.");
+ pw.println(" dex-use-notify PACKAGE_NAME DEX_PATH CLASS_LOADER_CONTEXT");
+ pw.println(" Notify that a dex file is loaded with the given class loader context by");
+ pw.println(" the given package.");
+ pw.println(" dump [PACKAGE_NAME]");
+ pw.println(" Dumps the dexopt state in text format to stdout.");
+ pw.println(" If PACKAGE_NAME is empty, the command is for all packages. Otherwise, it");
+ pw.println(" is for the given package.");
+ pw.println(" dex-use-dump");
+ pw.println(" Print all dex use information in textproto format.");
+ pw.println(" bg-dexopt-job [--cancel | --disable | --enable]");
+ pw.println(" Control the background dexopt job.");
+ pw.println(" Without flags, it starts a background dexopt job immediately. It does");
+ pw.println(" nothing if a job is already started either automatically by the system");
+ pw.println(" or through this command. This command is not blocking.");
+ pw.println(" Options:");
+ pw.println(" --cancel Cancel any currently running background dexopt job");
+ pw.println(" immediately. This cancels jobs started either automatically by the");
+ pw.println(" system or through this command. This command is not blocking.");
+ pw.println(" --disable: Disable the background dexopt job from being started by the");
+ pw.println(" job scheduler. If a job is already started by the job scheduler and");
+ pw.println(" is running, it will be cancelled immediately. Does not affect");
+ pw.println(" jobs started through this command or by the system in other ways.");
+ pw.println(" This state will be lost when the system_server process exits.");
+ pw.println(" --enable: Enable the background dexopt job to be started by the job");
+ pw.println(" scheduler again, if previously disabled by --disable.");
+ pw.println(" snapshot-app-profile PACKAGE_NAME [SPLIT_NAME]");
+ pw.println(" Snapshot the profile of the given app and save it to");
+ pw.println(" '" + PROFILE_DEBUG_LOCATION + "'.");
+ pw.println(" If SPLIT_NAME is empty, the command is for the base APK, and the output");
+ pw.println(" filename is 'PACKAGE_NAME.prof'. Otherwise, the command is for the given");
+ pw.println(" split, and the output filename is");
+ pw.println(" 'PACKAGE_NAME-split_SPLIT_NAME.apk.prof'.");
+ pw.println(" snapshot-boot-image-profile");
+ pw.println(" Snapshot the boot image profile and save it to");
+ pw.println(" '" + PROFILE_DEBUG_LOCATION + "/android.prof'.");
+ pw.println(" dump-profiles [--dump-classes-and-methods] PACKAGE_NAME");
+ pw.println(" Dump the profiles of the given app in text format and save the outputs to");
+ pw.println(" '" + PROFILE_DEBUG_LOCATION + "'.");
+ pw.println(" The profile of the base APK is dumped to 'PACKAGE_NAME-primary.prof.txt'");
+ pw.println(" The profile of a split APK is dumped to");
+ pw.println(" 'PACKAGE_NAME-SPLIT_NAME.split.prof.txt'");
+ }
+
+ private void enforceRoot() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.ROOT_UID) {
+ throw new SecurityException("ART service shell commands need root access");
+ }
+ }
+
+ @NonNull
+ private String dexoptResultStatusToString(@DexoptResultStatus int status) {
+ switch (status) {
+ case DexoptResult.DEXOPT_SKIPPED:
+ return "SKIPPED";
+ case DexoptResult.DEXOPT_PERFORMED:
+ return "PERFORMED";
+ case DexoptResult.DEXOPT_FAILED:
+ return "FAILED";
+ case DexoptResult.DEXOPT_CANCELLED:
+ return "CANCELLED";
+ }
+ throw new IllegalArgumentException("Unknown dexopt status " + status);
+ }
+
+ private void printDexoptResult(@NonNull PrintWriter pw, @NonNull DexoptResult result) {
+ pw.println(dexoptResultStatusToString(result.getFinalStatus()));
+ for (PackageDexoptResult packageResult : result.getPackageDexoptResults()) {
+ pw.printf("[%s]\n", packageResult.getPackageName());
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ pw.println(fileResult);
+ }
+ }
+ }
+
+ private void writeProfileFdContentsToFile(
+ @NonNull ParcelFileDescriptor fd, @NonNull String outputRelativePath) {
+ try {
+ StructStat st = Os.stat(PROFILE_DEBUG_LOCATION);
+ if (st.st_uid != Process.SYSTEM_UID || st.st_gid != Process.SHELL_UID
+ || (st.st_mode & 0007) != 0) {
+ throw new RuntimeException(
+ String.format("%s has wrong permissions: uid=%d, gid=%d, mode=%o",
+ PROFILE_DEBUG_LOCATION, st.st_uid, st.st_gid, st.st_mode));
+ }
+ } catch (ErrnoException e) {
+ throw new RuntimeException("Unable to stat " + PROFILE_DEBUG_LOCATION, e);
+ }
+ Path outputPath = Paths.get(PROFILE_DEBUG_LOCATION, outputRelativePath);
+ try (InputStream inputStream = new AutoCloseInputStream(fd);
+ FileOutputStream outputStream = new FileOutputStream(outputPath.toFile())) {
+ // The system server doesn't have the permission to chown the file to "shell", so we
+ // make it readable by everyone and put it in a directory that is only accessible by
+ // "shell", which is created by system/core/rootdir/init.rc. The permissions are
+ // verified by the code above.
+ Os.fchmod(outputStream.getFD(), 0644);
+ Streams.copy(inputStream, outputStream);
+ } catch (IOException | ErrnoException e) {
+ Utils.deleteIfExistsSafe(outputPath);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static class WithCancellationSignal implements AutoCloseable {
+ @NonNull private final CancellationSignal mSignal = new CancellationSignal();
+ @NonNull private final String mJobId;
+
+ public WithCancellationSignal(@NonNull PrintWriter pw) {
+ mJobId = UUID.randomUUID().toString();
+ pw.printf("Job ID: %s\n", mJobId);
+ pw.flush();
+
+ synchronized (sCancellationSignalMap) {
+ sCancellationSignalMap.put(mJobId, mSignal);
+ }
+ }
+
+ @NonNull
+ public CancellationSignal get() {
+ return mSignal;
+ }
+
+ public void close() {
+ synchronized (sCancellationSignalMap) {
+ sCancellationSignalMap.remove(mJobId);
+ }
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
new file mode 100644
index 0000000..9f03107
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJob.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.ArtManagerLocal.ScheduleBackgroundDexoptJobCallback;
+import static com.android.server.art.model.ArtFlags.ScheduleStatus;
+import static com.android.server.art.model.Config.Callback;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.CancellationSignal;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.Config;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.pm.PackageManagerLocal;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+/** @hide */
+public class BackgroundDexoptJob {
+ private static final String TAG = "BackgroundDexoptJob";
+
+ /**
+ * "android" is the package name for a <service> declared in
+ * frameworks/base/core/res/AndroidManifest.xml
+ */
+ private static final String JOB_PKG_NAME = Utils.PLATFORM_PACKAGE_NAME;
+ /** An arbitrary number. Must be unique among all jobs owned by the system uid. */
+ private static final int JOB_ID = 27873780;
+
+ @VisibleForTesting public static final long JOB_INTERVAL_MS = TimeUnit.DAYS.toMillis(1);
+
+ @NonNull private final Injector mInjector;
+
+ @GuardedBy("this") @Nullable private CompletableFuture<Result> mRunningJob = null;
+ @GuardedBy("this") @Nullable private CancellationSignal mCancellationSignal = null;
+ @GuardedBy("this") @NonNull private Optional<Integer> mLastStopReason = Optional.empty();
+
+ public BackgroundDexoptJob(@NonNull Context context, @NonNull ArtManagerLocal artManagerLocal,
+ @NonNull Config config) {
+ this(new Injector(context, artManagerLocal, config));
+ }
+
+ @VisibleForTesting
+ public BackgroundDexoptJob(@NonNull Injector injector) {
+ mInjector = injector;
+ }
+
+ /** Handles {@link BackgroundDexoptJobService#onStartJob(JobParameters)}. */
+ public boolean onStartJob(
+ @NonNull BackgroundDexoptJobService jobService, @NonNull JobParameters params) {
+ start().thenAcceptAsync(result -> {
+ writeStats(result);
+ // This is a periodic job, where the interval is specified in the `JobInfo`. "true"
+ // means to execute again during a future idle maintenance window in the same
+ // interval, while "false" means not to execute again during a future idle maintenance
+ // window in the same interval but to execute again in the next interval.
+ // This call will be ignored if `onStopJob` is called.
+ boolean wantsReschedule = result instanceof CompletedResult
+ && ((CompletedResult) result).dexoptResult().getFinalStatus()
+ == DexoptResult.DEXOPT_CANCELLED;
+ jobService.jobFinished(params, wantsReschedule);
+ });
+ // "true" means the job will continue running until `jobFinished` is called.
+ return true;
+ }
+
+ /** Handles {@link BackgroundDexoptJobService#onStopJob(JobParameters)}. */
+ public boolean onStopJob(@NonNull JobParameters params) {
+ synchronized (this) {
+ mLastStopReason = Optional.of(params.getStopReason());
+ }
+ cancel();
+ // "true" means to execute again during a future idle maintenance window in the same
+ // interval.
+ return true;
+ }
+
+ /** Handles {@link ArtManagerLocal#scheduleBackgroundDexoptJob()}. */
+ public @ScheduleStatus int schedule() {
+ if (this != BackgroundDexoptJobService.getJob()) {
+ throw new IllegalStateException("This job cannot be scheduled");
+ }
+
+ if (SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt", false /* def */)) {
+ Log.i(TAG, "Job is disabled by system property 'pm.dexopt.disable_bg_dexopt'");
+ return ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP;
+ }
+
+ JobInfo.Builder builder =
+ new JobInfo
+ .Builder(JOB_ID,
+ new ComponentName(
+ JOB_PKG_NAME, BackgroundDexoptJobService.class.getName()))
+ .setPeriodic(JOB_INTERVAL_MS)
+ .setRequiresDeviceIdle(true)
+ .setRequiresCharging(true)
+ .setRequiresBatteryNotLow(true);
+
+ Callback<ScheduleBackgroundDexoptJobCallback, Void> callback =
+ mInjector.getConfig().getScheduleBackgroundDexoptJobCallback();
+ if (callback != null) {
+ Utils.executeAndWait(
+ callback.executor(), () -> { callback.get().onOverrideJobInfo(builder); });
+ }
+
+ JobInfo info = builder.build();
+ if (info.isRequireStorageNotLow()) {
+ // See the javadoc of
+ // `ArtManagerLocal.ScheduleBackgroundDexoptJobCallback.onOverrideJobInfo` for details.
+ throw new IllegalStateException("'setRequiresStorageNotLow' must not be set");
+ }
+
+ return mInjector.getJobScheduler().schedule(info) == JobScheduler.RESULT_SUCCESS
+ ? ArtFlags.SCHEDULE_SUCCESS
+ : ArtFlags.SCHEDULE_JOB_SCHEDULER_FAILURE;
+ }
+
+ /** Handles {@link ArtManagerLocal#unscheduleBackgroundDexoptJob()}. */
+ public void unschedule() {
+ if (this != BackgroundDexoptJobService.getJob()) {
+ throw new IllegalStateException("This job cannot be unscheduled");
+ }
+
+ mInjector.getJobScheduler().cancel(JOB_ID);
+ }
+
+ @NonNull
+ public synchronized CompletableFuture<Result> start() {
+ if (mRunningJob != null) {
+ Log.i(TAG, "Job is already running");
+ return mRunningJob;
+ }
+
+ mCancellationSignal = new CancellationSignal();
+ mLastStopReason = Optional.empty();
+ mRunningJob = new CompletableFuture().supplyAsync(() -> {
+ Log.i(TAG, "Job started");
+ try {
+ return run(mCancellationSignal);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Fatal error", e);
+ return new FatalErrorResult();
+ } finally {
+ Log.i(TAG, "Job finished");
+ synchronized (this) {
+ mRunningJob = null;
+ mCancellationSignal = null;
+ }
+ }
+ });
+ return mRunningJob;
+ }
+
+ public synchronized void cancel() {
+ if (mRunningJob == null) {
+ Log.i(TAG, "Job is not running");
+ return;
+ }
+
+ mCancellationSignal.cancel();
+ Log.i(TAG, "Job cancelled");
+ }
+
+ @NonNull
+ private CompletedResult run(@NonNull CancellationSignal cancellationSignal) {
+ // TODO(b/254013427): Cleanup dex use info.
+ // TODO(b/254013425): Cleanup unused secondary dex file artifacts.
+ long startTimeMs = SystemClock.uptimeMillis();
+ DexoptResult dexoptResult;
+ try (var snapshot = mInjector.getPackageManagerLocal().withFilteredSnapshot()) {
+ dexoptResult = mInjector.getArtManagerLocal().dexoptPackages(snapshot,
+ ReasonMapping.REASON_BG_DEXOPT, cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */);
+ }
+ return CompletedResult.create(dexoptResult, SystemClock.uptimeMillis() - startTimeMs);
+ }
+
+ private void writeStats(@NonNull Result result) {
+ Optional<Integer> stopReason;
+ synchronized (this) {
+ stopReason = mLastStopReason;
+ }
+ if (result instanceof CompletedResult) {
+ var completedResult = (CompletedResult) result;
+ ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED,
+ getStatusForStats(completedResult, stopReason),
+ stopReason.orElse(JobParameters.STOP_REASON_UNDEFINED),
+ completedResult.durationMs(), 0 /* deprecated */);
+ } else if (result instanceof FatalErrorResult) {
+ ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED,
+ ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_FATAL_ERROR,
+ JobParameters.STOP_REASON_UNDEFINED, 0 /* durationMs */, 0 /* deprecated */);
+ }
+ }
+
+ private int getStatusForStats(@NonNull CompletedResult result, Optional<Integer> stopReason) {
+ if (result.dexoptResult().getFinalStatus() == DexoptResult.DEXOPT_CANCELLED) {
+ if (stopReason.isPresent()) {
+ return ArtStatsLog
+ .BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BY_CANCELLATION;
+ } else {
+ return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BY_API;
+ }
+ }
+
+ boolean isSkippedDueToStorageLow =
+ result.dexoptResult()
+ .getPackageDexoptResults()
+ .stream()
+ .flatMap(packageResult
+ -> packageResult.getDexContainerFileDexoptResults().stream())
+ .anyMatch(fileResult -> fileResult.isSkippedDueToStorageLow());
+ if (isSkippedDueToStorageLow) {
+ return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_NO_SPACE_LEFT;
+ }
+
+ return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_JOB_FINISHED;
+ }
+
+ static abstract class Result {}
+ static class FatalErrorResult extends Result {}
+
+ @AutoValue
+ static abstract class CompletedResult extends Result {
+ abstract @NonNull DexoptResult dexoptResult();
+ abstract long durationMs();
+
+ @NonNull
+ static CompletedResult create(@NonNull DexoptResult dexoptResult, long durationMs) {
+ return new AutoValue_BackgroundDexoptJob_CompletedResult(dexoptResult, durationMs);
+ }
+ }
+
+ /**
+ * Injector pattern for testing purpose.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Injector {
+ @NonNull private final Context mContext;
+ @NonNull private final ArtManagerLocal mArtManagerLocal;
+ @NonNull private final Config mConfig;
+
+ Injector(@NonNull Context context, @NonNull ArtManagerLocal artManagerLocal,
+ @NonNull Config config) {
+ mContext = context;
+ mArtManagerLocal = artManagerLocal;
+ mConfig = config;
+
+ // Call the getters for various dependencies, to ensure correct initialization order.
+ getPackageManagerLocal();
+ getJobScheduler();
+ }
+
+ @NonNull
+ public ArtManagerLocal getArtManagerLocal() {
+ return mArtManagerLocal;
+ }
+
+ @NonNull
+ public PackageManagerLocal getPackageManagerLocal() {
+ return LocalManagerRegistry.getManager(PackageManagerLocal.class);
+ }
+
+ @NonNull
+ public Config getConfig() {
+ return mConfig;
+ }
+
+ @NonNull
+ public JobScheduler getJobScheduler() {
+ return mContext.getSystemService(JobScheduler.class);
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/BackgroundDexoptJobService.java b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobService.java
new file mode 100644
index 0000000..d6932b1
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/BackgroundDexoptJobService.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+
+import com.android.server.LocalManagerRegistry;
+
+/**
+ * Entry point for the callback from the job scheduler. This class is instantiated by the system
+ * automatically.
+ *
+ * @hide
+ */
+public class BackgroundDexoptJobService extends JobService {
+ @Override
+ public boolean onStartJob(@NonNull JobParameters params) {
+ return getJob().onStartJob(this, params);
+ }
+
+ @Override
+ public boolean onStopJob(@NonNull JobParameters params) {
+ return getJob().onStopJob(params);
+ }
+
+ @NonNull
+ static BackgroundDexoptJob getJob() {
+ return LocalManagerRegistry.getManager(ArtManagerLocal.class).getBackgroundDexoptJob();
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/Constants.java b/libartservice/service/java/com/android/server/art/Constants.java
new file mode 100644
index 0000000..2d2d757
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/Constants.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Build;
+import android.system.Os;
+
+/**
+ * A mockable wrapper class for device-specific constants.
+ *
+ * @hide
+ */
+public class Constants {
+ private Constants() {}
+
+ /** Returns the ABI that the device prefers. */
+ @NonNull
+ public static String getPreferredAbi() {
+ return Build.SUPPORTED_ABIS[0];
+ }
+
+ /** Returns the 64 bit ABI that is native to the device. */
+ @Nullable
+ public static String getNative64BitAbi() {
+ // The value comes from "ro.product.cpu.abilist64" and we assume that the first element is
+ // the native one.
+ return Build.SUPPORTED_64_BIT_ABIS.length > 0 ? Build.SUPPORTED_64_BIT_ABIS[0] : null;
+ }
+
+ /** Returns the 32 bit ABI that is native to the device. */
+ @Nullable
+ public static String getNative32BitAbi() {
+ // The value comes from "ro.product.cpu.abilist32" and we assume that the first element is
+ // the native one.
+ return Build.SUPPORTED_32_BIT_ABIS.length > 0 ? Build.SUPPORTED_32_BIT_ABIS[0] : null;
+ }
+
+ @Nullable
+ public static String getenv(@NonNull String name) {
+ return Os.getenv(name);
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/Debouncer.java b/libartservice/service/java/com/android/server/art/Debouncer.java
new file mode 100644
index 0000000..61aea81
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/Debouncer.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+/**
+ * A class that executes commands with a minimum interval.
+ *
+ * @hide
+ */
+public class Debouncer {
+ @NonNull private Supplier<ScheduledExecutorService> mScheduledExecutorFactory;
+ private final long mIntervalMs;
+ @Nullable private ScheduledFuture<?> mCurrentTask = null;
+
+ public Debouncer(
+ long intervalMs, @NonNull Supplier<ScheduledExecutorService> scheduledExecutorFactory) {
+ mScheduledExecutorFactory = scheduledExecutorFactory;
+ mIntervalMs = intervalMs;
+ }
+
+ /**
+ * Runs the given command after the interval has passed. If another command comes in during
+ * this interval, the previous one will never run.
+ */
+ synchronized public void maybeRunAsync(@NonNull Runnable command) {
+ if (mCurrentTask != null) {
+ mCurrentTask.cancel(false /* mayInterruptIfRunning */);
+ }
+ ScheduledExecutorService executor = mScheduledExecutorFactory.get();
+ mCurrentTask = executor.schedule(command, mIntervalMs, TimeUnit.MILLISECONDS);
+ executor.shutdown();
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
new file mode 100644
index 0000000..acc9a16
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java
@@ -0,0 +1,906 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.Immutable;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.art.model.DetailedDexInfo;
+import com.android.server.art.model.DexContainerFileUseInfo;
+import com.android.server.art.proto.DexUseProto;
+import com.android.server.art.proto.Int32Value;
+import com.android.server.art.proto.PackageDexUseProto;
+import com.android.server.art.proto.PrimaryDexUseProto;
+import com.android.server.art.proto.PrimaryDexUseRecordProto;
+import com.android.server.art.proto.SecondaryDexUseProto;
+import com.android.server.art.proto.SecondaryDexUseRecordProto;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+
+import com.google.auto.value.AutoValue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+/**
+ * A singleton class that maintains the information about dex uses. This class is thread-safe.
+ *
+ * This class collects data sent directly by apps, and hence the data should be trusted as little as
+ * possible.
+ *
+ * To avoid overwriting data, {@link #load()} must be called exactly once, during initialization.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public class DexUseManagerLocal {
+ private static final String TAG = "DexUseManagerLocal";
+ private static final String FILENAME = "/data/system/package-dex-usage.pb";
+
+ /**
+ * The minimum interval between disk writes.
+ *
+ * In practice, the interval will be much longer because we use a debouncer to postpone the disk
+ * write to the end of a series of changes. Note that in theory we could postpone the disk write
+ * indefinitely, and therefore we could lose data if the device isn't shut down in the normal
+ * way, but that's fine because the data isn't crucial and is recoverable.
+ *
+ * @hide
+ */
+ @VisibleForTesting public static final long INTERVAL_MS = 15_000;
+
+ private static final Object sLock = new Object();
+ @GuardedBy("sLock") @Nullable private static DexUseManagerLocal sInstance = null;
+
+ @NonNull private final Injector mInjector;
+ @NonNull private final Debouncer mDebouncer;
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock") @NonNull private DexUse mDexUse; // Initialized by `load`.
+ @GuardedBy("mLock") private int mRevision = 0;
+ @GuardedBy("mLock") private int mLastCommittedRevision = 0;
+
+ /**
+ * Creates the singleton instance.
+ *
+ * Only {@code SystemServer} should create the instance and register it in {@link
+ * LocalManagerRegistry}. Other API users should obtain the instance from {@link
+ * LocalManagerRegistry}.
+ *
+ * In practice, it must be created and registered in {@link LocalManagerRegistry} before {@code
+ * PackageManagerService} starts because {@code PackageManagerService} needs it as soon as it
+ * starts. It's safe to create an instance early because it doesn't depend on anything else.
+ *
+ * @param context the system server context
+ * @throws IllegalStateException if the instance is already created
+ * @throws NullPointerException if required dependencies are missing
+ */
+ @NonNull
+ public static DexUseManagerLocal createInstance(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sInstance != null) {
+ throw new IllegalStateException("DexUseManagerLocal is already created");
+ }
+ sInstance = new DexUseManagerLocal(context);
+ return sInstance;
+ }
+ }
+
+ private DexUseManagerLocal(@NonNull Context context) {
+ this(new Injector(context));
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public DexUseManagerLocal(@NonNull Injector injector) {
+ mInjector = injector;
+ mDebouncer = new Debouncer(INTERVAL_MS, mInjector::createScheduledExecutor);
+ load();
+ }
+
+ /** Notifies dex use manager that {@link Context#registerReceiver} is ready for use. */
+ public void systemReady() {
+ // Save the data when the device is being shut down. The receiver is blocking, with a
+ // 10s timeout.
+ mInjector.getContext().registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ context.unregisterReceiver(this);
+ save();
+ }
+ }, new IntentFilter(Intent.ACTION_SHUTDOWN));
+ }
+
+ /**
+ * Returns the information about the use of all secondary dex files owned by the given package,
+ * or an empty list if the package does not own any secondary dex file or it does not exist.
+ */
+ @NonNull
+ public List<DexContainerFileUseInfo> getSecondaryDexContainerFileUseInfo(
+ @NonNull String packageName) {
+ return getSecondaryDexInfo(packageName)
+ .stream()
+ .map(info
+ -> DexContainerFileUseInfo.create(info.dexPath(), info.userHandle(),
+ info.loaders()
+ .stream()
+ .map(loader -> loader.loadingPackageName())
+ .collect(Collectors.toSet())))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns all entities that load the given primary dex file owned by the given package.
+ *
+ * @hide
+ */
+ @NonNull
+ public Set<DexLoader> getPrimaryDexLoaders(
+ @NonNull String packageName, @NonNull String dexPath) {
+ synchronized (mLock) {
+ PackageDexUse packageDexUse =
+ mDexUse.mPackageDexUseByOwningPackageName.get(packageName);
+ if (packageDexUse == null) {
+ return Set.of();
+ }
+ PrimaryDexUse primaryDexUse = packageDexUse.mPrimaryDexUseByDexFile.get(dexPath);
+ if (primaryDexUse == null) {
+ return Set.of();
+ }
+ return Set.copyOf(primaryDexUse.mRecordByLoader.keySet());
+ }
+ }
+
+ /**
+ * Returns whether a primary dex file owned by the given package is used by other apps.
+ *
+ * @hide
+ */
+ public boolean isPrimaryDexUsedByOtherApps(
+ @NonNull String packageName, @NonNull String dexPath) {
+ return isUsedByOtherApps(getPrimaryDexLoaders(packageName, dexPath), packageName);
+ }
+
+ /**
+ * Returns the basic information about all secondary dex files owned by the given package. This
+ * method doesn't take dex file visibility into account, so it can only be used for debugging
+ * purpose, such as dumpsys.
+ *
+ * @see #getFilteredDetailedSecondaryDexInfo(String)
+ * @hide
+ */
+ public @NonNull List<? extends SecondaryDexInfo> getSecondaryDexInfo(
+ @NonNull String packageName) {
+ return getSecondaryDexInfoImpl(packageName, false /* checkDexFile */);
+ }
+
+ /**
+ * Same as above, but requires disk IO, and returns the detailed information, including dex file
+ * visibility, filtered by dex file existence and visibility.
+ *
+ * @hide
+ */
+ public @NonNull List<DetailedSecondaryDexInfo> getFilteredDetailedSecondaryDexInfo(
+ @NonNull String packageName) {
+ return getSecondaryDexInfoImpl(packageName, true /* checkDexFile */);
+ }
+
+ /**
+ * Returns the last time the package was used, or 0 if the package has never been used.
+ *
+ * @hide
+ */
+ public long getPackageLastUsedAtMs(@NonNull String packageName) {
+ synchronized (mLock) {
+ PackageDexUse packageDexUse =
+ mDexUse.mPackageDexUseByOwningPackageName.get(packageName);
+ if (packageDexUse == null) {
+ return 0;
+ }
+ long primaryLastUsedAtMs =
+ packageDexUse.mPrimaryDexUseByDexFile.values()
+ .stream()
+ .flatMap(primaryDexUse
+ -> primaryDexUse.mRecordByLoader.values().stream())
+ .map(record -> record.mLastUsedAtMs)
+ .max(Long::compare)
+ .orElse(0l);
+ long secondaryLastUsedAtMs =
+ packageDexUse.mSecondaryDexUseByDexFile.values()
+ .stream()
+ .flatMap(secondaryDexUse
+ -> secondaryDexUse.mRecordByLoader.values().stream())
+ .map(record -> record.mLastUsedAtMs)
+ .max(Long::compare)
+ .orElse(0l);
+ return Math.max(primaryLastUsedAtMs, secondaryLastUsedAtMs);
+ }
+ }
+
+ /**
+ * @param checkDexFile if true, check the existence and visibility of the dex files, and filter
+ * the results accordingly. Note that the value of the {@link
+ * DetailedSecondaryDexInfo#isDexFilePublic()} field is undefined if this argument is
+ * false.
+ */
+ private @NonNull List<DetailedSecondaryDexInfo> getSecondaryDexInfoImpl(
+ @NonNull String packageName, boolean checkDexFile) {
+ synchronized (mLock) {
+ PackageDexUse packageDexUse =
+ mDexUse.mPackageDexUseByOwningPackageName.get(packageName);
+ if (packageDexUse == null) {
+ return List.of();
+ }
+ var results = new ArrayList<DetailedSecondaryDexInfo>();
+ for (var entry : packageDexUse.mSecondaryDexUseByDexFile.entrySet()) {
+ String dexPath = entry.getKey();
+ SecondaryDexUse secondaryDexUse = entry.getValue();
+
+ @FileVisibility
+ int visibility = checkDexFile ? getDexFileVisibility(dexPath)
+ : FileVisibility.OTHER_READABLE;
+ if (visibility == FileVisibility.NOT_FOUND) {
+ continue;
+ }
+
+ Map<DexLoader, SecondaryDexUseRecord> filteredRecordByLoader;
+ if (visibility == FileVisibility.OTHER_READABLE) {
+ filteredRecordByLoader = secondaryDexUse.mRecordByLoader;
+ } else {
+ // Only keep the entry that belongs to the same app.
+ DexLoader sameApp = DexLoader.create(packageName, false /* isolatedProcess */);
+ SecondaryDexUseRecord record = secondaryDexUse.mRecordByLoader.get(sameApp);
+ filteredRecordByLoader = record != null ? Map.of(sameApp, record) : Map.of();
+ }
+ if (filteredRecordByLoader.isEmpty()) {
+ continue;
+ }
+ List<String> distinctClcList =
+ filteredRecordByLoader.values()
+ .stream()
+ .map(record -> Utils.assertNonEmpty(record.mClassLoaderContext))
+ .filter(clc
+ -> !clc.equals(
+ SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT))
+ .distinct()
+ .collect(Collectors.toList());
+ String clc;
+ if (distinctClcList.size() == 0) {
+ clc = SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT;
+ } else if (distinctClcList.size() == 1) {
+ clc = distinctClcList.get(0);
+ } else {
+ // If there are more than one class loader contexts, we can't dexopt the dex
+ // file.
+ clc = SecondaryDexInfo.VARYING_CLASS_LOADER_CONTEXTS;
+ }
+ // Although we filter out unsupported CLCs above, `distinctAbiNames` and `loaders`
+ // still need to take apps with unsupported CLCs into account because the vdex file
+ // is still usable to them.
+ Set<String> distinctAbiNames =
+ filteredRecordByLoader.values()
+ .stream()
+ .map(record -> Utils.assertNonEmpty(record.mAbiName))
+ .collect(Collectors.toSet());
+ Set<DexLoader> loaders = Set.copyOf(filteredRecordByLoader.keySet());
+ results.add(DetailedSecondaryDexInfo.create(dexPath,
+ Objects.requireNonNull(secondaryDexUse.mUserHandle), clc, distinctAbiNames,
+ loaders, isUsedByOtherApps(loaders, packageName),
+ visibility == FileVisibility.OTHER_READABLE));
+ }
+ return Collections.unmodifiableList(results);
+ }
+ }
+
+ /**
+ * Notifies ART Service that a list of dex container files have been loaded.
+ *
+ * ART Service uses this information to:
+ * <ul>
+ * <li>Determine whether an app is used by another app
+ * <li>Record which secondary dex container files to dexopt and how to dexopt them
+ * </ul>
+ *
+ * @param loadingPackageName the name of the package who performs the load. ART Service assumes
+ * that this argument has been validated that it exists in the snapshot and matches the
+ * calling UID
+ * @param classLoaderContextByDexContainerFile a map from dex container files' absolute paths to
+ * the string representations of the class loader contexts used to load them
+ * @throws IllegalArgumentException if {@code classLoaderContextByDexContainerFile} contains
+ * invalid entries
+ */
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void notifyDexContainersLoaded(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull String loadingPackageName,
+ @NonNull Map<String, String> classLoaderContextByDexContainerFile) {
+ // "android" comes from `SystemServerDexLoadReporter`. ART Services doesn't need to handle
+ // this case because it doesn't compile system server and system server isn't allowed to
+ // load artifacts produced by ART Services.
+ if (loadingPackageName.equals(Utils.PLATFORM_PACKAGE_NAME)) {
+ return;
+ }
+
+ validateInputs(snapshot, loadingPackageName, classLoaderContextByDexContainerFile);
+
+ // TODO(jiakaiz): Investigate whether it should also be considered as isolated process if
+ // `Process.isSdkSandboxUid` returns true.
+ boolean isolatedProcess = Process.isIsolatedUid(Binder.getCallingUid());
+ long lastUsedAtMs = mInjector.getCurrentTimeMillis();
+
+ for (var entry : classLoaderContextByDexContainerFile.entrySet()) {
+ String dexPath = Utils.assertNonEmpty(entry.getKey());
+ String classLoaderContext = Utils.assertNonEmpty(entry.getValue());
+ String owningPackageName = findOwningPackage(snapshot, loadingPackageName, dexPath,
+ DexUseManagerLocal::isOwningPackageForPrimaryDex);
+ if (owningPackageName != null) {
+ addPrimaryDexUse(owningPackageName, dexPath, loadingPackageName, isolatedProcess,
+ lastUsedAtMs);
+ continue;
+ }
+ owningPackageName = findOwningPackage(snapshot, loadingPackageName, dexPath,
+ DexUseManagerLocal::isOwningPackageForSecondaryDex);
+ if (owningPackageName != null) {
+ PackageState loadingPkgState =
+ Utils.getPackageStateOrThrow(snapshot, loadingPackageName);
+ // An app is always launched with its primary ABI.
+ Utils.Abi abi = Utils.getPrimaryAbi(loadingPkgState);
+ addSecondaryDexUse(owningPackageName, dexPath, loadingPackageName, isolatedProcess,
+ classLoaderContext, abi.name(), lastUsedAtMs);
+ continue;
+ }
+ // It is expected that a dex file isn't owned by any package. For example, the dex file
+ // could be a shared library jar.
+ }
+ }
+
+ @Nullable
+ private static String findOwningPackage(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull String loadingPackageName, @NonNull String dexPath,
+ @NonNull BiFunction<PackageState, String, Boolean> predicate) {
+ // Most likely, the package is loading its own dex file, so we check this first as an
+ // optimization.
+ PackageState loadingPkgState = Utils.getPackageStateOrThrow(snapshot, loadingPackageName);
+ if (predicate.apply(loadingPkgState, dexPath)) {
+ return loadingPkgState.getPackageName();
+ }
+
+ return snapshot.getPackageStates()
+ .values()
+ .stream()
+ .filter(packageState -> predicate.apply(packageState, dexPath))
+ .map(PackageState::getPackageName)
+ .findFirst()
+ .orElse(null);
+ }
+
+ private static boolean isOwningPackageForPrimaryDex(
+ @NonNull PackageState pkgState, @NonNull String dexPath) {
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+ return PrimaryDexUtils.getDexInfo(pkg).stream().anyMatch(
+ dexInfo -> dexInfo.dexPath().equals(dexPath));
+ }
+
+ private static boolean isOwningPackageForSecondaryDex(
+ @NonNull PackageState pkgState, @NonNull String dexPath) {
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+ UUID storageUuid = pkg.getStorageUuid();
+ UserHandle handle = Binder.getCallingUserHandle();
+
+ File ceDir = Environment.getDataCePackageDirectoryForUser(
+ storageUuid, handle, pkgState.getPackageName());
+ if (Paths.get(dexPath).startsWith(ceDir.toPath())) {
+ return true;
+ }
+
+ File deDir = Environment.getDataDePackageDirectoryForUser(
+ storageUuid, handle, pkgState.getPackageName());
+ if (Paths.get(dexPath).startsWith(deDir.toPath())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void addPrimaryDexUse(@NonNull String owningPackageName, @NonNull String dexPath,
+ @NonNull String loadingPackageName, boolean isolatedProcess, long lastUsedAtMs) {
+ synchronized (mLock) {
+ PrimaryDexUseRecord record =
+ mDexUse.mPackageDexUseByOwningPackageName
+ .computeIfAbsent(owningPackageName, k -> new PackageDexUse())
+ .mPrimaryDexUseByDexFile
+ .computeIfAbsent(dexPath, k -> new PrimaryDexUse())
+ .mRecordByLoader.computeIfAbsent(
+ DexLoader.create(loadingPackageName, isolatedProcess),
+ k -> new PrimaryDexUseRecord());
+ record.mLastUsedAtMs = lastUsedAtMs;
+ mRevision++;
+ }
+ maybeSaveAsync();
+ }
+
+ private void addSecondaryDexUse(@NonNull String owningPackageName, @NonNull String dexPath,
+ @NonNull String loadingPackageName, boolean isolatedProcess,
+ @NonNull String classLoaderContext, @NonNull String abiName, long lastUsedAtMs) {
+ synchronized (mLock) {
+ SecondaryDexUse secondaryDexUse =
+ mDexUse.mPackageDexUseByOwningPackageName
+ .computeIfAbsent(owningPackageName, k -> new PackageDexUse())
+ .mSecondaryDexUseByDexFile.computeIfAbsent(
+ dexPath, k -> new SecondaryDexUse());
+ secondaryDexUse.mUserHandle = Binder.getCallingUserHandle();
+ SecondaryDexUseRecord record = secondaryDexUse.mRecordByLoader.computeIfAbsent(
+ DexLoader.create(loadingPackageName, isolatedProcess),
+ k -> new SecondaryDexUseRecord());
+ record.mClassLoaderContext = classLoaderContext;
+ record.mAbiName = abiName;
+ record.mLastUsedAtMs = lastUsedAtMs;
+ mRevision++;
+ }
+ maybeSaveAsync();
+ }
+
+ /** @hide */
+ public @NonNull String dump() {
+ var builder = DexUseProto.newBuilder();
+ synchronized (mLock) {
+ mDexUse.toProto(builder);
+ }
+ return builder.build().toString();
+ }
+
+ private void save() {
+ var builder = DexUseProto.newBuilder();
+ int thisRevision;
+ synchronized (mLock) {
+ if (mRevision <= mLastCommittedRevision) {
+ return;
+ }
+ mDexUse.toProto(builder);
+ thisRevision = mRevision;
+ }
+ var file = new File(mInjector.getFilename());
+ File tempFile = null;
+ try {
+ tempFile = File.createTempFile(file.getName(), null /* suffix */, file.getParentFile());
+ try (OutputStream out = new FileOutputStream(tempFile.getPath())) {
+ builder.build().writeTo(out);
+ }
+ synchronized (mLock) {
+ // Check revision again in case `mLastCommittedRevision` has changed since the check
+ // above, to avoid ABA race.
+ if (thisRevision > mLastCommittedRevision) {
+ Files.move(tempFile.toPath(), file.toPath(),
+ StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ mLastCommittedRevision = thisRevision;
+ }
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to save dex use data", e);
+ } finally {
+ Utils.deleteIfExistsSafe(tempFile);
+ }
+ }
+
+ private void maybeSaveAsync() {
+ mDebouncer.maybeRunAsync(this::save);
+ }
+
+ /** This should only be called during initialization. */
+ private void load() {
+ DexUseProto proto = null;
+ try (InputStream in = new FileInputStream(mInjector.getFilename())) {
+ proto = DexUseProto.parseFrom(in);
+ } catch (IOException e) {
+ // Nothing else we can do but to start from scratch.
+ Log.e(TAG, "Failed to load dex use data", e);
+ }
+ synchronized (mLock) {
+ if (mDexUse != null) {
+ throw new IllegalStateException("Load has already been attempted");
+ }
+ mDexUse = new DexUse();
+ if (proto != null) {
+ mDexUse.fromProto(proto);
+ }
+ }
+ }
+
+ private static boolean isUsedByOtherApps(
+ @NonNull Set<DexLoader> loaders, @NonNull String owningPackageName) {
+ return loaders.stream().anyMatch(loader -> isLoaderOtherApp(loader, owningPackageName));
+ }
+
+ /**
+ * Returns true if {@code loader} is considered as "other app" (i.e., its process UID is
+ * different from the UID of the package represented by {@code owningPackageName}).
+ *
+ * @hide
+ */
+ public static boolean isLoaderOtherApp(
+ @NonNull DexLoader loader, @NonNull String owningPackageName) {
+ // If the dex file is loaded by an isolated process of the same app, it can also be
+ // considered as "used by other apps" because isolated processes are sandboxed and can only
+ // read world readable files, so they need the dexopt artifacts to be world readable. An
+ // example of such a package is webview.
+ return !loader.loadingPackageName().equals(owningPackageName) || loader.isolatedProcess();
+ }
+
+ private static void validateInputs(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull String loadingPackageName,
+ @NonNull Map<String, String> classLoaderContextByDexContainerFile) {
+ if (classLoaderContextByDexContainerFile.isEmpty()) {
+ throw new IllegalArgumentException("Nothing to record");
+ }
+
+ for (var entry : classLoaderContextByDexContainerFile.entrySet()) {
+ Utils.assertNonEmpty(entry.getKey());
+ if (!Paths.get(entry.getKey()).isAbsolute()) {
+ throw new IllegalArgumentException(String.format(
+ "Dex container file path must be absolute, got '%s'", entry.getKey()));
+ }
+ Utils.assertNonEmpty(entry.getValue());
+ }
+
+ // TODO(b/253570365): Make the validation more strict.
+ }
+
+ private @FileVisibility int getDexFileVisibility(@NonNull String dexPath) {
+ try {
+ return mInjector.getArtd().getDexFileVisibility(dexPath);
+ } catch (ServiceSpecificException | RemoteException e) {
+ Log.e(TAG, "Failed to get visibility of " + dexPath, e);
+ return FileVisibility.NOT_FOUND;
+ }
+ }
+
+ /**
+ * Basic information about a secondary dex file (an APK or JAR file that an app adds to its
+ * own data directory and loads dynamically).
+ *
+ * @hide
+ */
+ @Immutable
+ public abstract static class SecondaryDexInfo {
+ // Special encoding used to denote a foreign ClassLoader was found when trying to encode
+ // class loader contexts for each classpath element in a ClassLoader.
+ // Must be in sync with `kUnsupportedClassLoaderContextEncoding` in
+ // `art/runtime/class_loader_context.h`.
+ public static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+ "=UnsupportedClassLoaderContext=";
+
+ // Special encoding used to denote that a dex file is loaded by different packages with
+ // different ClassLoader's. Only for display purpose (e.g., in dumpsys). This value is not
+ // written to the file, and so far only used here.
+ @VisibleForTesting
+ public static final String VARYING_CLASS_LOADER_CONTEXTS = "=VaryingClassLoaderContexts=";
+
+ /** The absolute path to the dex file within the user's app data directory. */
+ public abstract @NonNull String dexPath();
+
+ /**
+ * The {@link UserHandle} that represents the human user who owns and loads the dex file. A
+ * secondary dex file belongs to a specific human user, and only that user can load it.
+ */
+ public abstract @NonNull UserHandle userHandle();
+
+ /**
+ * A string describing the structure of the class loader that the dex file is loaded with,
+ * or {@link #UNSUPPORTED_CLASS_LOADER_CONTEXT} or {@link #VARYING_CLASS_LOADER_CONTEXTS}.
+ */
+ public abstract @NonNull String displayClassLoaderContext();
+
+ /**
+ * A string describing the structure of the class loader that the dex file is loaded with,
+ * or null if the class loader context is invalid.
+ */
+ public @Nullable String classLoaderContext() {
+ return !displayClassLoaderContext().equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)
+ && !displayClassLoaderContext().equals(VARYING_CLASS_LOADER_CONTEXTS)
+ ? displayClassLoaderContext()
+ : null;
+ }
+
+ /** The set of ABIs of the dex file is loaded with. Guaranteed to be non-empty. */
+ public abstract @NonNull Set<String> abiNames();
+
+ /** The set of entities that load the dex file. Guaranteed to be non-empty. */
+ public abstract @NonNull Set<DexLoader> loaders();
+
+ /** Returns whether the dex file is used by apps other than the app that owns it. */
+ public abstract boolean isUsedByOtherApps();
+ }
+
+ /**
+ * Detailed information about a secondary dex file (an APK or JAR file that an app adds to its
+ * own data directory and loads dynamically). It contains the visibility of the dex file in
+ * addition to what is in {@link SecondaryDexInfo}, but producing it requires disk IO.
+ *
+ * @hide
+ */
+ @Immutable
+ @AutoValue
+ public abstract static class DetailedSecondaryDexInfo
+ extends SecondaryDexInfo implements DetailedDexInfo {
+ static DetailedSecondaryDexInfo create(@NonNull String dexPath,
+ @NonNull UserHandle userHandle, @NonNull String displayClassLoaderContext,
+ @NonNull Set<String> abiNames, @NonNull Set<DexLoader> loaders,
+ boolean isUsedByOtherApps, boolean isDexFilePublic) {
+ return new AutoValue_DexUseManagerLocal_DetailedSecondaryDexInfo(dexPath, userHandle,
+ displayClassLoaderContext, Collections.unmodifiableSet(abiNames),
+ Collections.unmodifiableSet(loaders), isUsedByOtherApps, isDexFilePublic);
+ }
+
+ /**
+ * Returns true if the filesystem permission of the dex file has the "read" bit for "others"
+ * (S_IROTH).
+ */
+ public abstract boolean isDexFilePublic();
+ }
+
+ private static class DexUse {
+ @NonNull Map<String, PackageDexUse> mPackageDexUseByOwningPackageName = new HashMap<>();
+
+ void toProto(@NonNull DexUseProto.Builder builder) {
+ for (var entry : mPackageDexUseByOwningPackageName.entrySet()) {
+ var packageBuilder =
+ PackageDexUseProto.newBuilder().setOwningPackageName(entry.getKey());
+ entry.getValue().toProto(packageBuilder);
+ builder.addPackageDexUse(packageBuilder);
+ }
+ }
+
+ void fromProto(@NonNull DexUseProto proto) {
+ for (PackageDexUseProto packageProto : proto.getPackageDexUseList()) {
+ var packageDexUse = new PackageDexUse();
+ packageDexUse.fromProto(packageProto);
+ mPackageDexUseByOwningPackageName.put(
+ Utils.assertNonEmpty(packageProto.getOwningPackageName()), packageDexUse);
+ }
+ }
+ }
+
+ private static class PackageDexUse {
+ /**
+ * The keys are absolute paths to primary dex files of the owning package (the base APK and
+ * split APKs).
+ */
+ @NonNull Map<String, PrimaryDexUse> mPrimaryDexUseByDexFile = new HashMap<>();
+
+ /**
+ * The keys are absolute paths to secondary dex files of the owning package (the APKs and
+ * JARs in CE and DE directories).
+ */
+ @NonNull Map<String, SecondaryDexUse> mSecondaryDexUseByDexFile = new HashMap<>();
+
+ void toProto(@NonNull PackageDexUseProto.Builder builder) {
+ for (var entry : mPrimaryDexUseByDexFile.entrySet()) {
+ var primaryBuilder = PrimaryDexUseProto.newBuilder().setDexFile(entry.getKey());
+ entry.getValue().toProto(primaryBuilder);
+ builder.addPrimaryDexUse(primaryBuilder);
+ }
+ for (var entry : mSecondaryDexUseByDexFile.entrySet()) {
+ var secondaryBuilder = SecondaryDexUseProto.newBuilder().setDexFile(entry.getKey());
+ entry.getValue().toProto(secondaryBuilder);
+ builder.addSecondaryDexUse(secondaryBuilder);
+ }
+ }
+
+ void fromProto(@NonNull PackageDexUseProto proto) {
+ for (PrimaryDexUseProto primaryProto : proto.getPrimaryDexUseList()) {
+ var primaryDexUse = new PrimaryDexUse();
+ primaryDexUse.fromProto(primaryProto);
+ mPrimaryDexUseByDexFile.put(
+ Utils.assertNonEmpty(primaryProto.getDexFile()), primaryDexUse);
+ }
+ for (SecondaryDexUseProto secondaryProto : proto.getSecondaryDexUseList()) {
+ var secondaryDexUse = new SecondaryDexUse();
+ secondaryDexUse.fromProto(secondaryProto);
+ mSecondaryDexUseByDexFile.put(
+ Utils.assertNonEmpty(secondaryProto.getDexFile()), secondaryDexUse);
+ }
+ }
+ }
+
+ private static class PrimaryDexUse {
+ @NonNull Map<DexLoader, PrimaryDexUseRecord> mRecordByLoader = new HashMap<>();
+
+ void toProto(@NonNull PrimaryDexUseProto.Builder builder) {
+ for (var entry : mRecordByLoader.entrySet()) {
+ var recordBuilder =
+ PrimaryDexUseRecordProto.newBuilder()
+ .setLoadingPackageName(entry.getKey().loadingPackageName())
+ .setIsolatedProcess(entry.getKey().isolatedProcess());
+ entry.getValue().toProto(recordBuilder);
+ builder.addRecord(recordBuilder);
+ }
+ }
+
+ void fromProto(@NonNull PrimaryDexUseProto proto) {
+ for (PrimaryDexUseRecordProto recordProto : proto.getRecordList()) {
+ var record = new PrimaryDexUseRecord();
+ record.fromProto(recordProto);
+ mRecordByLoader.put(
+ DexLoader.create(Utils.assertNonEmpty(recordProto.getLoadingPackageName()),
+ recordProto.getIsolatedProcess()),
+ record);
+ }
+ }
+ }
+
+ private static class SecondaryDexUse {
+ @Nullable UserHandle mUserHandle = null;
+ @NonNull Map<DexLoader, SecondaryDexUseRecord> mRecordByLoader = new HashMap<>();
+
+ void toProto(@NonNull SecondaryDexUseProto.Builder builder) {
+ builder.setUserId(Int32Value.newBuilder().setValue(mUserHandle.getIdentifier()));
+ for (var entry : mRecordByLoader.entrySet()) {
+ var recordBuilder =
+ SecondaryDexUseRecordProto.newBuilder()
+ .setLoadingPackageName(entry.getKey().loadingPackageName())
+ .setIsolatedProcess(entry.getKey().isolatedProcess());
+ entry.getValue().toProto(recordBuilder);
+ builder.addRecord(recordBuilder);
+ }
+ }
+
+ void fromProto(@NonNull SecondaryDexUseProto proto) {
+ Utils.check(proto.hasUserId());
+ mUserHandle = UserHandle.of(proto.getUserId().getValue());
+ for (SecondaryDexUseRecordProto recordProto : proto.getRecordList()) {
+ var record = new SecondaryDexUseRecord();
+ record.fromProto(recordProto);
+ mRecordByLoader.put(
+ DexLoader.create(Utils.assertNonEmpty(recordProto.getLoadingPackageName()),
+ recordProto.getIsolatedProcess()),
+ record);
+ }
+ }
+ }
+
+ /**
+ * Represents an entity that loads a dex file.
+ *
+ * @hide
+ */
+ @Immutable
+ @AutoValue
+ public abstract static class DexLoader {
+ static DexLoader create(@NonNull String loadingPackageName, boolean isolatedProcess) {
+ return new AutoValue_DexUseManagerLocal_DexLoader(loadingPackageName, isolatedProcess);
+ }
+
+ abstract @NonNull String loadingPackageName();
+
+ /** @see Process#isIsolatedUid(int) */
+ abstract boolean isolatedProcess();
+ }
+
+ private static class PrimaryDexUseRecord {
+ @Nullable long mLastUsedAtMs = 0;
+
+ void toProto(@NonNull PrimaryDexUseRecordProto.Builder builder) {
+ builder.setLastUsedAtMs(mLastUsedAtMs);
+ }
+
+ void fromProto(@NonNull PrimaryDexUseRecordProto proto) {
+ mLastUsedAtMs = proto.getLastUsedAtMs();
+ Utils.check(mLastUsedAtMs > 0);
+ }
+ }
+
+ private static class SecondaryDexUseRecord {
+ // An app constructs their own class loader to load a secondary dex file, so only itself
+ // knows the class loader context. Therefore, we need to record the class loader context
+ // reported by the app.
+ @Nullable String mClassLoaderContext = null;
+ @Nullable String mAbiName = null;
+ @Nullable long mLastUsedAtMs = 0;
+
+ void toProto(@NonNull SecondaryDexUseRecordProto.Builder builder) {
+ builder.setClassLoaderContext(mClassLoaderContext)
+ .setAbiName(mAbiName)
+ .setLastUsedAtMs(mLastUsedAtMs);
+ }
+
+ void fromProto(@NonNull SecondaryDexUseRecordProto proto) {
+ mClassLoaderContext = Utils.assertNonEmpty(proto.getClassLoaderContext());
+ mAbiName = Utils.assertNonEmpty(proto.getAbiName());
+ mLastUsedAtMs = proto.getLastUsedAtMs();
+ Utils.check(mLastUsedAtMs > 0);
+ }
+ }
+
+ /**
+ * Injector pattern for testing purpose.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Injector {
+ @NonNull private final Context mContext;
+
+ Injector(@NonNull Context context) {
+ mContext = context;
+
+ // Call the getters for various dependencies, to ensure correct initialization order.
+ ArtModuleServiceInitializer.getArtModuleServiceManager();
+ }
+
+ @NonNull
+ public IArtd getArtd() {
+ return Utils.getArtd();
+ }
+
+ public long getCurrentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ @NonNull
+ public String getFilename() {
+ return FILENAME;
+ }
+
+ @NonNull
+ public ScheduledExecutorService createScheduledExecutor() {
+ return Executors.newSingleThreadScheduledExecutor();
+ }
+
+ @NonNull
+ public Context getContext() {
+ return mContext;
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/DexoptHelper.java b/libartservice/service/java/com/android/server/art/DexoptHelper.java
new file mode 100644
index 0000000..d139bfa
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/DexoptHelper.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.ArtManagerLocal.DexoptDoneCallback;
+import static com.android.server.art.model.Config.Callback;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.apphibernation.AppHibernationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.WorkSource;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.Config;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.model.OperationProgress;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.SharedLibrary;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * A helper class to handle dexopt.
+ *
+ * It talks to other components (e.g., PowerManager) and dispatches tasks to dexopters.
+ *
+ * @hide
+ */
+public class DexoptHelper {
+ private static final String TAG = "DexoptHelper";
+
+ /**
+ * Timeout of the wake lock. This is required by AndroidLint, but we set it to a very large
+ * value so that it should normally never triggered.
+ */
+ private static final long WAKE_LOCK_TIMEOUT_MS = TimeUnit.DAYS.toMillis(1);
+
+ @NonNull private final Injector mInjector;
+
+ public DexoptHelper(@NonNull Context context, @NonNull Config config) {
+ this(new Injector(context, config));
+ }
+
+ @VisibleForTesting
+ public DexoptHelper(@NonNull Injector injector) {
+ mInjector = injector;
+ }
+
+ /**
+ * DO NOT use this method directly. Use {@link ArtManagerLocal#dexoptPackage} or {@link
+ * ArtManagerLocal#dexoptPackages}.
+ */
+ @NonNull
+ public DexoptResult dexopt(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull List<String> packageNames, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor) {
+ return dexopt(snapshot, packageNames, params, cancellationSignal, dexoptExecutor,
+ null /* progressCallbackExecutor */, null /* progressCallback */);
+ }
+
+ /**
+ * DO NOT use this method directly. Use {@link ArtManagerLocal#dexoptPackage} or {@link
+ * ArtManagerLocal#dexoptPackages}.
+ */
+ @NonNull
+ public DexoptResult dexopt(@NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull List<String> packageNames, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal, @NonNull Executor dexoptExecutor,
+ @Nullable Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
+ return dexoptPackages(
+ getPackageStates(snapshot, packageNames,
+ (params.getFlags() & ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES) != 0),
+ params, cancellationSignal, dexoptExecutor, progressCallbackExecutor,
+ progressCallback);
+ }
+
+ /**
+ * DO NOT use this method directly. Use {@link ArtManagerLocal#dexoptPackage} or {@link
+ * ArtManagerLocal#dexoptPackages}.
+ */
+ @NonNull
+ private DexoptResult dexoptPackages(@NonNull List<PackageState> pkgStates,
+ @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal,
+ @NonNull Executor dexoptExecutor, @Nullable Executor progressCallbackExecutor,
+ @Nullable Consumer<OperationProgress> progressCallback) {
+ int callingUid = Binder.getCallingUid();
+ long identityToken = Binder.clearCallingIdentity();
+ PowerManager.WakeLock wakeLock = null;
+
+ try {
+ // Acquire a wake lock.
+ PowerManager powerManager = mInjector.getPowerManager();
+ wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ wakeLock.setWorkSource(new WorkSource(callingUid));
+ wakeLock.acquire(WAKE_LOCK_TIMEOUT_MS);
+
+ List<CompletableFuture<PackageDexoptResult>> futures = new ArrayList<>();
+ for (PackageState pkgState : pkgStates) {
+ futures.add(CompletableFuture.supplyAsync(
+ () -> dexoptPackage(pkgState, params, cancellationSignal), dexoptExecutor));
+ }
+
+ if (progressCallback != null) {
+ CompletableFuture.runAsync(() -> {
+ progressCallback.accept(
+ OperationProgress.create(0 /* current */, futures.size()));
+ }, progressCallbackExecutor);
+ AtomicInteger current = new AtomicInteger(0);
+ for (CompletableFuture<PackageDexoptResult> future : futures) {
+ future.thenRunAsync(() -> {
+ progressCallback.accept(OperationProgress.create(
+ current.incrementAndGet(), futures.size()));
+ }, progressCallbackExecutor);
+ }
+ }
+
+ List<PackageDexoptResult> results =
+ futures.stream().map(Utils::getFuture).collect(Collectors.toList());
+
+ var result =
+ DexoptResult.create(params.getCompilerFilter(), params.getReason(), results);
+
+ for (Callback<DexoptDoneCallback, Boolean> doneCallback :
+ mInjector.getConfig().getDexoptDoneCallbacks()) {
+ boolean onlyIncludeUpdates = doneCallback.extra();
+ if (onlyIncludeUpdates) {
+ List<PackageDexoptResult> filteredResults =
+ results.stream()
+ .filter(PackageDexoptResult::hasUpdatedArtifacts)
+ .collect(Collectors.toList());
+ if (!filteredResults.isEmpty()) {
+ var resultForCallback = DexoptResult.create(
+ params.getCompilerFilter(), params.getReason(), filteredResults);
+ CompletableFuture.runAsync(() -> {
+ doneCallback.get().onDexoptDone(resultForCallback);
+ }, doneCallback.executor());
+ }
+ } else {
+ CompletableFuture.runAsync(() -> {
+ doneCallback.get().onDexoptDone(result);
+ }, doneCallback.executor());
+ }
+ }
+
+ return result;
+ } finally {
+ if (wakeLock != null) {
+ wakeLock.release();
+ }
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ /**
+ * DO NOT use this method directly. Use {@link ArtManagerLocal#dexoptPackage} or {@link
+ * ArtManagerLocal#dexoptPackages}.
+ */
+ @NonNull
+ private PackageDexoptResult dexoptPackage(@NonNull PackageState pkgState,
+ @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal) {
+ List<DexContainerFileDexoptResult> results = new ArrayList<>();
+ Supplier<PackageDexoptResult> createResult = ()
+ -> PackageDexoptResult.create(
+ pkgState.getPackageName(), results, cancellationSignal.isCanceled());
+
+ AndroidPackage pkg = Utils.getPackageOrThrow(pkgState);
+
+ if (!canDexoptPackage(pkgState)) {
+ return createResult.get();
+ }
+
+ if ((params.getFlags() & ArtFlags.FLAG_FOR_SINGLE_SPLIT) != 0) {
+ // Throws if the split is not found.
+ PrimaryDexUtils.getDexInfoBySplitName(pkg, params.getSplitName());
+ }
+
+ try {
+ if ((params.getFlags() & ArtFlags.FLAG_FOR_PRIMARY_DEX) != 0) {
+ if (cancellationSignal.isCanceled()) {
+ return createResult.get();
+ }
+
+ results.addAll(
+ mInjector.getPrimaryDexopter(pkgState, pkg, params, cancellationSignal)
+ .dexopt());
+ }
+
+ if ((params.getFlags() & ArtFlags.FLAG_FOR_SECONDARY_DEX) != 0) {
+ if (cancellationSignal.isCanceled()) {
+ return createResult.get();
+ }
+
+ results.addAll(
+ mInjector.getSecondaryDexopter(pkgState, pkg, params, cancellationSignal)
+ .dexopt());
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("An error occurred when calling artd", e);
+ }
+
+ return createResult.get();
+ }
+
+ private boolean canDexoptPackage(@NonNull PackageState pkgState) {
+ // getAppHibernationManager may return null here during boot time compilation, which will
+ // make this function return true incorrectly for packages that shouldn't be dexopted due to
+ // hibernation. Further discussion in comments in ArtManagerLocal.getDefaultPackages.
+ return Utils.canDexoptPackage(pkgState, mInjector.getAppHibernationManager());
+ }
+
+ @NonNull
+ private List<PackageState> getPackageStates(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull List<String> packageNames, boolean includeDependencies) {
+ var pkgStates = new LinkedHashMap<String, PackageState>();
+ Set<String> visitedLibraries = new HashSet<>();
+ Queue<SharedLibrary> queue = new LinkedList<>();
+
+ Consumer<SharedLibrary> maybeEnqueue = library -> {
+ // The package name is not null if the library is an APK.
+ // TODO(jiakaiz): Support JAR libraries.
+ if (library.getPackageName() != null && !visitedLibraries.contains(library.getName())) {
+ visitedLibraries.add(library.getName());
+ queue.add(library);
+ }
+ };
+
+ for (String packageName : packageNames) {
+ PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
+ Utils.getPackageOrThrow(pkgState);
+ pkgStates.put(packageName, pkgState);
+ if (includeDependencies && canDexoptPackage(pkgState)) {
+ for (SharedLibrary library : pkgState.getSharedLibraryDependencies()) {
+ maybeEnqueue.accept(library);
+ }
+ }
+ }
+
+ SharedLibrary library;
+ while ((library = queue.poll()) != null) {
+ String packageName = library.getPackageName();
+ PackageState pkgState = Utils.getPackageStateOrThrow(snapshot, packageName);
+ if (canDexoptPackage(pkgState)) {
+ pkgStates.put(packageName, pkgState);
+
+ // Note that `library.getDependencies()` is different from
+ // `pkgState.getUsesLibraries()`. Different libraries can belong to the same
+ // package. `pkgState.getUsesLibraries()` returns a union of dependencies of
+ // libraries that belong to the same package, which is not what we want here.
+ // Therefore, this loop cannot be unified with the one above.
+ for (SharedLibrary dep : library.getDependencies()) {
+ maybeEnqueue.accept(dep);
+ }
+ }
+ }
+
+ // `LinkedHashMap` guarantees deterministic order.
+ return new ArrayList<>(pkgStates.values());
+ }
+
+ /**
+ * Injector pattern for testing purpose.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Injector {
+ @NonNull private final Context mContext;
+ @NonNull private final Config mConfig;
+
+ Injector(@NonNull Context context, @NonNull Config config) {
+ mContext = context;
+ mConfig = config;
+
+ // Call the getters for the dependencies that aren't optional, to ensure correct
+ // initialization order.
+ getPowerManager();
+ }
+
+ @NonNull
+ PrimaryDexopter getPrimaryDexopter(@NonNull PackageState pkgState,
+ @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ return new PrimaryDexopter(mContext, pkgState, pkg, params, cancellationSignal);
+ }
+
+ @NonNull
+ SecondaryDexopter getSecondaryDexopter(@NonNull PackageState pkgState,
+ @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ return new SecondaryDexopter(mContext, pkgState, pkg, params, cancellationSignal);
+ }
+
+ /**
+ * Returns the registered AppHibernationManager instance.
+ *
+ * It may be null because ArtManagerLocal needs to be available early to compile packages at
+ * boot with {@link onBoot}, before the hibernation manager has been initialized. It should
+ * not be null for other dexopt calls.
+ */
+ @Nullable
+ public AppHibernationManager getAppHibernationManager() {
+ return mContext.getSystemService(AppHibernationManager.class);
+ }
+
+ @NonNull
+ public PowerManager getPowerManager() {
+ return Objects.requireNonNull(mContext.getSystemService(PowerManager.class));
+ }
+
+ @NonNull
+ public Config getConfig() {
+ return mConfig;
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/Dexopter.java b/libartservice/service/java/com/android/server/art/Dexopter.java
new file mode 100644
index 0000000..acc797b
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/Dexopter.java
@@ -0,0 +1,683 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.GetDexoptNeededResult.ArtifactsLocation;
+import static com.android.server.art.OutputArtifacts.PermissionSettings;
+import static com.android.server.art.ProfilePath.TmpProfilePath;
+import static com.android.server.art.Utils.Abi;
+import static com.android.server.art.model.ArtFlags.DexoptFlags;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+
+import android.R;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.CancellationSignal;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.DetailedDexInfo;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+
+import dalvik.system.DexFile;
+
+import com.google.auto.value.AutoValue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** @hide */
+public abstract class Dexopter<DexInfoType extends DetailedDexInfo> {
+ private static final String TAG = "Dexopter";
+
+ @NonNull protected final Injector mInjector;
+ @NonNull protected final PackageState mPkgState;
+ /** This is always {@code mPkgState.getAndroidPackage()} and guaranteed to be non-null. */
+ @NonNull protected final AndroidPackage mPkg;
+ @NonNull protected final DexoptParams mParams;
+ @NonNull protected final CancellationSignal mCancellationSignal;
+
+ protected Dexopter(@NonNull Injector injector, @NonNull PackageState pkgState,
+ @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ mInjector = injector;
+ mPkgState = pkgState;
+ mPkg = pkg;
+ mParams = params;
+ mCancellationSignal = cancellationSignal;
+ if (pkgState.getAppId() < 0) {
+ throw new IllegalStateException(
+ "Package '" + pkgState.getPackageName() + "' has invalid app ID");
+ }
+ }
+
+ /**
+ * DO NOT use this method directly. Use {@link
+ * ArtManagerLocal#dexoptPackage(PackageManagerLocal.FilteredSnapshot, String,
+ * DexoptParams)}.
+ */
+ @NonNull
+ public final List<DexContainerFileDexoptResult> dexopt() throws RemoteException {
+ List<DexContainerFileDexoptResult> results = new ArrayList<>();
+
+ for (DexInfoType dexInfo : getDexInfoList()) {
+ ProfilePath profile = null;
+ boolean succeeded = true;
+ try {
+ if (!isDexoptable(dexInfo)) {
+ continue;
+ }
+
+ String compilerFilter = adjustCompilerFilter(mParams.getCompilerFilter(), dexInfo);
+ if (compilerFilter.equals(DexoptParams.COMPILER_FILTER_NOOP)) {
+ continue;
+ }
+
+ boolean needsToBeShared = needsToBeShared(dexInfo);
+ boolean isOtherReadable = true;
+ // If true, implies that the profile has changed since the last compilation.
+ boolean profileMerged = false;
+ if (DexFile.isProfileGuidedCompilerFilter(compilerFilter)) {
+ if (needsToBeShared) {
+ profile = initReferenceProfile(dexInfo);
+ } else {
+ Pair<ProfilePath, Boolean> pair = getOrInitReferenceProfile(dexInfo);
+ if (pair != null) {
+ profile = pair.first;
+ isOtherReadable = pair.second;
+ }
+ ProfilePath mergedProfile = mergeProfiles(dexInfo, profile);
+ if (mergedProfile != null) {
+ if (profile != null && profile.getTag() == ProfilePath.tmpProfilePath) {
+ mInjector.getArtd().deleteProfile(profile);
+ }
+ profile = mergedProfile;
+ isOtherReadable = false;
+ profileMerged = true;
+ }
+ }
+ if (profile == null) {
+ // A profile guided dexopt with no profile is essentially 'verify',
+ // and dex2oat already makes this transformation. However, we need to
+ // explicitly make this transformation here to guide the later decisions
+ // such as whether the artifacts can be public and whether dexopt is needed.
+ compilerFilter = needsToBeShared
+ ? ReasonMapping.getCompilerFilterForShared()
+ : "verify";
+ }
+ }
+ boolean isProfileGuidedCompilerFilter =
+ DexFile.isProfileGuidedCompilerFilter(compilerFilter);
+ Utils.check(isProfileGuidedCompilerFilter == (profile != null));
+
+ boolean canBePublic = (!isProfileGuidedCompilerFilter || isOtherReadable)
+ && isDexFilePublic(dexInfo);
+ Utils.check(Utils.implies(needsToBeShared, canBePublic));
+ PermissionSettings permissionSettings = getPermissionSettings(dexInfo, canBePublic);
+
+ DexoptOptions dexoptOptions =
+ getDexoptOptions(dexInfo, isProfileGuidedCompilerFilter);
+
+ for (Abi abi : getAllAbis(dexInfo)) {
+ @DexoptResult.DexoptResultStatus int status = DexoptResult.DEXOPT_SKIPPED;
+ long wallTimeMs = 0;
+ long cpuTimeMs = 0;
+ long sizeBytes = 0;
+ long sizeBeforeBytes = 0;
+ boolean isSkippedDueToStorageLow = false;
+ try {
+ var target = DexoptTarget.<DexInfoType>builder()
+ .setDexInfo(dexInfo)
+ .setIsa(abi.isa())
+ .setIsInDalvikCache(isInDalvikCache())
+ .setCompilerFilter(compilerFilter)
+ .build();
+ var options = GetDexoptNeededOptions.builder()
+ .setProfileMerged(profileMerged)
+ .setFlags(mParams.getFlags())
+ .setNeedsToBePublic(needsToBeShared)
+ .build();
+
+ GetDexoptNeededResult getDexoptNeededResult =
+ getDexoptNeeded(target, options);
+
+ if (!getDexoptNeededResult.isDexoptNeeded) {
+ continue;
+ }
+
+ try {
+ // `StorageManager.getAllocatableBytes` returns (free space + space used
+ // by clearable cache - low storage threshold). Since we only compare
+ // the result with 0, the clearable cache doesn't make a difference.
+ // When the free space is below the threshold, there should be no
+ // clearable cache left because system cleans up cache every minute.
+ if ((mParams.getFlags() & ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) != 0
+ && mInjector.getStorageManager().getAllocatableBytes(
+ mPkg.getStorageUuid())
+ <= 0) {
+ isSkippedDueToStorageLow = true;
+ continue;
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to check storage. Assuming storage not low", e);
+ }
+
+ IArtdCancellationSignal artdCancellationSignal =
+ mInjector.getArtd().createCancellationSignal();
+ mCancellationSignal.setOnCancelListener(() -> {
+ try {
+ artdCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Log.e(TAG, "An error occurred when sending a cancellation signal",
+ e);
+ }
+ });
+
+ ArtdDexoptResult dexoptResult = dexoptFile(target, profile,
+ getDexoptNeededResult, permissionSettings,
+ mParams.getPriorityClass(), dexoptOptions, artdCancellationSignal);
+ status = dexoptResult.cancelled ? DexoptResult.DEXOPT_CANCELLED
+ : DexoptResult.DEXOPT_PERFORMED;
+ wallTimeMs = dexoptResult.wallTimeMs;
+ cpuTimeMs = dexoptResult.cpuTimeMs;
+ sizeBytes = dexoptResult.sizeBytes;
+ sizeBeforeBytes = dexoptResult.sizeBeforeBytes;
+
+ if (status == DexoptResult.DEXOPT_CANCELLED) {
+ return results;
+ }
+ } catch (ServiceSpecificException e) {
+ // Log the error and continue.
+ Log.e(TAG,
+ String.format("Failed to dexopt [packageName = %s, dexPath = %s, "
+ + "isa = %s, classLoaderContext = %s]",
+ mPkgState.getPackageName(), dexInfo.dexPath(), abi.isa(),
+ dexInfo.classLoaderContext()),
+ e);
+ status = DexoptResult.DEXOPT_FAILED;
+ } finally {
+ results.add(DexContainerFileDexoptResult.create(dexInfo.dexPath(),
+ abi.isPrimaryAbi(), abi.name(), compilerFilter, status, wallTimeMs,
+ cpuTimeMs, sizeBytes, sizeBeforeBytes, isSkippedDueToStorageLow));
+ if (status != DexoptResult.DEXOPT_SKIPPED
+ && status != DexoptResult.DEXOPT_PERFORMED) {
+ succeeded = false;
+ }
+ // Make sure artd does not leak even if the caller holds
+ // `mCancellationSignal` forever.
+ mCancellationSignal.setOnCancelListener(null);
+ }
+ }
+
+ if (profile != null && succeeded) {
+ if (profile.getTag() == ProfilePath.tmpProfilePath) {
+ // Commit the profile only if dexopt succeeds.
+ if (commitProfileChanges(profile.getTmpProfilePath())) {
+ profile = null;
+ }
+ }
+ if (profileMerged) {
+ // Note that this is just an optimization, to reduce the amount of data that
+ // the runtime writes on every profile save. The profile merge result on the
+ // next run won't change regardless of whether the cleanup is done or not
+ // because profman only looks at the diff.
+ // A caveat is that it may delete more than what has been merged, if the
+ // runtime writes additional entries between the merge and the cleanup, but
+ // this is fine because the runtime writes all JITed classes and methods on
+ // every save and the additional entries will likely be written back on the
+ // next save.
+ cleanupCurProfiles(dexInfo);
+ }
+ }
+ } finally {
+ if (profile != null && profile.getTag() == ProfilePath.tmpProfilePath) {
+ mInjector.getArtd().deleteProfile(profile);
+ }
+ }
+ }
+
+ return results;
+ }
+
+ @NonNull
+ private String adjustCompilerFilter(
+ @NonNull String targetCompilerFilter, @NonNull DexInfoType dexInfo) {
+ if (mInjector.isSystemUiPackage(mPkgState.getPackageName())) {
+ String systemUiCompilerFilter = getSystemUiCompilerFilter();
+ if (!systemUiCompilerFilter.isEmpty()) {
+ return systemUiCompilerFilter;
+ }
+ }
+
+ // We force vmSafeMode on debuggable apps as well:
+ // - the runtime ignores their compiled code
+ // - they generally have lots of methods that could make the compiler used run out of
+ // memory (b/130828957)
+ // Note that forcing the compiler filter here applies to all compilations (even if they
+ // are done via adb shell commands). This is okay because the runtime will ignore the
+ // compiled code anyway.
+ if (mPkg.isVmSafeMode() || mPkg.isDebuggable()) {
+ return DexFile.getSafeModeCompilerFilter(targetCompilerFilter);
+ }
+
+ // We cannot do AOT compilation if we don't have a valid class loader context.
+ if (dexInfo.classLoaderContext() == null) {
+ return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
+ : targetCompilerFilter;
+ }
+
+ // This application wants to use the embedded dex in the APK, rather than extracted or
+ // locally compiled variants, so we only verify it.
+ // "verify" does not prevent dex2oat from extracting the dex code, but in practice, dex2oat
+ // won't extract the dex code because the APK is uncompressed, and the assumption is that
+ // such applications always use uncompressed APKs.
+ if (mPkg.isUseEmbeddedDex()) {
+ return DexFile.isOptimizedCompilerFilter(targetCompilerFilter) ? "verify"
+ : targetCompilerFilter;
+ }
+
+ return targetCompilerFilter;
+ }
+
+ @NonNull
+ private String getSystemUiCompilerFilter() {
+ String compilerFilter = SystemProperties.get("dalvik.vm.systemuicompilerfilter");
+ if (!compilerFilter.isEmpty() && !Utils.isValidArtServiceCompilerFilter(compilerFilter)) {
+ throw new IllegalStateException(
+ "Got invalid compiler filter '" + compilerFilter + "' for System UI");
+ }
+ return compilerFilter;
+ }
+
+ /**
+ * Gets the existing reference profile if exists, or initializes a reference profile from an
+ * external profile.
+ *
+ * @return A pair where the first element is the found or initialized profile, and the second
+ * element is true if the profile is readable by others. Or null if there is no
+ * reference profile or external profile to use.
+ */
+ @Nullable
+ private Pair<ProfilePath, Boolean> getOrInitReferenceProfile(@NonNull DexInfoType dexInfo)
+ throws RemoteException {
+ ProfilePath refProfile = buildRefProfilePath(dexInfo);
+ try {
+ if (mInjector.getArtd().isProfileUsable(refProfile, dexInfo.dexPath())) {
+ boolean isOtherReadable = mInjector.getArtd().getProfileVisibility(refProfile)
+ == FileVisibility.OTHER_READABLE;
+ return Pair.create(refProfile, isOtherReadable);
+ }
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG,
+ "Failed to use the existing reference profile "
+ + AidlUtils.toString(refProfile),
+ e);
+ }
+
+ ProfilePath initializedProfile = initReferenceProfile(dexInfo);
+ return initializedProfile != null ? Pair.create(initializedProfile, true) : null;
+ }
+
+ @NonNull
+ private DexoptOptions getDexoptOptions(
+ @NonNull DexInfoType dexInfo, boolean isProfileGuidedFilter) {
+ DexoptOptions dexoptOptions = new DexoptOptions();
+ dexoptOptions.compilationReason = mParams.getReason();
+ dexoptOptions.targetSdkVersion = mPkg.getTargetSdkVersion();
+ dexoptOptions.debuggable = mPkg.isDebuggable() || isAlwaysDebuggable();
+ // Generating a meaningful app image needs a profile to determine what to include in the
+ // image. Otherwise, the app image will be nearly empty.
+ dexoptOptions.generateAppImage =
+ isProfileGuidedFilter && isAppImageAllowed(dexInfo) && isAppImageEnabled();
+ dexoptOptions.hiddenApiPolicyEnabled = isHiddenApiPolicyEnabled();
+ return dexoptOptions;
+ }
+
+ private boolean isAlwaysDebuggable() {
+ return SystemProperties.getBoolean("dalvik.vm.always_debuggable", false /* def */);
+ }
+
+ private boolean isAppImageEnabled() {
+ return !SystemProperties.get("dalvik.vm.appimageformat").isEmpty();
+ }
+
+ private boolean isHiddenApiPolicyEnabled() {
+ return mPkgState.getHiddenApiEnforcementPolicy()
+ != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+ }
+
+ @NonNull
+ GetDexoptNeededResult getDexoptNeeded(@NonNull DexoptTarget<DexInfoType> target,
+ @NonNull GetDexoptNeededOptions options) throws RemoteException {
+ int dexoptTrigger = getDexoptTrigger(target, options);
+
+ // The result should come from artd even if all the bits of `dexoptTrigger` are set
+ // because the result also contains information about the usable VDEX file.
+ // Note that the class loader context can be null. In that case, we intentionally pass the
+ // null value down to lower levels to indicate that the class loader context check should be
+ // skipped because we are only going to verify the dex code (see `adjustCompilerFilter`).
+ GetDexoptNeededResult result = mInjector.getArtd().getDexoptNeeded(
+ target.dexInfo().dexPath(), target.isa(), target.dexInfo().classLoaderContext(),
+ target.compilerFilter(), dexoptTrigger);
+
+ return result;
+ }
+
+ int getDexoptTrigger(@NonNull DexoptTarget<DexInfoType> target,
+ @NonNull GetDexoptNeededOptions options) throws RemoteException {
+ if ((options.flags() & ArtFlags.FLAG_FORCE) != 0) {
+ return DexoptTrigger.COMPILER_FILTER_IS_BETTER | DexoptTrigger.COMPILER_FILTER_IS_SAME
+ | DexoptTrigger.COMPILER_FILTER_IS_WORSE
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE
+ | DexoptTrigger.NEED_EXTRACTION;
+ }
+
+ if ((options.flags() & ArtFlags.FLAG_SHOULD_DOWNGRADE) != 0) {
+ return DexoptTrigger.COMPILER_FILTER_IS_WORSE;
+ }
+
+ int dexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+ if (options.profileMerged()) {
+ dexoptTrigger |= DexoptTrigger.COMPILER_FILTER_IS_SAME;
+ }
+
+ ArtifactsPath existingArtifactsPath = AidlUtils.buildArtifactsPath(
+ target.dexInfo().dexPath(), target.isa(), target.isInDalvikCache());
+
+ if (options.needsToBePublic()
+ && mInjector.getArtd().getArtifactsVisibility(existingArtifactsPath)
+ == FileVisibility.NOT_OTHER_READABLE) {
+ // Typically, this happens after an app starts being used by other apps.
+ // This case should be the same as force as we have no choice but to trigger a new
+ // dexopt.
+ dexoptTrigger |=
+ DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE;
+ }
+
+ return dexoptTrigger;
+ }
+
+ private ArtdDexoptResult dexoptFile(@NonNull DexoptTarget<DexInfoType> target,
+ @Nullable ProfilePath profile, @NonNull GetDexoptNeededResult getDexoptNeededResult,
+ @NonNull PermissionSettings permissionSettings, @PriorityClass int priorityClass,
+ @NonNull DexoptOptions dexoptOptions, IArtdCancellationSignal artdCancellationSignal)
+ throws RemoteException {
+ OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(target.dexInfo().dexPath(),
+ target.isa(), target.isInDalvikCache(), permissionSettings);
+
+ VdexPath inputVdex =
+ getInputVdex(getDexoptNeededResult, target.dexInfo().dexPath(), target.isa());
+
+ DexMetadataPath dmFile = getDmFile(target.dexInfo());
+ if (dmFile != null
+ && ReasonMapping.REASONS_FOR_INSTALL.contains(dexoptOptions.compilationReason)) {
+ // If the DM file is passed to dex2oat, then add the "-dm" suffix to the reason (e.g.,
+ // "install-dm").
+ // Note that this only applies to reasons for app install because the goal is to give
+ // Play a signal that a DM file is downloaded at install time. We actually pass the DM
+ // file regardless of the compilation reason, but we don't append a suffix when the
+ // compilation reason is not a reason for app install.
+ // Also note that the "-dm" suffix does NOT imply anything in the DM file being used by
+ // dex2oat. dex2oat may ignore some contents of the DM file when appropriate. The
+ // compilation reason can still be "install-dm" even if dex2oat left all contents of the
+ // DM file unused or an empty DM file is passed to dex2oat.
+ dexoptOptions.compilationReason = dexoptOptions.compilationReason + "-dm";
+ }
+
+ return mInjector.getArtd().dexopt(outputArtifacts, target.dexInfo().dexPath(), target.isa(),
+ target.dexInfo().classLoaderContext(), target.compilerFilter(), profile, inputVdex,
+ dmFile, priorityClass, dexoptOptions, artdCancellationSignal);
+ }
+
+ @Nullable
+ private VdexPath getInputVdex(@NonNull GetDexoptNeededResult getDexoptNeededResult,
+ @NonNull String dexPath, @NonNull String isa) {
+ if (!getDexoptNeededResult.isVdexUsable) {
+ return null;
+ }
+ switch (getDexoptNeededResult.artifactsLocation) {
+ case ArtifactsLocation.DALVIK_CACHE:
+ return VdexPath.artifactsPath(
+ AidlUtils.buildArtifactsPath(dexPath, isa, true /* isInDalvikCache */));
+ case ArtifactsLocation.NEXT_TO_DEX:
+ return VdexPath.artifactsPath(
+ AidlUtils.buildArtifactsPath(dexPath, isa, false /* isInDalvikCache */));
+ case ArtifactsLocation.DM:
+ // The DM file is passed to dex2oat as a separate flag whenever it exists.
+ return null;
+ default:
+ // This should never happen as the value is got from artd.
+ throw new IllegalStateException(
+ "Unknown artifacts location " + getDexoptNeededResult.artifactsLocation);
+ }
+ }
+
+ @Nullable
+ private DexMetadataPath getDmFile(@NonNull DexInfoType dexInfo) throws RemoteException {
+ DexMetadataPath path = buildDmPath(dexInfo);
+ if (path == null) {
+ return null;
+ }
+ try {
+ if (mInjector.getArtd().getDmFileVisibility(path) != FileVisibility.NOT_FOUND) {
+ return path;
+ }
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "Failed to check DM file for " + dexInfo.dexPath(), e);
+ }
+ return null;
+ }
+
+ private boolean commitProfileChanges(@NonNull TmpProfilePath profile) throws RemoteException {
+ try {
+ mInjector.getArtd().commitTmpProfile(profile);
+ return true;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "Failed to commit profile changes " + AidlUtils.toString(profile.finalPath),
+ e);
+ return false;
+ }
+ }
+
+ @Nullable
+ private ProfilePath mergeProfiles(@NonNull DexInfoType dexInfo,
+ @Nullable ProfilePath referenceProfile) throws RemoteException {
+ OutputProfile output = buildOutputProfile(dexInfo, false /* isPublic */);
+
+ try {
+ if (mInjector.getArtd().mergeProfiles(getCurProfiles(dexInfo), referenceProfile, output,
+ List.of(dexInfo.dexPath()), new MergeProfileOptions())) {
+ return ProfilePath.tmpProfilePath(output.profilePath);
+ }
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG,
+ "Failed to merge profiles " + AidlUtils.toString(output.profilePath.finalPath),
+ e);
+ }
+
+ return null;
+ }
+
+ private void cleanupCurProfiles(@NonNull DexInfoType dexInfo) throws RemoteException {
+ for (ProfilePath profile : getCurProfiles(dexInfo)) {
+ mInjector.getArtd().deleteProfile(profile);
+ }
+ }
+
+ // Methods to be implemented by child classes.
+
+ /** Returns true if the artifacts should be written to the global dalvik-cache directory. */
+ protected abstract boolean isInDalvikCache();
+
+ /** Returns information about all dex files. */
+ @NonNull protected abstract List<DexInfoType> getDexInfoList();
+
+ /** Returns true if the given dex file should be dexopted. */
+ protected abstract boolean isDexoptable(@NonNull DexInfoType dexInfo);
+
+ /**
+ * Returns true if the artifacts should be shared with other apps. Note that this must imply
+ * {@link #isDexFilePublic(DexInfoType)}.
+ */
+ protected abstract boolean needsToBeShared(@NonNull DexInfoType dexInfo);
+
+ /**
+ * Returns true if the filesystem permission of the dex file has the "read" bit for "others"
+ * (S_IROTH).
+ */
+ protected abstract boolean isDexFilePublic(@NonNull DexInfoType dexInfo);
+
+ /**
+ * Returns a reference profile initialized from an external profile (e.g., a DM profile) if
+ * one exists, or null otherwise.
+ */
+ @Nullable
+ protected abstract ProfilePath initReferenceProfile(@NonNull DexInfoType dexInfo)
+ throws RemoteException;
+
+ /** Returns the permission settings to use for the artifacts of the given dex file. */
+ @NonNull
+ protected abstract PermissionSettings getPermissionSettings(
+ @NonNull DexInfoType dexInfo, boolean canBePublic);
+
+ /** Returns all ABIs that the given dex file should be compiled for. */
+ @NonNull protected abstract List<Abi> getAllAbis(@NonNull DexInfoType dexInfo);
+
+ /** Returns the path to the reference profile of the given dex file. */
+ @NonNull protected abstract ProfilePath buildRefProfilePath(@NonNull DexInfoType dexInfo);
+
+ /** Returns true if app image (--app-image-fd) is allowed. */
+ protected abstract boolean isAppImageAllowed(@NonNull DexInfoType dexInfo);
+
+ /**
+ * Returns the data structure that represents the temporary profile to use during processing.
+ */
+ @NonNull
+ protected abstract OutputProfile buildOutputProfile(
+ @NonNull DexInfoType dexInfo, boolean isPublic);
+
+ /** Returns the paths to the current profiles of the given dex file. */
+ @NonNull protected abstract List<ProfilePath> getCurProfiles(@NonNull DexInfoType dexInfo);
+
+ /**
+ * Returns the path to the DM file that should be passed to dex2oat, or null if no DM file
+ * should be passed.
+ */
+ @Nullable protected abstract DexMetadataPath buildDmPath(@NonNull DexInfoType dexInfo);
+
+ @AutoValue
+ abstract static class DexoptTarget<DexInfoType extends DetailedDexInfo> {
+ abstract @NonNull DexInfoType dexInfo();
+ abstract @NonNull String isa();
+ abstract boolean isInDalvikCache();
+ abstract @NonNull String compilerFilter();
+
+ static <DexInfoType extends DetailedDexInfo> Builder<DexInfoType> builder() {
+ return new AutoValue_Dexopter_DexoptTarget.Builder<DexInfoType>();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder<DexInfoType extends DetailedDexInfo> {
+ abstract Builder setDexInfo(@NonNull DexInfoType value);
+ abstract Builder setIsa(@NonNull String value);
+ abstract Builder setIsInDalvikCache(boolean value);
+ abstract Builder setCompilerFilter(@NonNull String value);
+ abstract DexoptTarget<DexInfoType> build();
+ }
+ }
+
+ @AutoValue
+ abstract static class GetDexoptNeededOptions {
+ abstract @DexoptFlags int flags();
+ abstract boolean profileMerged();
+ abstract boolean needsToBePublic();
+
+ static Builder builder() {
+ return new AutoValue_Dexopter_GetDexoptNeededOptions.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setFlags(@DexoptFlags int value);
+ abstract Builder setProfileMerged(boolean value);
+ abstract Builder setNeedsToBePublic(boolean value);
+ abstract GetDexoptNeededOptions build();
+ }
+ }
+
+ /**
+ * Injector pattern for testing purpose.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public static class Injector {
+ @NonNull private final Context mContext;
+
+ public Injector(@NonNull Context context) {
+ mContext = context;
+
+ // Call the getters for various dependencies, to ensure correct initialization order.
+ getUserManager();
+ getDexUseManager();
+ getStorageManager();
+ ArtModuleServiceInitializer.getArtModuleServiceManager();
+ }
+
+ public boolean isSystemUiPackage(@NonNull String packageName) {
+ return packageName.equals(mContext.getString(R.string.config_systemUi));
+ }
+
+ @NonNull
+ public UserManager getUserManager() {
+ return Objects.requireNonNull(mContext.getSystemService(UserManager.class));
+ }
+
+ @NonNull
+ public DexUseManagerLocal getDexUseManager() {
+ return Objects.requireNonNull(
+ LocalManagerRegistry.getManager(DexUseManagerLocal.class));
+ }
+
+ @NonNull
+ public IArtd getArtd() {
+ return Utils.getArtd();
+ }
+
+ @NonNull
+ public StorageManager getStorageManager() {
+ return Objects.requireNonNull(mContext.getSystemService(StorageManager.class));
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/DumpHelper.java b/libartservice/service/java/com/android/server/art/DumpHelper.java
new file mode 100644
index 0000000..0de2c1b
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/DumpHelper.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.DexUseManagerLocal.DexLoader;
+import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo;
+import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
+
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.PackageState;
+
+import dalvik.system.VMRuntime;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * A helper class to handle dump.
+ *
+ * @hide
+ */
+public class DumpHelper {
+ @NonNull private final Injector mInjector;
+
+ public DumpHelper(@NonNull ArtManagerLocal artManagerLocal) {
+ this(new Injector(artManagerLocal));
+ }
+
+ @VisibleForTesting
+ public DumpHelper(@NonNull Injector injector) {
+ mInjector = injector;
+ }
+
+ /** Handles {@link ArtManagerLocal#dump(PrintWriter, PackageManagerLocal.FilteredSnapshot)}. */
+ public void dump(
+ @NonNull PrintWriter pw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot) {
+ for (PackageState pkgState : snapshot.getPackageStates().values()) {
+ dumpPackage(pw, snapshot, pkgState);
+ }
+ }
+
+ /**
+ * Handles {@link
+ * ArtManagerLocal#dumpPackage(PrintWriter, PackageManagerLocal.FilteredSnapshot, String)}.
+ */
+ public void dumpPackage(@NonNull PrintWriter pw,
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot,
+ @NonNull PackageState pkgState) {
+ // An APEX has a uid of -1.
+ // TODO(b/256637152): Consider using `isApex` instead.
+ if (pkgState.getAppId() <= 0 || pkgState.getAndroidPackage() == null) {
+ return;
+ }
+
+ var ipw = new IndentingPrintWriter(pw);
+
+ String packageName = pkgState.getPackageName();
+ ipw.printf("[%s]\n", packageName);
+
+ List<DexContainerFileDexoptStatus> statuses =
+ mInjector.getArtManagerLocal()
+ .getDexoptStatus(snapshot, packageName)
+ .getDexContainerFileDexoptStatuses();
+ Map<String, SecondaryDexInfo> secondaryDexInfoByDexPath =
+ mInjector.getDexUseManager()
+ .getSecondaryDexInfo(packageName)
+ .stream()
+ .collect(Collectors.toMap(SecondaryDexInfo::dexPath, Function.identity()));
+
+ // Use LinkedHashMap to keep the order.
+ var primaryStatusesByDexPath =
+ new LinkedHashMap<String, List<DexContainerFileDexoptStatus>>();
+ var secondaryStatusesByDexPath =
+ new LinkedHashMap<String, List<DexContainerFileDexoptStatus>>();
+ for (DexContainerFileDexoptStatus fileStatus : statuses) {
+ if (fileStatus.isPrimaryDex()) {
+ primaryStatusesByDexPath
+ .computeIfAbsent(fileStatus.getDexContainerFile(), k -> new ArrayList<>())
+ .add(fileStatus);
+ } else if (secondaryDexInfoByDexPath.containsKey(fileStatus.getDexContainerFile())) {
+ // The condition above is false only if a change occurs between
+ // `getDexoptStatus` and `getSecondaryDexInfo`, which is an edge case.
+ secondaryStatusesByDexPath
+ .computeIfAbsent(fileStatus.getDexContainerFile(), k -> new ArrayList<>())
+ .add(fileStatus);
+ }
+ }
+
+ ipw.increaseIndent();
+ for (List<DexContainerFileDexoptStatus> fileStatuses : primaryStatusesByDexPath.values()) {
+ dumpPrimaryDex(ipw, fileStatuses, packageName);
+ }
+ if (!secondaryStatusesByDexPath.isEmpty()) {
+ ipw.println("known secondary dex files:");
+ ipw.increaseIndent();
+ for (Map.Entry<String, List<DexContainerFileDexoptStatus>> entry :
+ secondaryStatusesByDexPath.entrySet()) {
+ dumpSecondaryDex(ipw, entry.getValue(), packageName,
+ secondaryDexInfoByDexPath.get(entry.getKey()));
+ }
+ ipw.decreaseIndent();
+ }
+ ipw.decreaseIndent();
+ }
+
+ private void dumpPrimaryDex(@NonNull IndentingPrintWriter ipw,
+ List<DexContainerFileDexoptStatus> fileStatuses, @NonNull String packageName) {
+ String dexPath = fileStatuses.get(0).getDexContainerFile();
+ ipw.printf("path: %s\n", dexPath);
+ ipw.increaseIndent();
+ dumpFileStatuses(ipw, fileStatuses);
+ dumpUsedByOtherApps(ipw,
+ mInjector.getDexUseManager().getPrimaryDexLoaders(packageName, dexPath),
+ packageName);
+ ipw.decreaseIndent();
+ }
+
+ private void dumpSecondaryDex(@NonNull IndentingPrintWriter ipw,
+ List<DexContainerFileDexoptStatus> fileStatuses, @NonNull String packageName,
+ @NonNull SecondaryDexInfo info) {
+ String dexPath = fileStatuses.get(0).getDexContainerFile();
+ ipw.println(dexPath);
+ ipw.increaseIndent();
+ dumpFileStatuses(ipw, fileStatuses);
+ ipw.printf("class loader context: %s\n", info.displayClassLoaderContext());
+ dumpUsedByOtherApps(ipw, info.loaders(), packageName);
+ ipw.decreaseIndent();
+ }
+
+ private void dumpFileStatuses(
+ @NonNull IndentingPrintWriter ipw, List<DexContainerFileDexoptStatus> fileStatuses) {
+ for (DexContainerFileDexoptStatus fileStatus : fileStatuses) {
+ ipw.printf("%s: [status=%s] [reason=%s]\n",
+ VMRuntime.getInstructionSet(fileStatus.getAbi()),
+ fileStatus.getCompilerFilter(), fileStatus.getCompilationReason());
+ }
+ }
+
+ private void dumpUsedByOtherApps(@NonNull IndentingPrintWriter ipw,
+ @NonNull Set<DexLoader> dexLoaders, @NonNull String packageName) {
+ List<DexLoader> otherApps =
+ dexLoaders.stream()
+ .filter(loader -> DexUseManagerLocal.isLoaderOtherApp(loader, packageName))
+ .collect(Collectors.toList());
+ if (!otherApps.isEmpty()) {
+ ipw.printf("used by other apps: [%s]\n",
+ otherApps.stream()
+ .map(loader
+ -> loader.loadingPackageName()
+ + (loader.isolatedProcess() ? " (isolated)" : ""))
+ .collect(Collectors.joining(", ")));
+ }
+ }
+
+ /** Injector pattern for testing purpose. */
+ @VisibleForTesting
+ public static class Injector {
+ @NonNull private final ArtManagerLocal mArtManagerLocal;
+
+ Injector(@NonNull ArtManagerLocal artManagerLocal) {
+ mArtManagerLocal = artManagerLocal;
+ }
+
+ @NonNull
+ public ArtManagerLocal getArtManagerLocal() {
+ return mArtManagerLocal;
+ }
+
+ @NonNull
+ public DexUseManagerLocal getDexUseManager() {
+ return Objects.requireNonNull(
+ LocalManagerRegistry.getManager(DexUseManagerLocal.class));
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/IndentingPrintWriter.java b/libartservice/service/java/com/android/server/art/IndentingPrintWriter.java
new file mode 100644
index 0000000..b717786
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/IndentingPrintWriter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * A minimal copy of the hidden {@link android.util.IndentingPrintWriter}.
+ *
+ * TODO(b/264968147): Avoid the duplication.
+ *
+ * @hide
+ */
+public class IndentingPrintWriter extends PrintWriter {
+ private final String mSingleIndent;
+
+ /** Mutable version of current indent */
+ private StringBuilder mIndentBuilder = new StringBuilder();
+ /** Cache of current {@link #mIndentBuilder} value */
+ private char[] mCurrentIndent;
+ /** Length of current line being built, excluding any indent */
+ private int mCurrentLength;
+
+ /**
+ * Flag indicating if we're currently sitting on an empty line, and that
+ * next write should be prefixed with the current indent.
+ */
+ private boolean mEmptyLine = true;
+
+ private char[] mSingleChar = new char[1];
+
+ public IndentingPrintWriter(@NonNull Writer writer) {
+ super(writer);
+ mSingleIndent = " ";
+ }
+
+ /**
+ * Increases the indent starting with the next printed line.
+ */
+ @NonNull
+ public IndentingPrintWriter increaseIndent() {
+ mIndentBuilder.append(mSingleIndent);
+ mCurrentIndent = null;
+ return this;
+ }
+
+ /**
+ * Decreases the indent starting with the next printed line.
+ */
+ @NonNull
+ public IndentingPrintWriter decreaseIndent() {
+ mIndentBuilder.delete(0, mSingleIndent.length());
+ mCurrentIndent = null;
+ return this;
+ }
+
+ @Override
+ public void println() {
+ write('\n');
+ }
+
+ @Override
+ public void write(int c) {
+ mSingleChar[0] = (char) c;
+ write(mSingleChar, 0, 1);
+ }
+
+ @Override
+ public void write(@NonNull String s, int off, int len) {
+ final char[] buf = new char[len];
+ s.getChars(off, len - off, buf, 0);
+ write(buf, 0, len);
+ }
+
+ @Override
+ public void write(@NonNull char[] buf, int offset, int count) {
+ final int indentLength = mIndentBuilder.length();
+ final int bufferEnd = offset + count;
+ int lineStart = offset;
+ int lineEnd = offset;
+
+ // March through incoming buffer looking for newlines
+ while (lineEnd < bufferEnd) {
+ char ch = buf[lineEnd++];
+ mCurrentLength++;
+ if (ch == '\n') {
+ maybeWriteIndent();
+ super.write(buf, lineStart, lineEnd - lineStart);
+ lineStart = lineEnd;
+ mEmptyLine = true;
+ mCurrentLength = 0;
+ }
+ }
+
+ if (lineStart != lineEnd) {
+ maybeWriteIndent();
+ super.write(buf, lineStart, lineEnd - lineStart);
+ }
+ }
+
+ private void maybeWriteIndent() {
+ if (mEmptyLine) {
+ mEmptyLine = false;
+ if (mIndentBuilder.length() != 0) {
+ if (mCurrentIndent == null) {
+ mCurrentIndent = mIndentBuilder.toString().toCharArray();
+ }
+ super.write(mCurrentIndent, 0, mCurrentIndent.length);
+ }
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexUtils.java b/libartservice/service/java/com/android/server/art/PrimaryDexUtils.java
new file mode 100644
index 0000000..2f441e5
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/PrimaryDexUtils.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.Immutable;
+import com.android.server.art.model.DetailedDexInfo;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.SharedLibrary;
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.DexClassLoader;
+import dalvik.system.PathClassLoader;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/** @hide */
+public class PrimaryDexUtils {
+ public static final String PROFILE_PRIMARY = "primary";
+ private static final String SHARED_LIBRARY_LOADER_TYPE = PathClassLoader.class.getName();
+
+ /**
+ * Returns the basic information about all primary dex files belonging to the package. The
+ * return value is a list where the entry at index 0 is the information about the base APK, and
+ * the entry at index i is the information about the (i-1)-th split APK.
+ */
+ @NonNull
+ public static List<PrimaryDexInfo> getDexInfo(@NonNull AndroidPackage pkg) {
+ return getDexInfoImpl(pkg)
+ .stream()
+ .map(builder -> builder.build())
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Same as above, but requires {@link PackageState} in addition, and returns the detailed
+ * information, including the class loader context.
+ */
+ @NonNull
+ public static List<DetailedPrimaryDexInfo> getDetailedDexInfo(
+ @NonNull PackageState pkgState, @NonNull AndroidPackage pkg) {
+ return getDetailedDexInfoImpl(pkgState, pkg)
+ .stream()
+ .map(builder -> builder.buildDetailed())
+ .collect(Collectors.toList());
+ }
+
+ /** Returns the basic information about a dex file specified by {@code splitName}. */
+ @NonNull
+ public static PrimaryDexInfo getDexInfoBySplitName(
+ @NonNull AndroidPackage pkg, @Nullable String splitName) {
+ if (splitName == null) {
+ return getDexInfo(pkg).get(0);
+ } else {
+ return getDexInfo(pkg)
+ .stream()
+ .filter(info -> splitName.equals(info.splitName()))
+ .findFirst()
+ .orElseThrow(() -> {
+ return new IllegalArgumentException(
+ String.format("Split '%s' not found", splitName));
+ });
+ }
+ }
+
+ @NonNull
+ private static List<PrimaryDexInfoBuilder> getDexInfoImpl(@NonNull AndroidPackage pkg) {
+ List<PrimaryDexInfoBuilder> dexInfos = new ArrayList<>();
+
+ for (var split : pkg.getSplits()) {
+ dexInfos.add(new PrimaryDexInfoBuilder(split));
+ }
+
+ return dexInfos;
+ }
+
+ @NonNull
+ private static List<PrimaryDexInfoBuilder> getDetailedDexInfoImpl(
+ @NonNull PackageState pkgState, @NonNull AndroidPackage pkg) {
+ List<PrimaryDexInfoBuilder> dexInfos = getDexInfoImpl(pkg);
+
+ PrimaryDexInfoBuilder baseApk = dexInfos.get(0);
+ baseApk.mClassLoaderName = baseApk.mSplit.getClassLoaderName();
+ File baseDexFile = new File(baseApk.mSplit.getPath());
+ baseApk.mRelativeDexPath = baseDexFile.getName();
+
+ // Shared libraries are the dependencies of the base APK.
+ baseApk.mSharedLibrariesContext =
+ encodeSharedLibraries(pkgState.getSharedLibraryDependencies());
+
+ boolean isIsolatedSplitLoading = isIsolatedSplitLoading(pkg);
+
+ for (int i = 1; i < dexInfos.size(); i++) {
+ var dexInfoBuilder = dexInfos.get(i);
+ File splitDexFile = new File(dexInfoBuilder.mSplit.getPath());
+ if (!splitDexFile.getParent().equals(baseDexFile.getParent())) {
+ throw new IllegalStateException(
+ "Split APK and base APK are in different directories: "
+ + splitDexFile.getParent() + " != " + baseDexFile.getParent());
+ }
+ dexInfoBuilder.mRelativeDexPath = splitDexFile.getName();
+ if (isIsolatedSplitLoading && dexInfoBuilder.mSplit.isHasCode()) {
+ dexInfoBuilder.mClassLoaderName = dexInfoBuilder.mSplit.getClassLoaderName();
+
+ List<AndroidPackageSplit> dependencies = dexInfoBuilder.mSplit.getDependencies();
+ if (!Utils.isEmpty(dependencies)) {
+ // We only care about the first dependency because it is the parent split. The
+ // rest are configuration splits, which we don't care.
+ AndroidPackageSplit dependency = dependencies.get(0);
+ for (var dexInfo : dexInfos) {
+ if (Objects.equals(dexInfo.mSplit, dependency)) {
+ dexInfoBuilder.mSplitDependency = dexInfo;
+ break;
+ }
+ }
+
+ if (dexInfoBuilder.mSplitDependency == null) {
+ throw new IllegalStateException(
+ "Split dependency not found for " + splitDexFile);
+ }
+ }
+ }
+ }
+
+ if (isIsolatedSplitLoading) {
+ computeClassLoaderContextsIsolated(dexInfos);
+ } else {
+ computeClassLoaderContexts(dexInfos);
+ }
+
+ return dexInfos;
+ }
+
+ /**
+ * Computes class loader context for an app that didn't request isolated split loading. Stores
+ * the results in {@link PrimaryDexInfoBuilder#mClassLoaderContext}.
+ *
+ * In this case, all the splits will be loaded in the base apk class loader (in the order of
+ * their definition).
+ *
+ * The CLC for the base APK is `CLN[]{shared-libraries}`; the CLC for the n-th split APK is
+ * `CLN[base.apk, split_0.apk, ..., split_n-1.apk]{shared-libraries}`; where `CLN` is the
+ * class loader name for the base APK.
+ */
+ private static void computeClassLoaderContexts(@NonNull List<PrimaryDexInfoBuilder> dexInfos) {
+ String baseClassLoaderName = dexInfos.get(0).mClassLoaderName;
+ String sharedLibrariesContext = dexInfos.get(0).mSharedLibrariesContext;
+ List<String> classpath = new ArrayList<>();
+ for (PrimaryDexInfoBuilder dexInfo : dexInfos) {
+ if (dexInfo.mSplit.isHasCode()) {
+ dexInfo.mClassLoaderContext = encodeClassLoader(baseClassLoaderName, classpath,
+ null /* parentContext */, sharedLibrariesContext);
+ }
+ // Note that the splits with no code are not removed from the classpath computation.
+ // I.e., split_n might get the split_n-1 in its classpath dependency even if split_n-1
+ // has no code.
+ // The splits with no code do not matter for the runtime which ignores APKs without code
+ // when doing the classpath checks. As such we could actually filter them but we don't
+ // do it in order to keep consistency with how the apps are loaded.
+ classpath.add(dexInfo.mRelativeDexPath);
+ }
+ }
+
+ /**
+ * Computes class loader context for an app that requested for isolated split loading. Stores
+ * the results in {@link PrimaryDexInfoBuilder#mClassLoaderContext}.
+ *
+ * In this case, each split will be loaded with a separate class loader, whose context is a
+ * chain formed from inter-split dependencies.
+ *
+ * The CLC for the base APK is `CLN[]{shared-libraries}`; the CLC for the n-th split APK that
+ * depends on the base APK is `CLN_n[];CLN[base.apk]{shared-libraries}`; the CLC for the n-th
+ * split APK that depends on the m-th split APK is
+ * `CLN_n[];CLN_m[split_m.apk];...;CLN[base.apk]{shared-libraries}`; where `CLN` is the base
+ * class loader name for the base APK, `CLN_i` is the class loader name for the i-th split APK,
+ * and `...` represents the ancestors along the dependency chain.
+ *
+ * Specially, if a split does not have any dependency, the CLC for it is `CLN_n[]`.
+ */
+ private static void computeClassLoaderContextsIsolated(
+ @NonNull List<PrimaryDexInfoBuilder> dexInfos) {
+ for (PrimaryDexInfoBuilder dexInfo : dexInfos) {
+ if (dexInfo.mSplit.isHasCode()) {
+ dexInfo.mClassLoaderContext = encodeClassLoader(dexInfo.mClassLoaderName,
+ null /* classpath */, getParentContextRecursive(dexInfo),
+ dexInfo.mSharedLibrariesContext);
+ }
+ }
+ }
+
+ /**
+ * Computes the parent class loader context, recursively. Caches results in {@link
+ * PrimaryDexInfoBuilder#mContextForChildren}.
+ */
+ @Nullable
+ private static String getParentContextRecursive(@NonNull PrimaryDexInfoBuilder dexInfo) {
+ if (dexInfo.mSplitDependency == null) {
+ return null;
+ }
+ PrimaryDexInfoBuilder parent = dexInfo.mSplitDependency;
+ if (parent.mContextForChildren == null) {
+ parent.mContextForChildren =
+ encodeClassLoader(parent.mClassLoaderName, List.of(parent.mRelativeDexPath),
+ getParentContextRecursive(parent), parent.mSharedLibrariesContext);
+ }
+ return parent.mContextForChildren;
+ }
+
+ /**
+ * Returns class loader context in the format of
+ * `CLN[classpath...]{share-libraries};parent-context`, where `CLN` is the class loader name.
+ */
+ @NonNull
+ private static String encodeClassLoader(@Nullable String classLoaderName,
+ @Nullable List<String> classpath, @Nullable String parentContext,
+ @Nullable String sharedLibrariesContext) {
+ StringBuilder classLoaderContext = new StringBuilder();
+
+ classLoaderContext.append(encodeClassLoaderName(classLoaderName));
+
+ classLoaderContext.append(
+ "[" + (classpath != null ? String.join(":", classpath) : "") + "]");
+
+ if (!TextUtils.isEmpty(sharedLibrariesContext)) {
+ classLoaderContext.append(sharedLibrariesContext);
+ }
+
+ if (!TextUtils.isEmpty(parentContext)) {
+ classLoaderContext.append(";" + parentContext);
+ }
+
+ return classLoaderContext.toString();
+ }
+
+ @NonNull
+ private static String encodeClassLoaderName(@Nullable String classLoaderName) {
+ // `PathClassLoader` and `DexClassLoader` are grouped together because they have the same
+ // behavior. For null values we default to "PCL". This covers the case where a package does
+ // not specify any value for its class loader.
+ if (classLoaderName == null || PathClassLoader.class.getName().equals(classLoaderName)
+ || DexClassLoader.class.getName().equals(classLoaderName)) {
+ return "PCL";
+ } else if (DelegateLastClassLoader.class.getName().equals(classLoaderName)) {
+ return "DLC";
+ } else {
+ throw new IllegalStateException("Unsupported classLoaderName: " + classLoaderName);
+ }
+ }
+
+ /**
+ * Returns shared libraries context in the format of
+ * `{PCL[library_1_dex_1.jar:library_1_dex_2.jar:...]{library_1-dependencies}#PCL[
+ * library_1_dex_2.jar:library_2_dex_2.jar:...]{library_2-dependencies}#...}`.
+ */
+ @Nullable
+ private static String encodeSharedLibraries(@Nullable List<SharedLibrary> sharedLibraries) {
+ if (Utils.isEmpty(sharedLibraries)) {
+ return null;
+ }
+ return sharedLibraries.stream()
+ .map(library
+ -> encodeClassLoader(SHARED_LIBRARY_LOADER_TYPE, library.getAllCodePaths(),
+ null /* parentContext */,
+ encodeSharedLibraries(library.getDependencies())))
+ .collect(Collectors.joining("#", "{", "}"));
+ }
+
+ public static boolean isIsolatedSplitLoading(@NonNull AndroidPackage pkg) {
+ return pkg.isIsolatedSplitLoading() && pkg.getSplits().size() > 1;
+ }
+
+ @NonNull
+ public static ProfilePath buildRefProfilePath(
+ @NonNull PackageState pkgState, @NonNull PrimaryDexInfo dexInfo) {
+ String profileName = getProfileName(dexInfo.splitName());
+ return AidlUtils.buildProfilePathForPrimaryRef(pkgState.getPackageName(), profileName);
+ }
+
+ @NonNull
+ public static OutputProfile buildOutputProfile(@NonNull PackageState pkgState,
+ @NonNull PrimaryDexInfo dexInfo, int uid, int gid, boolean isPublic) {
+ String profileName = getProfileName(dexInfo.splitName());
+ return AidlUtils.buildOutputProfileForPrimary(
+ pkgState.getPackageName(), profileName, uid, gid, isPublic);
+ }
+
+ @NonNull
+ public static List<ProfilePath> getCurProfiles(@NonNull UserManager userManager,
+ @NonNull PackageState pkgState, @NonNull PrimaryDexInfo dexInfo) {
+ List<ProfilePath> profiles = new ArrayList<>();
+ for (UserHandle handle : userManager.getUserHandles(true /* excludeDying */)) {
+ int userId = handle.getIdentifier();
+ PackageUserState userState = pkgState.getStateForUser(handle);
+ if (userState.isInstalled()) {
+ profiles.add(AidlUtils.buildProfilePathForPrimaryCur(
+ userId, pkgState.getPackageName(), getProfileName(dexInfo.splitName())));
+ }
+ }
+ return profiles;
+ }
+
+ @NonNull
+ public static String getProfileName(@Nullable String splitName) {
+ return splitName == null ? PROFILE_PRIMARY : splitName + ".split";
+ }
+
+ /** Basic information about a primary dex file (either the base APK or a split APK). */
+ @Immutable
+ public static class PrimaryDexInfo {
+ private final @NonNull AndroidPackageSplit mSplit;
+
+ PrimaryDexInfo(@NonNull AndroidPackageSplit split) {
+ mSplit = split;
+ }
+
+ /** The path to the dex file. */
+ public @NonNull String dexPath() {
+ return mSplit.getPath();
+ }
+
+ /** True if the dex file has code. */
+ public boolean hasCode() {
+ return mSplit.isHasCode();
+ }
+
+ /** The name of the split, or null for base APK. */
+ public @Nullable String splitName() {
+ return mSplit.getName();
+ }
+ }
+
+ /**
+ * Detailed information about a primary dex file (either the base APK or a split APK). It
+ * contains the class loader context in addition to what is in {@link PrimaryDexInfo}, but
+ * producing it requires {@link PackageState}.
+ */
+ @Immutable
+ public static class DetailedPrimaryDexInfo extends PrimaryDexInfo implements DetailedDexInfo {
+ private final @Nullable String mClassLoaderContext;
+
+ DetailedPrimaryDexInfo(
+ @NonNull AndroidPackageSplit split, @Nullable String classLoaderContext) {
+ super(split);
+ mClassLoaderContext = classLoaderContext;
+ }
+
+ /**
+ * A string describing the structure of the class loader that the dex file is loaded with.
+ */
+ public @Nullable String classLoaderContext() {
+ return mClassLoaderContext;
+ }
+ }
+
+ private static class PrimaryDexInfoBuilder {
+ @NonNull AndroidPackageSplit mSplit;
+ @Nullable String mRelativeDexPath = null;
+ @Nullable String mClassLoaderContext = null;
+ @Nullable String mClassLoaderName = null;
+ @Nullable PrimaryDexInfoBuilder mSplitDependency = null;
+ /** The class loader context of the shared libraries. Only applicable for the base APK. */
+ @Nullable String mSharedLibrariesContext = null;
+ /** The class loader context for children to use when this dex file is used as a parent. */
+ @Nullable String mContextForChildren = null;
+
+ PrimaryDexInfoBuilder(@NonNull AndroidPackageSplit split) {
+ mSplit = split;
+ }
+
+ PrimaryDexInfo build() {
+ return new PrimaryDexInfo(mSplit);
+ }
+
+ DetailedPrimaryDexInfo buildDetailed() {
+ return new DetailedPrimaryDexInfo(mSplit, mClassLoaderContext);
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexopter.java b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java
new file mode 100644
index 0000000..3728ab8
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/PrimaryDexopter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.OutputArtifacts.PermissionSettings;
+import static com.android.server.art.OutputArtifacts.PermissionSettings.SeContext;
+import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo;
+import static com.android.server.art.Utils.Abi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.CancellationSignal;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.pm.PackageStateModulesUtils;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+
+import dalvik.system.DexFile;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** @hide */
+public class PrimaryDexopter extends Dexopter<DetailedPrimaryDexInfo> {
+ private static final String TAG = "PrimaryDexopter";
+
+ private final int mSharedGid;
+
+ public PrimaryDexopter(@NonNull Context context, @NonNull PackageState pkgState,
+ @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ this(new Injector(context), pkgState, pkg, params, cancellationSignal);
+ }
+
+ @VisibleForTesting
+ public PrimaryDexopter(@NonNull Injector injector, @NonNull PackageState pkgState,
+ @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ super(injector, pkgState, pkg, params, cancellationSignal);
+
+ mSharedGid = UserHandle.getSharedAppGid(pkgState.getAppId());
+ if (mSharedGid < 0) {
+ throw new IllegalStateException(
+ String.format("Unable to get shared gid for package '%s' (app ID: %d)",
+ pkgState.getPackageName(), pkgState.getAppId()));
+ }
+ }
+
+ @Override
+ protected boolean isInDalvikCache() {
+ return Utils.isInDalvikCache(mPkgState);
+ }
+
+ @Override
+ @NonNull
+ protected List<DetailedPrimaryDexInfo> getDexInfoList() {
+ return PrimaryDexUtils.getDetailedDexInfo(mPkgState, mPkg);
+ }
+
+ @Override
+ protected boolean isDexoptable(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ if (!dexInfo.hasCode()) {
+ return false;
+ }
+ if ((mParams.getFlags() & ArtFlags.FLAG_FOR_SINGLE_SPLIT) != 0) {
+ return Objects.equals(mParams.getSplitName(), dexInfo.splitName());
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean needsToBeShared(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ return isSharedLibrary()
+ || mInjector.getDexUseManager().isPrimaryDexUsedByOtherApps(
+ mPkgState.getPackageName(), dexInfo.dexPath());
+ }
+
+ @Override
+ protected boolean isDexFilePublic(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ // The filesystem permission of a primary dex file always has the S_IROTH bit. In practice,
+ // the accessibility is enforced by Application Sandbox, not filesystem permission.
+ return true;
+ }
+
+ @Override
+ @Nullable
+ protected ProfilePath initReferenceProfile(@NonNull DetailedPrimaryDexInfo dexInfo)
+ throws RemoteException {
+ OutputProfile output = buildOutputProfile(dexInfo, true /* isPublic */);
+
+ ProfilePath prebuiltProfile = AidlUtils.buildProfilePathForPrebuilt(dexInfo.dexPath());
+ try {
+ // If the APK is really a prebuilt one, rewriting the profile is unnecessary because the
+ // dex location is known at build time and is correctly set in the profile header.
+ // However, the APK can also be an installed one, in which case partners may place a
+ // profile file next to the APK at install time. Rewriting the profile in the latter
+ // case is necessary.
+ if (mInjector.getArtd().copyAndRewriteProfile(
+ prebuiltProfile, output, dexInfo.dexPath())) {
+ return ProfilePath.tmpProfilePath(output.profilePath);
+ }
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG,
+ "Failed to use prebuilt profile "
+ + AidlUtils.toString(output.profilePath.finalPath),
+ e);
+ }
+
+ ProfilePath dmProfile = AidlUtils.buildProfilePathForDm(dexInfo.dexPath());
+ try {
+ if (mInjector.getArtd().copyAndRewriteProfile(dmProfile, output, dexInfo.dexPath())) {
+ return ProfilePath.tmpProfilePath(output.profilePath);
+ }
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG,
+ "Failed to use profile in dex metadata file "
+ + AidlUtils.toString(output.profilePath.finalPath),
+ e);
+ }
+
+ return null;
+ }
+
+ @Override
+ @NonNull
+ protected PermissionSettings getPermissionSettings(
+ @NonNull DetailedPrimaryDexInfo dexInfo, boolean canBePublic) {
+ // The files and directories should belong to the system so that Package Manager can manage
+ // them (e.g., move them around).
+ // We don't need the "read" bit for "others" on the directories because others only need to
+ // access the files in the directories, but they don't need to "ls" the directories.
+ FsPermission dirFsPermission = AidlUtils.buildFsPermission(Process.SYSTEM_UID /* uid */,
+ Process.SYSTEM_UID /* gid */, false /* isOtherReadable */,
+ true /* isOtherExecutable */);
+ FsPermission fileFsPermission = AidlUtils.buildFsPermission(
+ Process.SYSTEM_UID /* uid */, mSharedGid /* gid */, canBePublic);
+ // For primary dex, we can use the default SELinux context.
+ SeContext seContext = null;
+ return AidlUtils.buildPermissionSettings(dirFsPermission, fileFsPermission, seContext);
+ }
+
+ @Override
+ @NonNull
+ protected List<Abi> getAllAbis(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ return Utils.getAllAbis(mPkgState);
+ }
+
+ @Override
+ @NonNull
+ protected ProfilePath buildRefProfilePath(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ return PrimaryDexUtils.buildRefProfilePath(mPkgState, dexInfo);
+ }
+
+ @Override
+ protected boolean isAppImageAllowed(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ // Only allow app image for the base APK because having multiple app images is not
+ // supported.
+ // Additionally, disable app images if the app requests for the splits to be loaded in
+ // isolation because app images are unsupported for multiple class loaders (b/72696798).
+ // TODO(jiakaiz): Investigate whether this is still the best choice today.
+ return dexInfo.splitName() == null && !PrimaryDexUtils.isIsolatedSplitLoading(mPkg);
+ }
+
+ @Override
+ @NonNull
+ protected OutputProfile buildOutputProfile(
+ @NonNull DetailedPrimaryDexInfo dexInfo, boolean isPublic) {
+ return PrimaryDexUtils.buildOutputProfile(
+ mPkgState, dexInfo, Process.SYSTEM_UID, mSharedGid, isPublic);
+ }
+
+ @Override
+ @NonNull
+ protected List<ProfilePath> getCurProfiles(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ return PrimaryDexUtils.getCurProfiles(mInjector.getUserManager(), mPkgState, dexInfo);
+ }
+
+ @Override
+ @Nullable
+ protected DexMetadataPath buildDmPath(@NonNull DetailedPrimaryDexInfo dexInfo) {
+ return AidlUtils.buildDexMetadataPath(dexInfo.dexPath());
+ }
+
+ @SuppressLint("NewApi")
+ private boolean isSharedLibrary() {
+ return PackageStateModulesUtils.isLoadableInOtherProcesses(mPkgState, true /* codeOnly */);
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/ReasonMapping.java b/libartservice/service/java/com/android/server/art/ReasonMapping.java
new file mode 100644
index 0000000..ca9b9ed
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/ReasonMapping.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.model.ArtFlags.PriorityClassApi;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+
+import com.android.server.art.model.ArtFlags;
+import com.android.server.pm.PackageManagerLocal;
+
+import dalvik.system.DexFile;
+
+import java.util.Set;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Maps a compilation reason to a compiler filter and a priority class.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public class ReasonMapping {
+ private ReasonMapping() {}
+
+ /** Dexopting apps on the first boot after flashing or factory resetting the device. */
+ public static final String REASON_FIRST_BOOT = "first-boot";
+ /** Dexopting apps on the next boot after an OTA. */
+ public static final String REASON_BOOT_AFTER_OTA = "boot-after-ota";
+ /** Dexopting apps on the next boot after a mainline update. */
+ public static final String REASON_BOOT_AFTER_MAINLINE_UPDATE = "boot-after-mainline-update";
+ /** Installing an app after user presses the "install"/"update" button. */
+ public static final String REASON_INSTALL = "install";
+ /** Dexopting apps in the background. */
+ public static final String REASON_BG_DEXOPT = "bg-dexopt";
+ /** Invoked by cmdline. */
+ public static final String REASON_CMDLINE = "cmdline";
+ /** Downgrading the compiler filter when an app is not used for a long time. */
+ public static final String REASON_INACTIVE = "inactive";
+
+ // Reasons for Play Install Hints (go/install-hints).
+ public static final String REASON_INSTALL_FAST = "install-fast";
+ public static final String REASON_INSTALL_BULK = "install-bulk";
+ public static final String REASON_INSTALL_BULK_SECONDARY = "install-bulk-secondary";
+ public static final String REASON_INSTALL_BULK_DOWNGRADED = "install-bulk-downgraded";
+ public static final String REASON_INSTALL_BULK_SECONDARY_DOWNGRADED =
+ "install-bulk-secondary-downgraded";
+
+ /** @hide */
+ public static final Set<String> REASONS_FOR_INSTALL = Set.of(REASON_INSTALL,
+ REASON_INSTALL_FAST, REASON_INSTALL_BULK, REASON_INSTALL_BULK_SECONDARY,
+ REASON_INSTALL_BULK_DOWNGRADED, REASON_INSTALL_BULK_SECONDARY_DOWNGRADED);
+
+ /**
+ * Reasons for {@link ArtManagerLocal#dexoptPackages}.
+ *
+ * @hide
+ */
+ // clang-format off
+ @StringDef(prefix = "REASON_", value = {
+ REASON_FIRST_BOOT,
+ REASON_BOOT_AFTER_OTA,
+ REASON_BOOT_AFTER_MAINLINE_UPDATE,
+ REASON_BG_DEXOPT,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BatchDexoptReason {}
+
+ /**
+ * Reasons for {@link ArtManagerLocal#onBoot(String, Executor, Consumer<OperationProgress>)}.
+ *
+ * @hide
+ */
+ // clang-format off
+ @StringDef(prefix = "REASON_", value = {
+ REASON_FIRST_BOOT,
+ REASON_BOOT_AFTER_OTA,
+ REASON_BOOT_AFTER_MAINLINE_UPDATE,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BootReason {}
+
+ /**
+ * Loads the compiler filter from the system property for the given reason and checks for
+ * validity.
+ *
+ * @throws IllegalArgumentException if the reason is invalid
+ * @throws IllegalStateException if the system property value is invalid
+ *
+ * @hide
+ */
+ @NonNull
+ public static String getCompilerFilterForReason(@NonNull String reason) {
+ String value = SystemProperties.get("pm.dexopt." + reason);
+ if (TextUtils.isEmpty(value)) {
+ throw new IllegalArgumentException("No compiler filter for reason '" + reason + "'");
+ }
+ if (!Utils.isValidArtServiceCompilerFilter(value)) {
+ throw new IllegalStateException(
+ "Got invalid compiler filter '" + value + "' for reason '" + reason + "'");
+ }
+ return value;
+ }
+
+ /**
+ * Loads the compiler filter from the system property for:
+ * - shared libraries
+ * - apps used by other apps without a dex metadata file
+ *
+ * @throws IllegalStateException if the system property value is invalid
+ *
+ * @hide
+ */
+ @NonNull
+ public static String getCompilerFilterForShared() {
+ // "shared" is technically not a compilation reason, but the compiler filter is defined as a
+ // system property as if "shared" is a reason.
+ String value = getCompilerFilterForReason("shared");
+ if (DexFile.isProfileGuidedCompilerFilter(value)) {
+ throw new IllegalStateException(
+ "Compiler filter for 'shared' must not be profile guided, got '" + value + "'");
+ }
+ return value;
+ }
+
+ /**
+ * Returns the priority for the given reason.
+ *
+ * @throws IllegalArgumentException if the reason is invalid
+ * @see PriorityClassApi
+ *
+ * @hide
+ */
+ public static @PriorityClassApi byte getPriorityClassForReason(@NonNull String reason) {
+ switch (reason) {
+ case REASON_FIRST_BOOT:
+ case REASON_BOOT_AFTER_OTA:
+ case REASON_BOOT_AFTER_MAINLINE_UPDATE:
+ return ArtFlags.PRIORITY_BOOT;
+ case REASON_INSTALL_FAST:
+ return ArtFlags.PRIORITY_INTERACTIVE_FAST;
+ case REASON_INSTALL:
+ case REASON_CMDLINE:
+ return ArtFlags.PRIORITY_INTERACTIVE;
+ case REASON_BG_DEXOPT:
+ case REASON_INACTIVE:
+ case REASON_INSTALL_BULK:
+ case REASON_INSTALL_BULK_SECONDARY:
+ case REASON_INSTALL_BULK_DOWNGRADED:
+ case REASON_INSTALL_BULK_SECONDARY_DOWNGRADED:
+ return ArtFlags.PRIORITY_BACKGROUND;
+ default:
+ throw new IllegalArgumentException("No priority class for reason '" + reason + "'");
+ }
+ }
+
+ /**
+ * Loads the concurrency from the system property, for batch dexopt ({@link
+ * ArtManagerLocal#dexoptPackages}), or 1 if the system property is not found or cannot be
+ * parsed.
+ *
+ * @hide
+ */
+ public static int getConcurrencyForReason(@NonNull @BatchDexoptReason String reason) {
+ return SystemProperties.getInt("pm.dexopt." + reason + ".concurrency", 1 /* def */);
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/SecondaryDexopter.java b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java
new file mode 100644
index 0000000..b751073
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo;
+import static com.android.server.art.OutputArtifacts.PermissionSettings;
+import static com.android.server.art.OutputArtifacts.PermissionSettings.SeContext;
+import static com.android.server.art.Utils.Abi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.CancellationSignal;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+
+import java.util.List;
+
+/** @hide */
+public class SecondaryDexopter extends Dexopter<DetailedSecondaryDexInfo> {
+ private static final String TAG = "SecondaryDexopter";
+
+ public SecondaryDexopter(@NonNull Context context, @NonNull PackageState pkgState,
+ @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ this(new Injector(context), pkgState, pkg, params, cancellationSignal);
+ }
+
+ @VisibleForTesting
+ public SecondaryDexopter(@NonNull Injector injector, @NonNull PackageState pkgState,
+ @NonNull AndroidPackage pkg, @NonNull DexoptParams params,
+ @NonNull CancellationSignal cancellationSignal) {
+ super(injector, pkgState, pkg, params, cancellationSignal);
+ }
+
+ @Override
+ protected boolean isInDalvikCache() {
+ // A secondary dex file is added by the app, so it's always in a writable location and hence
+ // never uses dalvik-cache.
+ return false;
+ }
+
+ @Override
+ @NonNull
+ protected List<DetailedSecondaryDexInfo> getDexInfoList() {
+ return mInjector.getDexUseManager().getFilteredDetailedSecondaryDexInfo(
+ mPkgState.getPackageName());
+ }
+
+ @Override
+ protected boolean isDexoptable(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ return true;
+ }
+
+ @Override
+ protected boolean needsToBeShared(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ return dexInfo.isUsedByOtherApps();
+ }
+
+ @Override
+ protected boolean isDexFilePublic(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ return dexInfo.isDexFilePublic();
+ }
+
+ @Override
+ @Nullable
+ protected ProfilePath initReferenceProfile(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ // A secondary dex file doesn't have any external profile to use.
+ return null;
+ }
+
+ @Override
+ @NonNull
+ protected PermissionSettings getPermissionSettings(
+ @NonNull DetailedSecondaryDexInfo dexInfo, boolean canBePublic) {
+ int uid = getUid(dexInfo);
+ // We need the "execute" bit for "others" even though `canBePublic` is false because the
+ // directory can contain other artifacts that needs to be public.
+ // We don't need the "read" bit for "others" on the directories because others only need to
+ // access the files in the directories, but they don't need to "ls" the directories.
+ FsPermission dirFsPermission = AidlUtils.buildFsPermission(uid /* uid */, uid /* gid */,
+ false /* isOtherReadable */, true /* isOtherExecutable */);
+ FsPermission fileFsPermission =
+ AidlUtils.buildFsPermission(uid /* uid */, uid /* gid */, canBePublic);
+ SeContext seContext = AidlUtils.buildSeContext(mPkgState.getSeInfo(), uid);
+ return AidlUtils.buildPermissionSettings(dirFsPermission, fileFsPermission, seContext);
+ }
+
+ @Override
+ @NonNull
+ protected List<Abi> getAllAbis(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ return Utils.getAllAbisForNames(dexInfo.abiNames(), mPkgState);
+ }
+
+ @Override
+ @NonNull
+ protected ProfilePath buildRefProfilePath(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ return AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath());
+ }
+
+ @Override
+ protected boolean isAppImageAllowed(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ // The runtime can only load the app image of the base APK.
+ return false;
+ }
+
+ @Override
+ @NonNull
+ protected OutputProfile buildOutputProfile(
+ @NonNull DetailedSecondaryDexInfo dexInfo, boolean isPublic) {
+ int uid = getUid(dexInfo);
+ return AidlUtils.buildOutputProfileForSecondary(dexInfo.dexPath(), uid, uid, isPublic);
+ }
+
+ @Override
+ @NonNull
+ protected List<ProfilePath> getCurProfiles(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ // A secondary dex file can only be loaded by one user, so there is only one profile.
+ return List.of(AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath()));
+ }
+
+ @Override
+ @Nullable
+ protected DexMetadataPath buildDmPath(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ return null;
+ }
+
+ private int getUid(@NonNull DetailedSecondaryDexInfo dexInfo) {
+ return dexInfo.userHandle().getUid(mPkgState.getAppId());
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/Utils.java b/libartservice/service/java/com/android/server/art/Utils.java
new file mode 100644
index 0000000..0d40923
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/Utils.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.apphibernation.AppHibernationManager;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.modules.utils.pm.PackageStateModulesUtils;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+
+import dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+
+import com.google.auto.value.AutoValue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+
+/** @hide */
+public final class Utils {
+ public static final String TAG = "ArtServiceUtils";
+ public static final String PLATFORM_PACKAGE_NAME = "android";
+
+ private Utils() {}
+
+ /**
+ * Checks if given array is null or has zero elements.
+ */
+ public static <T> boolean isEmpty(@Nullable Collection<T> array) {
+ return array == null || array.isEmpty();
+ }
+
+ /**
+ * Checks if given array is null or has zero elements.
+ */
+ public static <T> boolean isEmpty(@Nullable SparseArray<T> array) {
+ return array == null || array.size() == 0;
+ }
+
+ /**
+ * Checks if given array is null or has zero elements.
+ */
+ public static boolean isEmpty(@Nullable int[] array) {
+ return array == null || array.length == 0;
+ }
+
+ /** Returns the ABI information for the package. */
+ @NonNull
+ public static List<Abi> getAllAbis(@NonNull PackageState pkgState) {
+ List<Abi> abis = new ArrayList<>();
+ abis.add(getPrimaryAbi(pkgState));
+ String pkgPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
+ String pkgSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
+ if (pkgSecondaryCpuAbi != null) {
+ Utils.check(pkgState.getPrimaryCpuAbi() != null);
+ String isa = getTranslatedIsa(VMRuntime.getInstructionSet(pkgSecondaryCpuAbi));
+ abis.add(Abi.create(nativeIsaToAbi(isa), isa, false /* isPrimaryAbi */));
+ }
+ // Primary and secondary ABIs should be guaranteed to have different ISAs.
+ if (abis.size() == 2 && abis.get(0).isa().equals(abis.get(1).isa())) {
+ throw new IllegalStateException(String.format(
+ "Duplicate ISA: primary ABI '%s' ('%s'), secondary ABI '%s' ('%s')",
+ pkgPrimaryCpuAbi, abis.get(0).name(), pkgSecondaryCpuAbi, abis.get(1).name()));
+ }
+ return abis;
+ }
+
+ /** Returns the ABI information for the ABIs with the given names. */
+ @NonNull
+ public static List<Abi> getAllAbisForNames(
+ @NonNull Set<String> abiNames, @NonNull PackageState pkgState) {
+ Abi pkgPrimaryAbi = getPrimaryAbi(pkgState);
+ return abiNames.stream()
+ .map(name
+ -> Abi.create(name, VMRuntime.getInstructionSet(name),
+ name.equals(pkgPrimaryAbi.name())))
+ .collect(Collectors.toList());
+ }
+
+ @NonNull
+ public static Abi getPrimaryAbi(@NonNull PackageState pkgState) {
+ String primaryCpuAbi = pkgState.getPrimaryCpuAbi();
+ if (primaryCpuAbi != null) {
+ String isa = getTranslatedIsa(VMRuntime.getInstructionSet(primaryCpuAbi));
+ return Abi.create(nativeIsaToAbi(isa), isa, true /* isPrimaryAbi */);
+ }
+ // This is the most common case. The package manager can't infer the ABIs, probably because
+ // the package doesn't contain any native library. The app is launched with the device's
+ // preferred ABI.
+ String preferredAbi = Constants.getPreferredAbi();
+ return Abi.create(
+ preferredAbi, VMRuntime.getInstructionSet(preferredAbi), true /* isPrimaryAbi */);
+ }
+
+ /**
+ * If the given ISA isn't native to the device, returns the ISA that the native bridge
+ * translates it to. Otherwise, returns the ISA as is. This is the ISA that the app is actually
+ * launched with and therefore the ISA that should be used to compile the app.
+ */
+ @NonNull
+ private static String getTranslatedIsa(@NonNull String isa) {
+ String abi64 = Constants.getNative64BitAbi();
+ String abi32 = Constants.getNative32BitAbi();
+ if ((abi64 != null && isa.equals(VMRuntime.getInstructionSet(abi64)))
+ || (abi32 != null && isa.equals(VMRuntime.getInstructionSet(abi32)))) {
+ return isa;
+ }
+ String translatedIsa = SystemProperties.get("ro.dalvik.vm.isa." + isa);
+ if (TextUtils.isEmpty(translatedIsa)) {
+ throw new IllegalStateException(String.format("Unsupported isa '%s'", isa));
+ }
+ return translatedIsa;
+ }
+
+ @NonNull
+ private static String nativeIsaToAbi(@NonNull String isa) {
+ String abi64 = Constants.getNative64BitAbi();
+ if (abi64 != null && isa.equals(VMRuntime.getInstructionSet(abi64))) {
+ return abi64;
+ }
+ String abi32 = Constants.getNative32BitAbi();
+ if (abi32 != null && isa.equals(VMRuntime.getInstructionSet(abi32))) {
+ return abi32;
+ }
+ throw new IllegalStateException(String.format("Non-native isa '%s'", isa));
+ }
+
+ @NonNull
+ public static boolean isInDalvikCache(@NonNull PackageState pkg) {
+ return pkg.isSystem() && !pkg.isUpdatedSystemApp();
+ }
+
+ /** Returns true if the given string is a valid compiler filter. */
+ public static boolean isValidArtServiceCompilerFilter(@NonNull String compilerFilter) {
+ if (compilerFilter.equals(DexoptParams.COMPILER_FILTER_NOOP)) {
+ return true;
+ }
+ return DexFile.isValidCompilerFilter(compilerFilter);
+ }
+
+ @NonNull
+ public static IArtd getArtd() {
+ IArtd artd = IArtd.Stub.asInterface(ArtModuleServiceInitializer.getArtModuleServiceManager()
+ .getArtdServiceRegisterer()
+ .waitForService());
+ if (artd == null) {
+ throw new IllegalStateException("Unable to connect to artd");
+ }
+ return artd;
+ }
+
+ public static boolean implies(boolean cond1, boolean cond2) {
+ return cond1 ? cond2 : true;
+ }
+
+ public static void check(boolean cond) {
+ // This cannot be replaced with `assert` because `assert` is not enabled in Android.
+ if (!cond) {
+ throw new IllegalStateException("Check failed");
+ }
+ }
+
+ @NonNull
+ public static PackageState getPackageStateOrThrow(
+ @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @NonNull String packageName) {
+ PackageState pkgState = snapshot.getPackageState(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException("Package not found: " + packageName);
+ }
+ return pkgState;
+ }
+
+ @NonNull
+ public static AndroidPackage getPackageOrThrow(@NonNull PackageState pkgState) {
+ AndroidPackage pkg = pkgState.getAndroidPackage();
+ if (pkg == null) {
+ throw new IllegalArgumentException(
+ "Unable to get package " + pkgState.getPackageName());
+ }
+ return pkg;
+ }
+
+ @NonNull
+ public static String assertNonEmpty(@Nullable String str) {
+ if (TextUtils.isEmpty(str)) {
+ throw new IllegalArgumentException();
+ }
+ return str;
+ }
+
+ public static void executeAndWait(@NonNull Executor executor, @NonNull Runnable runnable) {
+ getFuture(CompletableFuture.runAsync(runnable, executor));
+ }
+
+ public static <T> T getFuture(Future<T> future) {
+ try {
+ return future.get();
+ } catch (ExecutionException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
+ throw new RuntimeException(cause);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns true if the given package is dexoptable.
+ *
+ * @param appHibernationManager the {@link AppHibernationManager} instance for checking
+ * hibernation status, or null to skip the check
+ */
+ @SuppressLint("NewApi")
+ public static boolean canDexoptPackage(
+ @NonNull PackageState pkgState, @Nullable AppHibernationManager appHibernationManager) {
+ if (!PackageStateModulesUtils.isDexoptable(pkgState)) {
+ return false;
+ }
+
+ // We do not dexopt unused packages.
+ // If `appHibernationManager` is null, the caller's intention is to skip the check.
+ if (appHibernationManager != null
+ && appHibernationManager.isHibernatingGlobally(pkgState.getPackageName())
+ && appHibernationManager.isOatArtifactDeletionEnabled()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public static long getPackageLastActiveTime(@NonNull PackageState pkgState,
+ @NonNull DexUseManagerLocal dexUseManager, @NonNull UserManager userManager) {
+ long lastUsedAtMs = dexUseManager.getPackageLastUsedAtMs(pkgState.getPackageName());
+ // The time where the last user installed the package the first time.
+ long lastFirstInstallTimeMs =
+ userManager.getUserHandles(true /* excludeDying */)
+ .stream()
+ .map(handle -> pkgState.getStateForUser(handle))
+ .map(userState -> userState.getFirstInstallTimeMillis())
+ .max(Long::compare)
+ .orElse(0l);
+ return Math.max(lastUsedAtMs, lastFirstInstallTimeMs);
+ }
+
+ public static void deleteIfExistsSafe(@Nullable File file) {
+ if (file != null) {
+ deleteIfExistsSafe(file.toPath());
+ }
+ }
+
+ public static void deleteIfExistsSafe(@NonNull Path path) {
+ try {
+ Files.deleteIfExists(path);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to delete file '" + path + "'", e);
+ }
+ }
+
+ @AutoValue
+ public abstract static class Abi {
+ static @NonNull Abi create(
+ @NonNull String name, @NonNull String isa, boolean isPrimaryAbi) {
+ return new AutoValue_Utils_Abi(name, isa, isPrimaryAbi);
+ }
+
+ // The ABI name. E.g., "arm64-v8a".
+ abstract @NonNull String name();
+
+ // The instruction set name. E.g., "arm64".
+ abstract @NonNull String isa();
+
+ abstract boolean isPrimaryAbi();
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/model/ArtFlags.java b/libartservice/service/java/com/android/server/art/model/ArtFlags.java
new file mode 100644
index 0000000..64ae807
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/ArtFlags.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.job.JobScheduler;
+
+import com.android.server.art.ArtManagerLocal;
+import com.android.server.art.PriorityClass;
+import com.android.server.art.ReasonMapping;
+import com.android.server.pm.PackageManagerLocal;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public class ArtFlags {
+ // Common flags.
+
+ /** Whether the operation is applied for primary dex'es. */
+ public static final int FLAG_FOR_PRIMARY_DEX = 1 << 0;
+ /** Whether the operation is applied for secondary dex'es. */
+ public static final int FLAG_FOR_SECONDARY_DEX = 1 << 1;
+
+ // Flags specific to `dexoptPackage`.
+
+ /** Whether to dexopt dependency libraries as well. */
+ public static final int FLAG_SHOULD_INCLUDE_DEPENDENCIES = 1 << 2;
+ /**
+ * Whether the intention is to downgrade the compiler filter. If true, the dexopt will
+ * be skipped if the target compiler filter is better than or equal to the compiler filter
+ * of the existing dexopt artifacts, or dexopt artifacts do not exist.
+ */
+ public static final int FLAG_SHOULD_DOWNGRADE = 1 << 3;
+ /**
+ * Whether to force dexopt. If true, the dexopt will be performed regardless of
+ * any existing dexopt artifacts.
+ */
+ public static final int FLAG_FORCE = 1 << 4;
+ /**
+ * If set, the dexopt will be performed for a single split. Otherwise, the dexopt
+ * will be performed for all splits. {@link DexoptParams.Builder#setSplitName()} can be used
+ * to specify the split to dexopt.
+ *
+ * When this flag is set, {@link #FLAG_FOR_PRIMARY_DEX} must be set, and {@link
+ * #FLAG_FOR_SECONDARY_DEX} and {@link #FLAG_SHOULD_INCLUDE_DEPENDENCIES} must not be set.
+ */
+ public static final int FLAG_FOR_SINGLE_SPLIT = 1 << 5;
+ /**
+ * If set, skips the dexopt if the remaining storage space is low. The threshold is
+ * controlled by the global settings {@code sys_storage_threshold_percentage} and {@code
+ * sys_storage_threshold_max_bytes}.
+ */
+ public static final int FLAG_SKIP_IF_STORAGE_LOW = 1 << 6;
+
+ /**
+ * Flags for {@link
+ * ArtManagerLocal#getDexoptStatus(PackageManagerLocal.FilteredSnapshot, String, int)}.
+ *
+ * @hide
+ */
+ // clang-format off
+ @IntDef(flag = true, prefix = "FLAG_", value = {
+ FLAG_FOR_PRIMARY_DEX,
+ FLAG_FOR_SECONDARY_DEX,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GetStatusFlags {}
+
+ /**
+ * Default flags that are used when {@link
+ * ArtManagerLocal#getDexoptStatus(PackageManagerLocal.FilteredSnapshot, String)} is
+ * called.
+ * Value: {@link #FLAG_FOR_PRIMARY_DEX}, {@link #FLAG_FOR_SECONDARY_DEX}.
+ */
+ public static @GetStatusFlags int defaultGetStatusFlags() {
+ return FLAG_FOR_PRIMARY_DEX | FLAG_FOR_SECONDARY_DEX;
+ }
+
+ /**
+ * Flags for {@link DexoptParams}.
+ *
+ * @hide
+ */
+ // clang-format off
+ @IntDef(flag = true, prefix = "FLAG_", value = {
+ FLAG_FOR_PRIMARY_DEX,
+ FLAG_FOR_SECONDARY_DEX,
+ FLAG_SHOULD_INCLUDE_DEPENDENCIES,
+ FLAG_SHOULD_DOWNGRADE,
+ FLAG_FORCE,
+ FLAG_FOR_SINGLE_SPLIT,
+ FLAG_SKIP_IF_STORAGE_LOW,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DexoptFlags {}
+
+ /**
+ * Default flags that are used when
+ * {@link DexoptParams.Builder#Builder(String)} is called.
+ *
+ * @hide
+ */
+ public static @DexoptFlags int defaultDexoptFlags(@NonNull String reason) {
+ switch (reason) {
+ case ReasonMapping.REASON_INSTALL:
+ case ReasonMapping.REASON_INSTALL_FAST:
+ case ReasonMapping.REASON_INSTALL_BULK:
+ case ReasonMapping.REASON_INSTALL_BULK_SECONDARY:
+ case ReasonMapping.REASON_INSTALL_BULK_DOWNGRADED:
+ case ReasonMapping.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED:
+ return FLAG_FOR_PRIMARY_DEX;
+ case ReasonMapping.REASON_INACTIVE:
+ return FLAG_FOR_PRIMARY_DEX | FLAG_FOR_SECONDARY_DEX | FLAG_SHOULD_DOWNGRADE;
+ case ReasonMapping.REASON_FIRST_BOOT:
+ case ReasonMapping.REASON_BOOT_AFTER_OTA:
+ case ReasonMapping.REASON_BOOT_AFTER_MAINLINE_UPDATE:
+ return FLAG_FOR_PRIMARY_DEX | FLAG_SHOULD_INCLUDE_DEPENDENCIES;
+ case ReasonMapping.REASON_BG_DEXOPT:
+ return FLAG_FOR_PRIMARY_DEX | FLAG_FOR_SECONDARY_DEX
+ | FLAG_SHOULD_INCLUDE_DEPENDENCIES | FLAG_SKIP_IF_STORAGE_LOW;
+ case ReasonMapping.REASON_CMDLINE:
+ default:
+ return FLAG_FOR_PRIMARY_DEX | FLAG_FOR_SECONDARY_DEX
+ | FLAG_SHOULD_INCLUDE_DEPENDENCIES;
+ }
+ }
+
+ // Keep in sync with `PriorityClass` except for `PRIORITY_NONE`.
+
+ /**
+ * Initial value. Not expected.
+ *
+ * @hide
+ */
+ public static final int PRIORITY_NONE = -1;
+ /** Indicates that the operation blocks boot. */
+ public static final int PRIORITY_BOOT = PriorityClass.BOOT;
+ /**
+ * Indicates that a human is waiting on the result and the operation is more latency sensitive
+ * than usual.
+ */
+ public static final int PRIORITY_INTERACTIVE_FAST = PriorityClass.INTERACTIVE_FAST;
+ /** Indicates that a human is waiting on the result. */
+ public static final int PRIORITY_INTERACTIVE = PriorityClass.INTERACTIVE;
+ /** Indicates that the operation runs in background. */
+ public static final int PRIORITY_BACKGROUND = PriorityClass.BACKGROUND;
+
+ /**
+ * Indicates the priority of an operation. The value affects the resource usage and the process
+ * priority. A higher value may result in faster execution but may consume more resources and
+ * compete for resources with other processes.
+ *
+ * @hide
+ */
+ // clang-format off
+ @IntDef(prefix = "PRIORITY_", value = {
+ PRIORITY_NONE,
+ PRIORITY_BOOT,
+ PRIORITY_INTERACTIVE_FAST,
+ PRIORITY_INTERACTIVE,
+ PRIORITY_BACKGROUND,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PriorityClassApi {}
+
+ /** The job has been successfully scheduled. */
+ public static final int SCHEDULE_SUCCESS = 0;
+
+ /** @see JobScheduler#RESULT_FAILURE */
+ public static final int SCHEDULE_JOB_SCHEDULER_FAILURE = 1;
+
+ /** The job is disabled by the system property {@code pm.dexopt.disable_bg_dexopt}. */
+ public static final int SCHEDULE_DISABLED_BY_SYSPROP = 2;
+
+ /**
+ * Indicates the result of scheduling a background dexopt job.
+ *
+ * @hide
+ */
+ // clang-format off
+ @IntDef(prefix = "SCHEDULE_", value = {
+ SCHEDULE_SUCCESS,
+ SCHEDULE_JOB_SCHEDULER_FAILURE,
+ SCHEDULE_DISABLED_BY_SYSPROP,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScheduleStatus {}
+
+ private ArtFlags() {}
+}
diff --git a/libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java b/libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java
new file mode 100644
index 0000000..ffe5500
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/BatchDexoptParams.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.Immutable;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+@AutoValue
+public abstract class BatchDexoptParams {
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ public static final class Builder {
+ private @NonNull List<String> mPackageNames; // This is assumed immutable.
+ private @NonNull DexoptParams mDexoptParams;
+
+ /** @hide */
+ public Builder(
+ @NonNull List<String> defaultPackages, @NonNull DexoptParams defaultDexoptParams) {
+ mPackageNames = defaultPackages; // The argument is assumed immutable.
+ mDexoptParams = defaultDexoptParams;
+ }
+
+ /**
+ * Sets the list of packages to dexopt. The dexopt will be scheduled in the given
+ * order.
+ *
+ * If not called, the default list will be used.
+ */
+ @NonNull
+ public Builder setPackages(@NonNull List<String> packageNames) {
+ mPackageNames = Collections.unmodifiableList(new ArrayList<>(packageNames));
+ return this;
+ }
+
+ /**
+ * Sets the params for dexopting each package.
+ *
+ * If not called, the default params built from {@link DexoptParams#Builder(String)} will
+ * be used.
+ */
+ @NonNull
+ public Builder setDexoptParams(@NonNull DexoptParams dexoptParams) {
+ mDexoptParams = dexoptParams;
+ return this;
+ }
+
+ /** Returns the built object. */
+ @NonNull
+ public BatchDexoptParams build() {
+ return new AutoValue_BatchDexoptParams(mPackageNames, mDexoptParams);
+ }
+ }
+
+ /** @hide */
+ protected BatchDexoptParams() {}
+
+ /** The ordered list of packages to dexopt. */
+ public abstract @NonNull List<String> getPackages();
+
+ /** The params for dexopting each package. */
+ public abstract @NonNull DexoptParams getDexoptParams();
+}
diff --git a/libartservice/service/java/com/android/server/art/model/Config.java b/libartservice/service/java/com/android/server/art/model/Config.java
new file mode 100644
index 0000000..49bc930
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/Config.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import static com.android.server.art.ArtManagerLocal.BatchDexoptStartCallback;
+import static com.android.server.art.ArtManagerLocal.DexoptDoneCallback;
+import static com.android.server.art.ArtManagerLocal.ScheduleBackgroundDexoptJobCallback;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.art.ArtManagerLocal;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A class that stores the configurations set by the consumer of ART Service at runtime. This class
+ * is thread-safe.
+ *
+ * @hide
+ */
+public class Config {
+ /** @see ArtManagerLocal#setBatchDexoptStartCallback(Executor, BatchDexoptStartCallback) */
+ @GuardedBy("this")
+ @Nullable
+ private Callback<BatchDexoptStartCallback, Void> mBatchDexoptStartCallback = null;
+
+ /**
+ * @see ArtManagerLocal#setScheduleBackgroundDexoptJobCallback(Executor,
+ * ScheduleBackgroundDexoptJobCallback)
+ */
+ @GuardedBy("this")
+ @Nullable
+ private Callback<ScheduleBackgroundDexoptJobCallback, Void>
+ mScheduleBackgroundDexoptJobCallback = null;
+
+ /**
+ * @see ArtManagerLocal#addDexoptDoneCallback(Executor, DexoptDoneCallback)
+ */
+ @GuardedBy("this")
+ @NonNull
+ private LinkedHashMap<DexoptDoneCallback, Callback<DexoptDoneCallback, Boolean>>
+ mDexoptDoneCallbacks = new LinkedHashMap<>();
+
+ public synchronized void setBatchDexoptStartCallback(
+ @NonNull Executor executor, @NonNull BatchDexoptStartCallback callback) {
+ mBatchDexoptStartCallback = Callback.create(callback, executor);
+ }
+
+ public synchronized void clearBatchDexoptStartCallback() {
+ mBatchDexoptStartCallback = null;
+ }
+
+ @Nullable
+ public synchronized Callback<BatchDexoptStartCallback, Void> getBatchDexoptStartCallback() {
+ return mBatchDexoptStartCallback;
+ }
+
+ public synchronized void setScheduleBackgroundDexoptJobCallback(
+ @NonNull Executor executor, @NonNull ScheduleBackgroundDexoptJobCallback callback) {
+ mScheduleBackgroundDexoptJobCallback = Callback.create(callback, executor);
+ }
+
+ public synchronized void clearScheduleBackgroundDexoptJobCallback() {
+ mScheduleBackgroundDexoptJobCallback = null;
+ }
+
+ @Nullable
+ public synchronized Callback<ScheduleBackgroundDexoptJobCallback, Void>
+ getScheduleBackgroundDexoptJobCallback() {
+ return mScheduleBackgroundDexoptJobCallback;
+ }
+
+ public synchronized void addDexoptDoneCallback(boolean onlyIncludeUpdates,
+ @NonNull Executor executor, @NonNull DexoptDoneCallback callback) {
+ if (mDexoptDoneCallbacks.putIfAbsent(
+ callback, Callback.create(callback, executor, onlyIncludeUpdates))
+ != null) {
+ throw new IllegalStateException("callback already added");
+ }
+ }
+
+ public synchronized void removeDexoptDoneCallback(@NonNull DexoptDoneCallback callback) {
+ mDexoptDoneCallbacks.remove(callback);
+ }
+
+ @NonNull
+ public synchronized List<Callback<DexoptDoneCallback, Boolean>> getDexoptDoneCallbacks() {
+ return new ArrayList<>(mDexoptDoneCallbacks.values());
+ }
+
+ @AutoValue
+ public static abstract class Callback<CallbackType, ExtraType> {
+ public abstract @NonNull CallbackType get();
+ public abstract @NonNull Executor executor();
+ public abstract @Nullable ExtraType extra();
+ static <CallbackType, ExtraType> @NonNull Callback<CallbackType, ExtraType> create(
+ @NonNull CallbackType callback, @NonNull Executor executor,
+ @Nullable ExtraType extra) {
+ return new AutoValue_Config_Callback<CallbackType, ExtraType>(
+ callback, executor, extra);
+ }
+ static <CallbackType> @NonNull Callback<CallbackType, Void> create(
+ @NonNull CallbackType callback, @NonNull Executor executor) {
+ return new AutoValue_Config_Callback<CallbackType, Void>(
+ callback, executor, null /* extra */);
+ }
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/model/DeleteResult.java b/libartservice/service/java/com/android/server/art/model/DeleteResult.java
new file mode 100644
index 0000000..e8e1520
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/DeleteResult.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.Immutable;
+
+import com.google.auto.value.AutoValue;
+
+/** @hide */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+@AutoValue
+public abstract class DeleteResult {
+ /** @hide */
+ protected DeleteResult() {}
+
+ /** @hide */
+ public static @NonNull DeleteResult create(long freedBytes) {
+ return new AutoValue_DeleteResult(freedBytes);
+ }
+
+ /** The amount of the disk space freed by the deletion, in bytes. */
+ public abstract long getFreedBytes();
+}
diff --git a/libartservice/service/java/com/android/server/art/model/DetailedDexInfo.java b/libartservice/service/java/com/android/server/art/model/DetailedDexInfo.java
new file mode 100644
index 0000000..932813f
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/DetailedDexInfo.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * Detailed information about a dex file.
+ *
+ * @hide
+ */
+@Immutable
+public interface DetailedDexInfo {
+ /** The path to the dex file. */
+ @NonNull String dexPath();
+
+ /**
+ * A string describing the structure of the class loader that the dex file is loaded with, or
+ * null if the class loader context is invalid.
+ */
+ @Nullable String classLoaderContext();
+}
diff --git a/libartservice/service/java/com/android/server/art/model/DexContainerFileUseInfo.java b/libartservice/service/java/com/android/server/art/model/DexContainerFileUseInfo.java
new file mode 100644
index 0000000..1f4c013
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/DexContainerFileUseInfo.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 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 com.android.server.art.model;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.Immutable;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The information about the use of a dex container file.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+@AutoValue
+public abstract class DexContainerFileUseInfo {
+ /** @hide */
+ protected DexContainerFileUseInfo() {}
+
+ /** @hide */
+ public static @NonNull DexContainerFileUseInfo create(@NonNull String dexContainerFile,
+ @NonNull UserHandle userHandle, @NonNull Set<String> loadingPackages) {
+ return new AutoValue_DexContainerFileUseInfo(dexContainerFile, userHandle, loadingPackages);
+ }
+
+ /** The absolute path to the dex container file. */
+ public abstract @NonNull String getDexContainerFile();
+
+ /** The {@link UserHandle} that represents the human user who loads the dex file. */
+ public abstract @NonNull UserHandle getUserHandle();
+
+ /** The names of packages that load the dex file. Guaranteed to be non-empty. */
+ public abstract @NonNull Set<String> getLoadingPackages();
+}
diff --git a/libartservice/service/java/com/android/server/art/model/DexoptParams.java b/libartservice/service/java/com/android/server/art/model/DexoptParams.java
new file mode 100644
index 0000000..4dc9471
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/DexoptParams.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import static com.android.server.art.model.ArtFlags.DexoptFlags;
+import static com.android.server.art.model.ArtFlags.PriorityClassApi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.Immutable;
+import com.android.server.art.ReasonMapping;
+import com.android.server.art.Utils;
+
+/** @hide */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+public class DexoptParams {
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ public static final class Builder {
+ private DexoptParams mParams = new DexoptParams();
+
+ /**
+ * Creates a builder.
+ *
+ * Uses default flags ({@link ArtFlags#defaultDexoptFlags()}).
+ *
+ * @param reason Compilation reason. Can be a string defined in {@link ReasonMapping} or a
+ * custom string. If the value is a string defined in {@link ReasonMapping}, it
+ * determines the compiler filter and/or the priority class, if those values are not
+ * explicitly set. If the value is a custom string, the priority class and the
+ * compiler filter must be explicitly set.
+ */
+ public Builder(@NonNull String reason) {
+ this(reason, ArtFlags.defaultDexoptFlags(reason));
+ }
+
+ /**
+ * Same as above, but allows to specify flags.
+ */
+ public Builder(@NonNull String reason, @DexoptFlags int flags) {
+ mParams.mReason = reason;
+ setFlags(flags);
+ }
+
+ /** Replaces all flags with the given value. */
+ @NonNull
+ public Builder setFlags(@DexoptFlags int value) {
+ mParams.mFlags = value;
+ return this;
+ }
+
+ /** Replaces the flags specified by the mask with the given value. */
+ @NonNull
+ public Builder setFlags(@DexoptFlags int value, @DexoptFlags int mask) {
+ mParams.mFlags = (mParams.mFlags & ~mask) | (value & mask);
+ return this;
+ }
+
+ /**
+ * The target compiler filter, passed as the {@code --compiler-filer} option to dex2oat.
+ * Supported values are listed in
+ * https://source.android.com/docs/core/dalvik/configure#compilation_options.
+ *
+ * Note that the compiler filter might be adjusted before the execution based on factors
+ * like whether the profile is available or whether the app is used by other apps. If not
+ * set, the default compiler filter for the given reason will be used.
+ */
+ @NonNull
+ public Builder setCompilerFilter(@NonNull String value) {
+ mParams.mCompilerFilter = value;
+ return this;
+ }
+
+ /**
+ * The priority of the operation. If not set, the default priority class for the given
+ * reason will be used.
+ *
+ * @see PriorityClassApi
+ */
+ @NonNull
+ public Builder setPriorityClass(@PriorityClassApi int value) {
+ mParams.mPriorityClass = value;
+ return this;
+ }
+
+ /**
+ * The name of the split to dexopt, or null for the base split. This option is only
+ * available when {@link ArtFlags#FLAG_FOR_SINGLE_SPLIT} is set.
+ */
+ @NonNull
+ public Builder setSplitName(@Nullable String value) {
+ mParams.mSplitName = value;
+ return this;
+ }
+
+ /**
+ * Returns the built object.
+ *
+ * @throws IllegalArgumentException if the built options would be invalid
+ */
+ @NonNull
+ public DexoptParams build() {
+ if (mParams.mReason.isEmpty()) {
+ throw new IllegalArgumentException("Reason must not be empty");
+ }
+
+ if (mParams.mCompilerFilter.isEmpty()) {
+ mParams.mCompilerFilter = ReasonMapping.getCompilerFilterForReason(mParams.mReason);
+ } else if (!Utils.isValidArtServiceCompilerFilter(mParams.mCompilerFilter)) {
+ throw new IllegalArgumentException(
+ "Invalid compiler filter '" + mParams.mCompilerFilter + "'");
+ }
+
+ if (mParams.mPriorityClass == ArtFlags.PRIORITY_NONE) {
+ mParams.mPriorityClass = ReasonMapping.getPriorityClassForReason(mParams.mReason);
+ } else if (mParams.mPriorityClass < 0 || mParams.mPriorityClass > 100) {
+ throw new IllegalArgumentException("Invalid priority class "
+ + mParams.mPriorityClass + ". Must be between 0 and 100");
+ }
+
+ if ((mParams.mFlags & (ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX))
+ == 0) {
+ throw new IllegalArgumentException("Nothing to dexopt");
+ }
+
+ if ((mParams.mFlags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0
+ && (mParams.mFlags & ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES) != 0) {
+ throw new IllegalArgumentException(
+ "FLAG_SHOULD_INCLUDE_DEPENDENCIES must not set if FLAG_FOR_PRIMARY_DEX is "
+ + "not set.");
+ }
+
+ if ((mParams.mFlags & ArtFlags.FLAG_FOR_SINGLE_SPLIT) != 0) {
+ if ((mParams.mFlags & ArtFlags.FLAG_FOR_PRIMARY_DEX) == 0) {
+ throw new IllegalArgumentException(
+ "FLAG_FOR_PRIMARY_DEX must be set when FLAG_FOR_SINGLE_SPLIT is set");
+ }
+ if ((mParams.mFlags
+ & (ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES))
+ != 0) {
+ throw new IllegalArgumentException(
+ "FLAG_FOR_SECONDARY_DEX and FLAG_SHOULD_INCLUDE_DEPENDENCIES must "
+ + "not be set when FLAG_FOR_SINGLE_SPLIT is set");
+ }
+ } else {
+ if (mParams.mSplitName != null) {
+ throw new IllegalArgumentException(
+ "Split name must not be set when FLAG_FOR_SINGLE_SPLIT is not set");
+ }
+ }
+
+ return mParams;
+ }
+ }
+
+ /**
+ * A value indicating that dexopt shouldn't be run. This value is consumed by ART Services and
+ * is not propagated to dex2oat.
+ */
+ public static final String COMPILER_FILTER_NOOP = "skip";
+
+ private @DexoptFlags int mFlags = 0;
+ private @NonNull String mCompilerFilter = "";
+ private @PriorityClassApi int mPriorityClass = ArtFlags.PRIORITY_NONE;
+ private @NonNull String mReason = "";
+ private @Nullable String mSplitName = null;
+
+ private DexoptParams() {}
+
+ /** Returns all flags. */
+ public @DexoptFlags int getFlags() {
+ return mFlags;
+ }
+
+ /** The target compiler filter. */
+ public @NonNull String getCompilerFilter() {
+ return mCompilerFilter;
+ }
+
+ /** The priority class. */
+ public @PriorityClassApi int getPriorityClass() {
+ return mPriorityClass;
+ }
+
+ /**
+ * The compilation reason.
+ *
+ * DO NOT directly use the string value to determine the resource usage and the process
+ * priority. Use {@link #getPriorityClass}.
+ */
+ public @NonNull String getReason() {
+ return mReason;
+ }
+
+ /** The name of the split to dexopt, or null for the base split. */
+ public @Nullable String getSplitName() {
+ return mSplitName;
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/model/DexoptResult.java b/libartservice/service/java/com/android/server/art/model/DexoptResult.java
new file mode 100644
index 0000000..d8a8514
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/DexoptResult.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.Immutable;
+
+import com.google.auto.value.AutoValue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/** @hide */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+@AutoValue
+public abstract class DexoptResult {
+ // Possible values of {@link #DexoptResultStatus}.
+ // A larger number means a higher priority. If multiple dex container files are processed, the
+ // final status will be the one with the highest priority.
+ /** Dexopt is skipped because there is no need to do it. */
+ public static final int DEXOPT_SKIPPED = 10;
+ /** Dexopt is performed successfully. */
+ public static final int DEXOPT_PERFORMED = 20;
+ /** Dexopt is failed. */
+ public static final int DEXOPT_FAILED = 30;
+ /** Dexopt is cancelled. */
+ public static final int DEXOPT_CANCELLED = 40;
+
+ /** @hide */
+ // clang-format off
+ @IntDef(prefix = {"DEXOPT_"}, value = {
+ DEXOPT_SKIPPED,
+ DEXOPT_FAILED,
+ DEXOPT_PERFORMED,
+ DEXOPT_CANCELLED,
+ })
+ // clang-format on
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DexoptResultStatus {}
+
+ /** @hide */
+ protected DexoptResult() {}
+
+ /** @hide */
+ public static @NonNull DexoptResult create(@NonNull String requestedCompilerFilter,
+ @NonNull String reason, @NonNull List<PackageDexoptResult> packageDexoptResult) {
+ return new AutoValue_DexoptResult(requestedCompilerFilter, reason, packageDexoptResult);
+ }
+
+ /**
+ * The requested compiler filter. Note that the compiler filter might be adjusted before the
+ * execution based on factors like whether the profile is available or whether the app is
+ * used by other apps.
+ *
+ * @see DexoptParams.Builder#setCompilerFilter(String)
+ * @see DexContainerFileDexoptResult#getActualCompilerFilter()
+ */
+ public abstract @NonNull String getRequestedCompilerFilter();
+
+ /** The compilation reason. */
+ public abstract @NonNull String getReason();
+
+ /**
+ * The result of each individual package.
+ *
+ * If the request is to dexopt a single package without dexopting dependencies, the only
+ * element is the result of the requested package.
+ *
+ * If the request is to dexopt a single package with {@link
+ * ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES} set, the first element is the result of the
+ * requested package, and the rest are the results of the dependency packages.
+ *
+ * If the request is to dexopt multiple packages, the list contains the results of all the
+ * requested packages. The results of their dependency packages are also included if {@link
+ * ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES} is set.
+ *
+ * If the request is a batch dexopt operation that got cancelled, the list still has an entry
+ * for every package that was requested to be optimized.
+ */
+ public abstract @NonNull List<PackageDexoptResult> getPackageDexoptResults();
+
+ /** The final status. */
+ public @DexoptResultStatus int getFinalStatus() {
+ return getPackageDexoptResults()
+ .stream()
+ .mapToInt(result -> result.getStatus())
+ .max()
+ .orElse(DEXOPT_SKIPPED);
+ }
+
+ /**
+ * Describes the result of a package.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ @Immutable
+ @AutoValue
+ public static abstract class PackageDexoptResult {
+ /** @hide */
+ protected PackageDexoptResult() {}
+
+ /** @hide */
+ public static @NonNull PackageDexoptResult create(@NonNull String packageName,
+ @NonNull List<DexContainerFileDexoptResult> dexContainerFileDexoptResults,
+ boolean isCanceled) {
+ return new AutoValue_DexoptResult_PackageDexoptResult(
+ packageName, dexContainerFileDexoptResults, isCanceled);
+ }
+
+ /** The package name. */
+ public abstract @NonNull String getPackageName();
+
+ /**
+ * The results of dexopting dex container files. Note that there can be multiple entries
+ * for the same dex container file, but for different ABIs.
+ */
+ public abstract @NonNull List<DexContainerFileDexoptResult>
+ getDexContainerFileDexoptResults();
+
+ /** @hide */
+ public abstract boolean isCanceled();
+
+ /** The overall status of the package. */
+ public @DexoptResultStatus int getStatus() {
+ return isCanceled() ? DEXOPT_CANCELLED
+ : getDexContainerFileDexoptResults()
+ .stream()
+ .mapToInt(result -> result.getStatus())
+ .max()
+ .orElse(DEXOPT_SKIPPED);
+ }
+
+ /** True if the package has any artifacts updated by this operation. */
+ public boolean hasUpdatedArtifacts() {
+ return getDexContainerFileDexoptResults().stream().anyMatch(
+ result -> result.getStatus() == DEXOPT_PERFORMED);
+ }
+ }
+
+ /**
+ * Describes the result of dexopting a dex container file.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ @Immutable
+ @AutoValue
+ public static abstract class DexContainerFileDexoptResult {
+ /** @hide */
+ protected DexContainerFileDexoptResult() {}
+
+ /** @hide */
+ public static @NonNull DexContainerFileDexoptResult create(@NonNull String dexContainerFile,
+ boolean isPrimaryAbi, @NonNull String abi, @NonNull String compilerFilter,
+ @DexoptResultStatus int status, long dex2oatWallTimeMillis,
+ long dex2oatCpuTimeMillis, long sizeBytes, long sizeBeforeBytes,
+ boolean isSkippedDueToStorageLow) {
+ return new AutoValue_DexoptResult_DexContainerFileDexoptResult(dexContainerFile,
+ isPrimaryAbi, abi, compilerFilter, status, dex2oatWallTimeMillis,
+ dex2oatCpuTimeMillis, sizeBytes, sizeBeforeBytes, isSkippedDueToStorageLow);
+ }
+
+ /** The absolute path to the dex container file. */
+ public abstract @NonNull String getDexContainerFile();
+
+ /**
+ * If true, the dexopt is for the primary ABI of the package (the ABI that the
+ * application is launched with). Otherwise, the dexopt is for an ABI that other
+ * applications might be launched with when using this application's code.
+ */
+ public abstract boolean isPrimaryAbi();
+
+ /**
+ * Returns the ABI that the dexopt is for. Possible values are documented at
+ * https://developer.android.com/ndk/guides/abis#sa.
+ */
+ public abstract @NonNull String getAbi();
+
+ /**
+ * The actual compiler filter.
+ *
+ * @see DexoptParams.Builder#setCompilerFilter(String)
+ */
+ public abstract @NonNull String getActualCompilerFilter();
+
+ /** The status of dexopting this dex container file. */
+ public abstract @DexoptResultStatus int getStatus();
+
+ /**
+ * The wall time of the dex2oat invocation, in milliseconds, if dex2oat succeeded or was
+ * cancelled. Returns 0 if dex2oat failed or was not run, or if failed to get the value.
+ */
+ public abstract @DurationMillisLong long getDex2oatWallTimeMillis();
+
+ /**
+ * The CPU time of the dex2oat invocation, in milliseconds, if dex2oat succeeded or was
+ * cancelled. Returns 0 if dex2oat failed or was not run, or if failed to get the value.
+ */
+ public abstract @DurationMillisLong long getDex2oatCpuTimeMillis();
+
+ /**
+ * The total size, in bytes, of the dexopt artifacts. Returns 0 if {@link #getStatus()}
+ * is not {@link #DEXOPT_PERFORMED}.
+ */
+ public abstract long getSizeBytes();
+
+ /**
+ * The total size, in bytes, of the previous dexopt artifacts that has been replaced.
+ * Returns 0 if there were no previous dexopt artifacts or {@link #getStatus()} is not
+ * {@link #DEXOPT_PERFORMED}.
+ */
+ public abstract long getSizeBeforeBytes();
+
+ /** @hide */
+ public abstract boolean isSkippedDueToStorageLow();
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/model/DexoptStatus.java b/libartservice/service/java/com/android/server/art/model/DexoptStatus.java
new file mode 100644
index 0000000..40130ea
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/DexoptStatus.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.Immutable;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.List;
+
+/**
+ * Describes the dexopt status of a package.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+@AutoValue
+public abstract class DexoptStatus {
+ /** @hide */
+ protected DexoptStatus() {}
+
+ /** @hide */
+ public static @NonNull DexoptStatus create(
+ @NonNull List<DexContainerFileDexoptStatus> dexContainerFileDexoptStatuses) {
+ return new AutoValue_DexoptStatus(dexContainerFileDexoptStatuses);
+ }
+
+ /**
+ * The statuses of the dex container file dexopts. Note that there can be multiple entries
+ * for the same dex container file, but for different ABIs.
+ */
+ @NonNull public abstract List<DexContainerFileDexoptStatus> getDexContainerFileDexoptStatuses();
+
+ /**
+ * Describes the dexopt status of a dex container file.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ @Immutable
+ @AutoValue
+ public abstract static class DexContainerFileDexoptStatus {
+ /** @hide */
+ protected DexContainerFileDexoptStatus() {}
+
+ /** @hide */
+ public static @NonNull DexContainerFileDexoptStatus create(@NonNull String dexContainerFile,
+ boolean isPrimaryDex, boolean isPrimaryAbi, @NonNull String abi,
+ @NonNull String compilerFilter, @NonNull String compilationReason,
+ @NonNull String locationDebugString) {
+ return new AutoValue_DexoptStatus_DexContainerFileDexoptStatus(dexContainerFile,
+ isPrimaryDex, isPrimaryAbi, abi, compilerFilter, compilationReason,
+ locationDebugString);
+ }
+
+ /** The absolute path to the dex container file. */
+ public abstract @NonNull String getDexContainerFile();
+
+ /**
+ * If true, the dex container file is a primary dex (the base APK or a split APK).
+ * Otherwise, it's a secondary dex (a APK or a JAR that the package sideloaded into its data
+ * directory).
+ */
+ public abstract boolean isPrimaryDex();
+
+ /**
+ * If true, the dexopt is for the primary ABI of the package (the ABI that the
+ * application is launched with). Otherwise, the dexopt is for an ABI that other
+ * applications might be launched with when using this application's code.
+ */
+ public abstract boolean isPrimaryAbi();
+
+ /**
+ * Returns the ABI that the dexopt is for. Possible values are documented at
+ * https://developer.android.com/ndk/guides/abis#sa.
+ */
+ public abstract @NonNull String getAbi();
+
+ /**
+ * A human-readable string that describes the compiler filter.
+ *
+ * Possible values are:
+ * <ul>
+ * <li>A valid value of the {@code --compiler-filer} option passed to {@code dex2oat}, if
+ * the dexopt artifacts are valid. See
+ * https://source.android.com/docs/core/dalvik/configure#compilation_options.
+ * <li>{@code "run-from-apk"}, if the dexopt artifacts do not exist.
+ * <li>{@code "run-from-apk-fallback"}, if the dexopt artifacts exist but are invalid
+ * because the dex container file has changed.
+ * <li>{@code "error"}, if an unexpected error occurs.
+ * </ul>
+ */
+ public abstract @NonNull String getCompilerFilter();
+
+ /**
+ * A string that describes the compilation reason.
+ *
+ * Possible values are:
+ * <ul>
+ * <li>The compilation reason, in text format, passed to {@code dex2oat}.
+ * <li>{@code "unknown"}: if the reason is empty or the dexopt artifacts do not exist.
+ * <li>{@code "error"}: if an unexpected error occurs.
+ * </ul>
+ *
+ * Note that this value can differ from the requested compilation reason passed to {@link
+ * DexoptParams.Builder}. Specifically, if the requested reason is for app install (e.g.,
+ * "install"), and a DM file is passed to {@code dex2oat}, a "-dm" suffix will be appended
+ * to the actual reason (e.g., "install-dm"). Other compilation reasons remain unchanged
+ * even if a DM file is passed to {@code dex2oat}.
+ *
+ * Also note that the "-dm" suffix does <b>not</b> imply anything in the DM file being used
+ * by {@code dex2oat}. The compilation reason can still be "install-dm" even if {@code
+ * dex2oat} left all contents of the DM file unused or an empty DM file is passed to
+ * {@code dex2oat}.
+ */
+ public abstract @NonNull String getCompilationReason();
+
+ /**
+ * A human-readable string that describes the location of the dexopt artifacts.
+ *
+ * Note that this string is for debugging purposes only. There is no stability guarantees
+ * for the format of the string. DO NOT use it programmatically.
+ */
+ public abstract @NonNull String getLocationDebugString();
+ }
+}
diff --git a/libartservice/service/java/com/android/server/art/model/OperationProgress.java b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
new file mode 100644
index 0000000..a47a556
--- /dev/null
+++ b/libartservice/service/java/com/android/server/art/model/OperationProgress.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.Immutable;
+
+import com.google.auto.value.AutoValue;
+
+/** @hide */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+@AutoValue
+public abstract class OperationProgress {
+ /** @hide */
+ protected OperationProgress() {}
+
+ /** @hide */
+ public static @NonNull OperationProgress create(int current, int total) {
+ return new AutoValue_OperationProgress(current, total);
+ }
+
+ /** The overall progress, in the range of [0, 100]. */
+ public int getPercentage() {
+ return 100 * getCurrent() / getTotal();
+ }
+
+ /**
+ * The number of processed items. Can be 0, which means the operation was just started.
+ *
+ * Currently, this is the number of packages, for which dexopt has been done, regardless
+ * of the results (performed, failed, skipped, etc.).
+ *
+ * @hide
+ */
+ public abstract int getCurrent();
+
+ /**
+ * The total number of items. Stays constant during the operation.
+ *
+ * Currently, this is the total number of packages to dexopt.
+ *
+ * @hide
+ */
+ public abstract int getTotal();
+}
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
new file mode 100644
index 0000000..54a6a77
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2021 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 com.android.server.art;
+
+import static android.os.ParcelFileDescriptor.AutoCloseInputStream;
+
+import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo;
+import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
+import static com.android.server.art.testing.TestingUtils.deepEq;
+import static com.android.server.art.testing.TestingUtils.inAnyOrder;
+import static com.android.server.art.testing.TestingUtils.inAnyOrderDeepEquals;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.apphibernation.AppHibernationManager;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.modules.utils.pm.PackageStateModulesUtils;
+import com.android.server.art.model.Config;
+import com.android.server.art.model.DeleteResult;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.model.DexoptStatus;
+import com.android.server.art.testing.StaticMockitoRule;
+import com.android.server.art.testing.TestingUtils;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageUserState;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.mockito.Mock;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(Parameterized.class)
+public class ArtManagerLocalTest {
+ private static final String PKG_NAME = "com.example.foo";
+ private static final String PKG_NAME_SYS_UI = "com.android.systemui";
+ private static final String PKG_NAME_HIBERNATING = "com.example.hibernating";
+ private static final int INACTIVE_DAYS = 1;
+ private static final long CURRENT_TIME_MS = 10000000000l;
+ private static final long RECENT_TIME_MS =
+ CURRENT_TIME_MS - TimeUnit.DAYS.toMillis(INACTIVE_DAYS) + 1;
+ private static final long NOT_RECENT_TIME_MS =
+ CURRENT_TIME_MS - TimeUnit.DAYS.toMillis(INACTIVE_DAYS) - 1;
+
+ @Rule
+ public StaticMockitoRule mockitoRule = new StaticMockitoRule(
+ SystemProperties.class, Constants.class, PackageStateModulesUtils.class);
+
+ @Mock private ArtManagerLocal.Injector mInjector;
+ @Mock private PackageManagerLocal mPackageManagerLocal;
+ @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
+ @Mock private IArtd mArtd;
+ @Mock private DexoptHelper mDexoptHelper;
+ @Mock private AppHibernationManager mAppHibernationManager;
+ @Mock private UserManager mUserManager;
+ @Mock private DexUseManagerLocal mDexUseManager;
+ @Mock private StorageManager mStorageManager;
+ private PackageState mPkgState;
+ private AndroidPackage mPkg;
+ private Config mConfig;
+
+ // True if the primary dex'es are in a readonly partition.
+ @Parameter(0) public boolean mIsInReadonlyPartition;
+
+ private ArtManagerLocal mArtManagerLocal;
+
+ @Parameters(name = "isInReadonlyPartition={0}")
+ public static Iterable<? extends Object> data() {
+ return List.of(false, true);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mConfig = new Config();
+
+ // Use `lenient()` to suppress `UnnecessaryStubbingException` thrown by the strict stubs.
+ // These are the default test setups. They may or may not be used depending on the code path
+ // that each test case examines.
+ lenient().when(mInjector.getPackageManagerLocal()).thenReturn(mPackageManagerLocal);
+ lenient().when(mInjector.getArtd()).thenReturn(mArtd);
+ lenient().when(mInjector.getDexoptHelper()).thenReturn(mDexoptHelper);
+ lenient().when(mInjector.getConfig()).thenReturn(mConfig);
+ lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAppHibernationManager);
+ lenient().when(mInjector.getUserManager()).thenReturn(mUserManager);
+ lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(false);
+ lenient().when(mInjector.isSystemUiPackage(PKG_NAME_SYS_UI)).thenReturn(true);
+ lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager);
+ lenient().when(mInjector.getCurrentTimeMillis()).thenReturn(CURRENT_TIME_MS);
+ lenient().when(mInjector.getStorageManager()).thenReturn(mStorageManager);
+
+ lenient().when(SystemProperties.get(eq("pm.dexopt.install"))).thenReturn("speed-profile");
+ lenient().when(SystemProperties.get(eq("pm.dexopt.bg-dexopt"))).thenReturn("speed-profile");
+ lenient().when(SystemProperties.get(eq("pm.dexopt.first-boot"))).thenReturn("verify");
+ lenient()
+ .when(SystemProperties.get(eq("pm.dexopt.boot-after-mainline-update")))
+ .thenReturn("verify");
+ lenient().when(SystemProperties.get(eq("pm.dexopt.inactive"))).thenReturn("verify");
+ lenient()
+ .when(SystemProperties.getInt(eq("pm.dexopt.bg-dexopt.concurrency"), anyInt()))
+ .thenReturn(3);
+ lenient()
+ .when(SystemProperties.getInt(
+ eq("pm.dexopt.boot-after-mainline-update.concurrency"), anyInt()))
+ .thenReturn(3);
+ lenient()
+ .when(SystemProperties.getInt(eq("pm.dexopt.inactive.concurrency"), anyInt()))
+ .thenReturn(3);
+ lenient()
+ .when(SystemProperties.getInt(
+ eq("pm.dexopt.downgrade_after_inactive_days"), anyInt()))
+ .thenReturn(INACTIVE_DAYS);
+
+ // No ISA translation.
+ lenient()
+ .when(SystemProperties.get(argThat(arg -> arg.startsWith("ro.dalvik.vm.isa."))))
+ .thenReturn("");
+
+ lenient().when(Constants.getPreferredAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative64BitAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative32BitAbi()).thenReturn("armeabi-v7a");
+
+ lenient().when(mAppHibernationManager.isHibernatingGlobally(any())).thenReturn(false);
+ lenient().when(mAppHibernationManager.isOatArtifactDeletionEnabled()).thenReturn(true);
+
+ lenient()
+ .when(mUserManager.getUserHandles(anyBoolean()))
+ .thenReturn(List.of(UserHandle.of(0), UserHandle.of(1)));
+
+ // All packages are by default recently used.
+ lenient().when(mDexUseManager.getPackageLastUsedAtMs(any())).thenReturn(RECENT_TIME_MS);
+ List<? extends SecondaryDexInfo> secondaryDexInfo = createSecondaryDexInfo();
+ lenient().doReturn(secondaryDexInfo).when(mDexUseManager).getSecondaryDexInfo(eq(PKG_NAME));
+
+ simulateStorageNotLow();
+
+ lenient().when(mPackageManagerLocal.withFilteredSnapshot()).thenReturn(mSnapshot);
+ List<PackageState> pkgStates = createPackageStates();
+ for (PackageState pkgState : pkgStates) {
+ lenient()
+ .when(mSnapshot.getPackageState(pkgState.getPackageName()))
+ .thenReturn(pkgState);
+ }
+ var packageStateMap = pkgStates.stream().collect(
+ Collectors.toMap(PackageState::getPackageName, it -> it));
+ lenient().when(mSnapshot.getPackageStates()).thenReturn(packageStateMap);
+ mPkgState = mSnapshot.getPackageState(PKG_NAME);
+ mPkg = mPkgState.getAndroidPackage();
+
+ mArtManagerLocal = new ArtManagerLocal(mInjector);
+ }
+
+ @Test
+ public void testdeleteDexoptArtifacts() throws Exception {
+ when(mArtd.deleteArtifacts(any())).thenReturn(1l);
+
+ DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+ assertThat(result.getFreedBytes()).isEqualTo(5);
+
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/base.apk", "arm64", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/base.apk", "arm", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/split_0.apk", "arm64", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/split_0.apk", "arm", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */)));
+ verifyNoMoreInteractions(mArtd);
+ }
+
+ @Test
+ public void testdeleteDexoptArtifactsTranslatedIsas() throws Exception {
+ lenient().when(SystemProperties.get("ro.dalvik.vm.isa.arm64")).thenReturn("x86_64");
+ lenient().when(SystemProperties.get("ro.dalvik.vm.isa.arm")).thenReturn("x86");
+ lenient().when(Constants.getPreferredAbi()).thenReturn("x86_64");
+ lenient().when(Constants.getNative64BitAbi()).thenReturn("x86_64");
+ lenient().when(Constants.getNative32BitAbi()).thenReturn("x86");
+
+ when(mArtd.deleteArtifacts(any())).thenReturn(1l);
+
+ DeleteResult result = mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+ assertThat(result.getFreedBytes()).isEqualTo(5);
+
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/base.apk", "x86_64", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/base.apk", "x86", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/split_0.apk", "x86_64", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/split_0.apk", "x86", mIsInReadonlyPartition)));
+ // We assume that the ISA got from `DexUseManagerLocal` is already the translated one.
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */)));
+ verifyNoMoreInteractions(mArtd);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testdeleteDexoptArtifactsPackageNotFound() throws Exception {
+ when(mSnapshot.getPackageState(anyString())).thenReturn(null);
+
+ mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testdeleteDexoptArtifactsNoPackage() throws Exception {
+ when(mPkgState.getAndroidPackage()).thenReturn(null);
+
+ mArtManagerLocal.deleteDexoptArtifacts(mSnapshot, PKG_NAME);
+ }
+
+ @Test
+ public void testGetDexoptStatus() throws Exception {
+ doReturn(createGetDexoptStatusResult(
+ "speed", "compilation-reason-0", "location-debug-string-0"))
+ .when(mArtd)
+ .getDexoptStatus("/data/app/foo/base.apk", "arm64", "PCL[]");
+ doReturn(createGetDexoptStatusResult(
+ "speed-profile", "compilation-reason-1", "location-debug-string-1"))
+ .when(mArtd)
+ .getDexoptStatus("/data/app/foo/base.apk", "arm", "PCL[]");
+ doReturn(createGetDexoptStatusResult(
+ "verify", "compilation-reason-2", "location-debug-string-2"))
+ .when(mArtd)
+ .getDexoptStatus("/data/app/foo/split_0.apk", "arm64", "PCL[base.apk]");
+ doReturn(createGetDexoptStatusResult(
+ "extract", "compilation-reason-3", "location-debug-string-3"))
+ .when(mArtd)
+ .getDexoptStatus("/data/app/foo/split_0.apk", "arm", "PCL[base.apk]");
+ doReturn(createGetDexoptStatusResult("run-from-apk", "unknown", "unknown"))
+ .when(mArtd)
+ .getDexoptStatus("/data/user/0/foo/1.apk", "arm64", "CLC");
+
+ DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+
+ assertThat(result.getDexContainerFileDexoptStatuses())
+ .comparingElementsUsing(TestingUtils.<DexContainerFileDexoptStatus>deepEquality())
+ .containsExactly(
+ DexContainerFileDexoptStatus.create("/data/app/foo/base.apk",
+ true /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a",
+ "speed", "compilation-reason-0", "location-debug-string-0"),
+ DexContainerFileDexoptStatus.create("/data/app/foo/base.apk",
+ true /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a",
+ "speed-profile", "compilation-reason-1", "location-debug-string-1"),
+ DexContainerFileDexoptStatus.create("/data/app/foo/split_0.apk",
+ true /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a",
+ "verify", "compilation-reason-2", "location-debug-string-2"),
+ DexContainerFileDexoptStatus.create("/data/app/foo/split_0.apk",
+ true /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a",
+ "extract", "compilation-reason-3", "location-debug-string-3"),
+ DexContainerFileDexoptStatus.create("/data/user/0/foo/1.apk",
+ false /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a",
+ "run-from-apk", "unknown", "unknown"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetDexoptStatusPackageNotFound() throws Exception {
+ when(mSnapshot.getPackageState(anyString())).thenReturn(null);
+
+ mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetDexoptStatusNoPackage() throws Exception {
+ when(mPkgState.getAndroidPackage()).thenReturn(null);
+
+ mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+ }
+
+ @Test
+ public void testGetDexoptStatusNonFatalError() throws Exception {
+ when(mArtd.getDexoptStatus(any(), any(), any()))
+ .thenThrow(new ServiceSpecificException(1 /* errorCode */, "some error message"));
+
+ DexoptStatus result = mArtManagerLocal.getDexoptStatus(mSnapshot, PKG_NAME);
+
+ List<DexContainerFileDexoptStatus> statuses = result.getDexContainerFileDexoptStatuses();
+ assertThat(statuses.size()).isEqualTo(5);
+
+ for (DexContainerFileDexoptStatus status : statuses) {
+ assertThat(status.getCompilerFilter()).isEqualTo("error");
+ assertThat(status.getCompilationReason()).isEqualTo("error");
+ assertThat(status.getLocationDebugString()).isEqualTo("some error message");
+ }
+ }
+
+ @Test
+ public void testClearAppProfiles() throws Exception {
+ mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME);
+
+ verify(mArtd).deleteProfile(
+ deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary")));
+ verify(mArtd).deleteProfile(deepEq(
+ AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary")));
+ verify(mArtd).deleteProfile(deepEq(
+ AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME, "primary")));
+
+ verify(mArtd).deleteProfile(
+ deepEq(AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk")));
+ verify(mArtd).deleteProfile(
+ deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk")));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testClearAppProfilesPackageNotFound() throws Exception {
+ when(mSnapshot.getPackageState(anyString())).thenReturn(null);
+
+ mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testClearAppProfilesNoPackage() throws Exception {
+ when(mPkgState.getAndroidPackage()).thenReturn(null);
+
+ mArtManagerLocal.clearAppProfiles(mSnapshot, PKG_NAME);
+ }
+
+ @Test
+ public void testDexoptPackage() throws Exception {
+ var params = new DexoptParams.Builder("install").build();
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+
+ when(mDexoptHelper.dexopt(any(), deepEq(List.of(PKG_NAME)), same(params),
+ same(cancellationSignal), any()))
+ .thenReturn(result);
+
+ assertThat(mArtManagerLocal.dexoptPackage(mSnapshot, PKG_NAME, params, cancellationSignal))
+ .isSameInstanceAs(result);
+ }
+
+ @Test
+ public void testResetDexoptStatus() throws Exception {
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+
+ when(mDexoptHelper.dexopt(
+ any(), deepEq(List.of(PKG_NAME)), any(), same(cancellationSignal), any()))
+ .thenReturn(result);
+
+ assertThat(mArtManagerLocal.resetDexoptStatus(mSnapshot, PKG_NAME, cancellationSignal))
+ .isSameInstanceAs(result);
+
+ verify(mArtd).deleteProfile(
+ deepEq(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary")));
+ verify(mArtd).deleteProfile(deepEq(
+ AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary")));
+ verify(mArtd).deleteProfile(deepEq(
+ AidlUtils.buildProfilePathForPrimaryCur(1 /* userId */, PKG_NAME, "primary")));
+
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/base.apk", "arm64", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/base.apk", "arm", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/split_0.apk", "arm64", mIsInReadonlyPartition)));
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/app/foo/split_0.apk", "arm", mIsInReadonlyPartition)));
+
+ verify(mArtd).deleteProfile(
+ deepEq(AidlUtils.buildProfilePathForSecondaryRef("/data/user/0/foo/1.apk")));
+ verify(mArtd).deleteProfile(
+ deepEq(AidlUtils.buildProfilePathForSecondaryCur("/data/user/0/foo/1.apk")));
+
+ verify(mArtd).deleteArtifacts(deepEq(AidlUtils.buildArtifactsPath(
+ "/data/user/0/foo/1.apk", "arm64", false /* isInDalvikCache */)));
+ }
+
+ @Test
+ public void testDexoptPackages() throws Exception {
+ var dexoptResult = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+ when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME_SYS_UI)).thenReturn(CURRENT_TIME_MS);
+ simulateStorageLow();
+
+ // It should use the default package list and params. The list is sorted by last active
+ // time in descending order.
+ doReturn(dexoptResult)
+ .when(mDexoptHelper)
+ .dexopt(any(), deepEq(List.of(PKG_NAME_SYS_UI, PKG_NAME)),
+ argThat(params -> params.getReason().equals("bg-dexopt")),
+ same(cancellationSignal), any(), any(), any());
+
+ assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */))
+ .isSameInstanceAs(dexoptResult);
+
+ // Nothing to downgrade.
+ verify(mDexoptHelper, never())
+ .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")),
+ any(), any(), any(), any());
+ }
+
+ @Test
+ public void testDexoptPackagesRecentlyInstalled() throws Exception {
+ // The package is recently installed but hasn't been used.
+ PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+ when(userState.getFirstInstallTimeMillis()).thenReturn(RECENT_TIME_MS);
+ when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(0l);
+ simulateStorageLow();
+
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+
+ // PKG_NAME should be dexopted.
+ doReturn(result)
+ .when(mDexoptHelper)
+ .dexopt(any(), inAnyOrder(PKG_NAME, PKG_NAME_SYS_UI),
+ argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
+ any(), any());
+
+ mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */);
+
+ // PKG_NAME should not be downgraded.
+ verify(mDexoptHelper, never())
+ .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")),
+ any(), any(), any(), any());
+ }
+
+ @Test
+ public void testDexoptPackagesInactive() throws Exception {
+ // PKG_NAME is neither recently installed nor recently used.
+ PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+ when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
+ when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(NOT_RECENT_TIME_MS);
+ simulateStorageLow();
+
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+
+ // PKG_NAME should not be dexopted.
+ doReturn(result)
+ .when(mDexoptHelper)
+ .dexopt(any(), deepEq(List.of(PKG_NAME_SYS_UI)),
+ argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
+ any(), any());
+
+ // PKG_NAME should be downgraded.
+ doReturn(result)
+ .when(mDexoptHelper)
+ .dexopt(any(), deepEq(List.of(PKG_NAME)),
+ argThat(params -> params.getReason().equals("inactive")), any(), any(),
+ any(), any());
+
+ mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */);
+ }
+
+ @Test
+ public void testDexoptPackagesInactiveStorageNotLow() throws Exception {
+ // PKG_NAME is neither recently installed nor recently used.
+ PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+ when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
+ when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(NOT_RECENT_TIME_MS);
+
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+
+ // PKG_NAME should not be dexopted.
+ doReturn(result)
+ .when(mDexoptHelper)
+ .dexopt(any(), deepEq(List.of(PKG_NAME_SYS_UI)),
+ argThat(params -> params.getReason().equals("bg-dexopt")), any(), any(),
+ any(), any());
+
+ mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */);
+
+ // PKG_NAME should not be downgraded because the storage is not low.
+ verify(mDexoptHelper, never())
+ .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")),
+ any(), any(), any(), any());
+ }
+
+ @Test
+ public void testDexoptPackagesBootAfterMainlineUpdate() throws Exception {
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+ simulateStorageLow();
+
+ // It should only dexopt system UI.
+ when(mDexoptHelper.dexopt(
+ any(), deepEq(List.of(PKG_NAME_SYS_UI)), any(), any(), any(), any(), any()))
+ .thenReturn(result);
+
+ assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "boot-after-mainline-update",
+ cancellationSignal, null /* processCallbackExecutor */,
+ null /* processCallback */))
+ .isSameInstanceAs(result);
+
+ // It should never downgrade apps, even if the storage is low.
+ verify(mDexoptHelper, never())
+ .dexopt(any(), any(), argThat(params -> params.getReason().equals("inactive")),
+ any(), any(), any(), any());
+ }
+
+ @Test
+ public void testDexoptPackagesOverride() throws Exception {
+ // PKG_NAME is neither recently installed nor recently used.
+ PackageUserState userState = mPkgState.getStateForUser(UserHandle.of(1));
+ when(userState.getFirstInstallTimeMillis()).thenReturn(NOT_RECENT_TIME_MS);
+ when(mDexUseManager.getPackageLastUsedAtMs(PKG_NAME)).thenReturn(NOT_RECENT_TIME_MS);
+ simulateStorageLow();
+
+ var params = new DexoptParams.Builder("bg-dexopt").build();
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+
+ mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
+ (snapshot, reason, defaultPackages, builder, passedSignal) -> {
+ assertThat(reason).isEqualTo("bg-dexopt");
+ assertThat(defaultPackages).containsExactly(PKG_NAME_SYS_UI);
+ assertThat(passedSignal).isSameInstanceAs(cancellationSignal);
+ builder.setPackages(List.of(PKG_NAME)).setDexoptParams(params);
+ });
+
+ // It should use the overridden package list and params.
+ doReturn(result)
+ .when(mDexoptHelper)
+ .dexopt(any(), deepEq(List.of(PKG_NAME)), same(params), any(), any(), any(), any());
+
+ mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */);
+
+ // It should not downgrade PKG_NAME because it's in the overridden package list. It should
+ // not downgrade PKG_NAME_SYS_UI either because it's not an inactive package.
+ verify(mDexoptHelper, never())
+ .dexopt(any(), any(), argThat(params2 -> params2.getReason().equals("inactive")),
+ any(), any(), any(), any());
+ }
+
+ @Test
+ public void testDexoptPackagesOverrideCleared() throws Exception {
+ var params = new DexoptParams.Builder("bg-dexopt").build();
+ var result = mock(DexoptResult.class);
+ var cancellationSignal = new CancellationSignal();
+
+ mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
+ (snapshot, reason, defaultPackages, builder, passedSignal) -> {
+ builder.setPackages(List.of(PKG_NAME)).setDexoptParams(params);
+ });
+ mArtManagerLocal.clearBatchDexoptStartCallback();
+
+ // It should use the default package list and params.
+ when(mDexoptHelper.dexopt(any(), inAnyOrder(PKG_NAME, PKG_NAME_SYS_UI), not(same(params)),
+ same(cancellationSignal), any(), any(), any()))
+ .thenReturn(result);
+
+ assertThat(mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */))
+ .isSameInstanceAs(result);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testDexoptPackagesOverrideReasonChanged() throws Exception {
+ var params = new DexoptParams.Builder("first-boot").build();
+ var cancellationSignal = new CancellationSignal();
+
+ mArtManagerLocal.setBatchDexoptStartCallback(ForkJoinPool.commonPool(),
+ (snapshot, reason, defaultPackages, builder, passedSignal) -> {
+ builder.setDexoptParams(params);
+ });
+
+ mArtManagerLocal.dexoptPackages(mSnapshot, "bg-dexopt", cancellationSignal,
+ null /* processCallbackExecutor */, null /* processCallback */);
+ }
+
+ @Test
+ public void testSnapshotAppProfile() throws Exception {
+ var options = new MergeProfileOptions();
+ options.forceMerge = true;
+ options.forBootImage = false;
+
+ File tempFile = File.createTempFile("primary", ".prof");
+ tempFile.deleteOnExit();
+
+ when(mArtd.mergeProfiles(
+ deepEq(List.of(AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, PKG_NAME, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 1 /* userId */, PKG_NAME, "primary"))),
+ isNull(),
+ deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "primary",
+ Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)),
+ deepEq(List.of("/data/app/foo/base.apk")), deepEq(options)))
+ .thenAnswer(invocation -> {
+ try (var writer = new FileWriter(tempFile)) {
+ writer.write("snapshot");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ var output = invocation.<OutputProfile>getArgument(2);
+ output.profilePath.tmpPath = tempFile.getPath();
+ return true;
+ });
+
+ ParcelFileDescriptor fd =
+ mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+
+ verify(mArtd).deleteProfile(
+ argThat(profile -> profile.getTmpProfilePath().tmpPath.equals(tempFile.getPath())));
+
+ try (InputStream inputStream = new AutoCloseInputStream(fd)) {
+ String contents = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+ assertThat(contents).isEqualTo("snapshot");
+ }
+ }
+
+ @Test
+ public void testSnapshotAppProfileSplit() throws Exception {
+ when(mArtd.mergeProfiles(deepEq(List.of(AidlUtils.buildProfilePathForPrimaryRef(
+ PKG_NAME, "split_0.split"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, PKG_NAME, "split_0.split"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 1 /* userId */, PKG_NAME, "split_0.split"))),
+ isNull(),
+ deepEq(AidlUtils.buildOutputProfileForPrimary(PKG_NAME, "split_0.split",
+ Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)),
+ deepEq(List.of("/data/app/foo/split_0.apk")), any()))
+ .thenReturn(false);
+
+ mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, "split_0");
+ }
+
+ @Test
+ public void testSnapshotAppProfileEmpty() throws Exception {
+ when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false);
+
+ ParcelFileDescriptor fd =
+ mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+
+ verify(mArtd, never()).deleteProfile(any());
+
+ try (InputStream inputStream = new AutoCloseInputStream(fd)) {
+ assertThat(inputStream.readAllBytes()).isEmpty();
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSnapshotAppProfilePackageNotFound() throws Exception {
+ when(mSnapshot.getPackageState(anyString())).thenReturn(null);
+
+ mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSnapshotAppProfileNoPackage() throws Exception {
+ when(mPkgState.getAndroidPackage()).thenReturn(null);
+
+ mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, null /* splitName */);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSnapshotAppProfileSplitNotFound() throws Exception {
+ mArtManagerLocal.snapshotAppProfile(mSnapshot, PKG_NAME, "non-existent-split");
+ }
+
+ @Test
+ public void testDumpAppProfile() throws Exception {
+ var options = new MergeProfileOptions();
+ options.dumpOnly = true;
+
+ when(mArtd.mergeProfiles(any(), isNull(), any(), any(), deepEq(options)))
+ .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`.
+
+ ParcelFileDescriptor fd = mArtManagerLocal.dumpAppProfile(
+ mSnapshot, PKG_NAME, null /* splitName */, false /* dumpClassesAndMethods */);
+ }
+
+ @Test
+ public void testDumpAppProfileDumpClassesAndMethods() throws Exception {
+ var options = new MergeProfileOptions();
+ options.dumpClassesAndMethods = true;
+
+ when(mArtd.mergeProfiles(any(), isNull(), any(), any(), deepEq(options)))
+ .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`.
+
+ ParcelFileDescriptor fd = mArtManagerLocal.dumpAppProfile(
+ mSnapshot, PKG_NAME, null /* splitName */, true /* dumpClassesAndMethods */);
+ }
+
+ @Test
+ public void testSnapshotBootImageProfile() throws Exception {
+ // `lenient()` is required to allow mocking the same method multiple times.
+ lenient().when(Constants.getenv("BOOTCLASSPATH")).thenReturn("bcp0:bcp1");
+ lenient().when(Constants.getenv("SYSTEMSERVERCLASSPATH")).thenReturn("sscp0:sscp1");
+ lenient().when(Constants.getenv("STANDALONE_SYSTEMSERVER_JARS")).thenReturn("sssj0:sssj1");
+
+ var options = new MergeProfileOptions();
+ options.forceMerge = true;
+ options.forBootImage = true;
+
+ when(mArtd.mergeProfiles(
+ inAnyOrderDeepEquals(
+ AidlUtils.buildProfilePathForPrimaryRef("android", "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, "android", "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 1 /* userId */, "android", "primary"),
+ AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, PKG_NAME, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 1 /* userId */, PKG_NAME, "primary"),
+ AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "split_0.split"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, PKG_NAME, "split_0.split"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 1 /* userId */, PKG_NAME, "split_0.split"),
+ AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME_SYS_UI, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, PKG_NAME_SYS_UI, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 1 /* userId */, PKG_NAME_SYS_UI, "primary"),
+ AidlUtils.buildProfilePathForPrimaryRef(
+ PKG_NAME_HIBERNATING, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, PKG_NAME_HIBERNATING, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 1 /* userId */, PKG_NAME_HIBERNATING, "primary")),
+ isNull(),
+ deepEq(AidlUtils.buildOutputProfileForPrimary("android", "primary",
+ Process.SYSTEM_UID, Process.SYSTEM_UID, false /* isPublic */)),
+ deepEq(List.of("bcp0", "bcp1", "sscp0", "sscp1", "sssj0", "sssj1")),
+ deepEq(options)))
+ .thenReturn(false); // A non-empty merge is tested in `testSnapshotAppProfile`.
+
+ mArtManagerLocal.snapshotBootImageProfile(mSnapshot);
+ }
+
+ private AndroidPackage createPackage(boolean multiSplit) {
+ AndroidPackage pkg = mock(AndroidPackage.class);
+
+ var baseSplit = mock(AndroidPackageSplit.class);
+ lenient().when(baseSplit.getPath()).thenReturn("/data/app/foo/base.apk");
+ lenient().when(baseSplit.isHasCode()).thenReturn(true);
+
+ if (multiSplit) {
+ // split_0 has code while split_1 doesn't.
+ var split0 = mock(AndroidPackageSplit.class);
+ lenient().when(split0.getName()).thenReturn("split_0");
+ lenient().when(split0.getPath()).thenReturn("/data/app/foo/split_0.apk");
+ lenient().when(split0.isHasCode()).thenReturn(true);
+ var split1 = mock(AndroidPackageSplit.class);
+ lenient().when(split1.getName()).thenReturn("split_1");
+ lenient().when(split1.getPath()).thenReturn("/data/app/foo/split_1.apk");
+ lenient().when(split1.isHasCode()).thenReturn(false);
+
+ lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit, split0, split1));
+ } else {
+ lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit));
+ }
+
+ return pkg;
+ }
+
+ private PackageUserState createPackageUserState() {
+ PackageUserState pkgUserState = mock(PackageUserState.class);
+ lenient().when(pkgUserState.isInstalled()).thenReturn(true);
+ // All packages are by default pre-installed.
+ lenient().when(pkgUserState.getFirstInstallTimeMillis()).thenReturn(0l);
+ return pkgUserState;
+ }
+
+ private PackageState createPackageState(
+ String packageName, boolean isDexoptable, boolean multiSplit) {
+ PackageState pkgState = mock(PackageState.class);
+
+ lenient().when(pkgState.getPackageName()).thenReturn(packageName);
+ lenient().when(pkgState.getPrimaryCpuAbi()).thenReturn("arm64-v8a");
+ lenient().when(pkgState.getSecondaryCpuAbi()).thenReturn("armeabi-v7a");
+ lenient().when(pkgState.isSystem()).thenReturn(mIsInReadonlyPartition);
+ lenient().when(pkgState.isUpdatedSystemApp()).thenReturn(false);
+
+ AndroidPackage pkg = createPackage(multiSplit);
+ lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg);
+
+ PackageUserState pkgUserState0 = createPackageUserState();
+ lenient().when(pkgState.getStateForUser(UserHandle.of(0))).thenReturn(pkgUserState0);
+ PackageUserState pkgUserState1 = createPackageUserState();
+ lenient().when(pkgState.getStateForUser(UserHandle.of(1))).thenReturn(pkgUserState1);
+
+ lenient().when(PackageStateModulesUtils.isDexoptable(pkgState)).thenReturn(isDexoptable);
+
+ return pkgState;
+ }
+
+ private List<PackageState> createPackageStates() {
+ PackageState pkgState =
+ createPackageState(PKG_NAME, true /* isDexoptable */, true /* multiSplit */);
+
+ PackageState sysUiPkgState = createPackageState(
+ PKG_NAME_SYS_UI, true /* isDexoptable */, false /* multiSplit */);
+
+ // This should not be dexopted because it's hibernating. However, it should be included
+ // when snapshotting boot image profile.
+ PackageState pkgHibernatingState = createPackageState(
+ PKG_NAME_HIBERNATING, true /* isDexoptable */, false /* multiSplit */);
+ lenient()
+ .when(mAppHibernationManager.isHibernatingGlobally(PKG_NAME_HIBERNATING))
+ .thenReturn(true);
+
+ // This should not be dexopted because it's not dexoptable.
+ PackageState nonDexoptablePkgState = createPackageState(
+ "com.example.non-dexoptable", false /* isDexoptable */, false /* multiSplit */);
+
+ return List.of(pkgState, sysUiPkgState, pkgHibernatingState, nonDexoptablePkgState);
+ }
+
+ private GetDexoptStatusResult createGetDexoptStatusResult(
+ String compilerFilter, String compilationReason, String locationDebugString) {
+ var getDexoptStatusResult = new GetDexoptStatusResult();
+ getDexoptStatusResult.compilerFilter = compilerFilter;
+ getDexoptStatusResult.compilationReason = compilationReason;
+ getDexoptStatusResult.locationDebugString = locationDebugString;
+ return getDexoptStatusResult;
+ }
+
+ private List<? extends SecondaryDexInfo> createSecondaryDexInfo() throws Exception {
+ var dexInfo = mock(SecondaryDexInfo.class);
+ lenient().when(dexInfo.dexPath()).thenReturn("/data/user/0/foo/1.apk");
+ lenient().when(dexInfo.abiNames()).thenReturn(Set.of("arm64-v8a"));
+ lenient().when(dexInfo.classLoaderContext()).thenReturn("CLC");
+ return List.of(dexInfo);
+ }
+
+ private void simulateStorageLow() throws Exception {
+ lenient()
+ .when(mStorageManager.getAllocatableBytes(any()))
+ .thenReturn(ArtManagerLocal.DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES - 1);
+ }
+
+ private void simulateStorageNotLow() throws Exception {
+ lenient()
+ .when(mStorageManager.getAllocatableBytes(any()))
+ .thenReturn(ArtManagerLocal.DOWNGRADE_THRESHOLD_ABOVE_LOW_BYTES);
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java b/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
new file mode 100644
index 0000000..b6a0a81
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/BackgroundDexoptJobTest.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.model.Config.Callback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.os.CancellationSignal;
+import android.os.SystemProperties;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.art.BackgroundDexoptJob.CompletedResult;
+import com.android.server.art.BackgroundDexoptJob.FatalErrorResult;
+import com.android.server.art.BackgroundDexoptJob.Result;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.Config;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.testing.StaticMockitoRule;
+import com.android.server.pm.PackageManagerLocal;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BackgroundDexoptJobTest {
+ private static final long TIMEOUT_SEC = 1;
+
+ @Rule
+ public StaticMockitoRule mockitoRule =
+ new StaticMockitoRule(SystemProperties.class, BackgroundDexoptJobService.class);
+
+ @Mock private BackgroundDexoptJob.Injector mInjector;
+ @Mock private ArtManagerLocal mArtManagerLocal;
+ @Mock private PackageManagerLocal mPackageManagerLocal;
+ @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
+ @Mock private JobScheduler mJobScheduler;
+ @Mock private DexoptResult mDexoptResult;
+ @Mock private BackgroundDexoptJobService mJobService;
+ @Mock private JobParameters mJobParameters;
+ private Config mConfig;
+ private BackgroundDexoptJob mBackgroundDexoptJob;
+ private Semaphore mJobFinishedCalled = new Semaphore(0);
+
+ @Before
+ public void setUp() throws Exception {
+ lenient()
+ .when(SystemProperties.getBoolean(eq("pm.dexopt.disable_bg_dexopt"), anyBoolean()))
+ .thenReturn(false);
+
+ lenient().when(mPackageManagerLocal.withFilteredSnapshot()).thenReturn(mSnapshot);
+
+ mConfig = new Config();
+
+ lenient().when(mInjector.getArtManagerLocal()).thenReturn(mArtManagerLocal);
+ lenient().when(mInjector.getPackageManagerLocal()).thenReturn(mPackageManagerLocal);
+ lenient().when(mInjector.getConfig()).thenReturn(mConfig);
+ lenient().when(mInjector.getJobScheduler()).thenReturn(mJobScheduler);
+
+ mBackgroundDexoptJob = new BackgroundDexoptJob(mInjector);
+ lenient().when(BackgroundDexoptJobService.getJob()).thenReturn(mBackgroundDexoptJob);
+
+ lenient()
+ .doAnswer(invocation -> {
+ mJobFinishedCalled.release();
+ return null;
+ })
+ .when(mJobService)
+ .jobFinished(any(), anyBoolean());
+
+ lenient()
+ .when(mJobParameters.getStopReason())
+ .thenReturn(JobParameters.STOP_REASON_UNDEFINED);
+ }
+
+ @Test
+ public void testStart() {
+ when(mArtManagerLocal.dexoptPackages(
+ same(mSnapshot), eq(ReasonMapping.REASON_BG_DEXOPT), any(), any(), any()))
+ .thenReturn(mDexoptResult);
+
+ Result result = Utils.getFuture(mBackgroundDexoptJob.start());
+ assertThat(result).isInstanceOf(CompletedResult.class);
+ assertThat(((CompletedResult) result).dexoptResult()).isSameInstanceAs(mDexoptResult);
+ }
+
+ @Test
+ public void testStartAlreadyRunning() {
+ Semaphore dexoptDone = new Semaphore(0);
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenAnswer(invocation -> {
+ assertThat(dexoptDone.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
+ return mDexoptResult;
+ });
+
+ Future<Result> future1 = mBackgroundDexoptJob.start();
+ Future<Result> future2 = mBackgroundDexoptJob.start();
+ assertThat(future1).isSameInstanceAs(future2);
+
+ dexoptDone.release();
+ Utils.getFuture(future1);
+
+ verify(mArtManagerLocal, times(1)).dexoptPackages(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testStartAnother() {
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenReturn(mDexoptResult);
+
+ Future<Result> future1 = mBackgroundDexoptJob.start();
+ Utils.getFuture(future1);
+ Future<Result> future2 = mBackgroundDexoptJob.start();
+ Utils.getFuture(future2);
+ assertThat(future1).isNotSameInstanceAs(future2);
+ }
+
+ @Test
+ public void testStartFatalError() {
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenThrow(IllegalStateException.class);
+
+ Result result = Utils.getFuture(mBackgroundDexoptJob.start());
+ assertThat(result).isInstanceOf(FatalErrorResult.class);
+ }
+
+ @Test
+ public void testStartIgnoreDisabled() {
+ lenient()
+ .when(SystemProperties.getBoolean(eq("pm.dexopt.disable_bg_dexopt"), anyBoolean()))
+ .thenReturn(true);
+
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenReturn(mDexoptResult);
+
+ // The `start` method should ignore the system property. The system property is for
+ // `schedule`.
+ Utils.getFuture(mBackgroundDexoptJob.start());
+ }
+
+ @Test
+ public void testCancel() {
+ Semaphore dexoptCancelled = new Semaphore(0);
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenAnswer(invocation -> {
+ assertThat(dexoptCancelled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
+ var cancellationSignal = invocation.<CancellationSignal>getArgument(2);
+ assertThat(cancellationSignal.isCanceled()).isTrue();
+ return mDexoptResult;
+ });
+
+ Future<Result> future = mBackgroundDexoptJob.start();
+ mBackgroundDexoptJob.cancel();
+ dexoptCancelled.release();
+ Utils.getFuture(future);
+ }
+
+ @Test
+ public void testSchedule() {
+ var captor = ArgumentCaptor.forClass(JobInfo.class);
+ when(mJobScheduler.schedule(captor.capture())).thenReturn(JobScheduler.RESULT_SUCCESS);
+
+ assertThat(mBackgroundDexoptJob.schedule()).isEqualTo(ArtFlags.SCHEDULE_SUCCESS);
+
+ JobInfo jobInfo = captor.getValue();
+ assertThat(jobInfo.getIntervalMillis()).isEqualTo(BackgroundDexoptJob.JOB_INTERVAL_MS);
+ assertThat(jobInfo.isRequireDeviceIdle()).isTrue();
+ assertThat(jobInfo.isRequireCharging()).isTrue();
+ assertThat(jobInfo.isRequireBatteryNotLow()).isTrue();
+ assertThat(jobInfo.isRequireStorageNotLow()).isFalse();
+ }
+
+ @Test
+ public void testScheduleDisabled() {
+ when(SystemProperties.getBoolean(eq("pm.dexopt.disable_bg_dexopt"), anyBoolean()))
+ .thenReturn(true);
+
+ assertThat(mBackgroundDexoptJob.schedule())
+ .isEqualTo(ArtFlags.SCHEDULE_DISABLED_BY_SYSPROP);
+
+ verify(mJobScheduler, never()).schedule(any());
+ }
+
+ @Test
+ public void testScheduleOverride() {
+ mConfig.setScheduleBackgroundDexoptJobCallback(Runnable::run, builder -> {
+ builder.setRequiresBatteryNotLow(false);
+ builder.setPriority(JobInfo.PRIORITY_LOW);
+ });
+
+ var captor = ArgumentCaptor.forClass(JobInfo.class);
+ when(mJobScheduler.schedule(captor.capture())).thenReturn(JobScheduler.RESULT_SUCCESS);
+
+ assertThat(mBackgroundDexoptJob.schedule()).isEqualTo(ArtFlags.SCHEDULE_SUCCESS);
+
+ JobInfo jobInfo = captor.getValue();
+ assertThat(jobInfo.getIntervalMillis()).isEqualTo(BackgroundDexoptJob.JOB_INTERVAL_MS);
+ assertThat(jobInfo.isRequireDeviceIdle()).isTrue();
+ assertThat(jobInfo.isRequireCharging()).isTrue();
+ assertThat(jobInfo.isRequireBatteryNotLow()).isFalse();
+ assertThat(jobInfo.getPriority()).isEqualTo(JobInfo.PRIORITY_LOW);
+ }
+
+ @Test
+ public void testScheduleOverrideCleared() {
+ mConfig.setScheduleBackgroundDexoptJobCallback(
+ Runnable::run, builder -> { builder.setRequiresBatteryNotLow(false); });
+ mConfig.clearScheduleBackgroundDexoptJobCallback();
+
+ var captor = ArgumentCaptor.forClass(JobInfo.class);
+ when(mJobScheduler.schedule(captor.capture())).thenReturn(JobScheduler.RESULT_SUCCESS);
+
+ assertThat(mBackgroundDexoptJob.schedule()).isEqualTo(ArtFlags.SCHEDULE_SUCCESS);
+
+ JobInfo jobInfo = captor.getValue();
+ assertThat(jobInfo.isRequireBatteryNotLow()).isTrue();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testScheduleOverrideStorageNotLow() {
+ mConfig.setScheduleBackgroundDexoptJobCallback(
+ Runnable::run, builder -> { builder.setRequiresStorageNotLow(true); });
+
+ mBackgroundDexoptJob.schedule();
+ }
+
+ @Test
+ public void testUnschedule() {
+ mBackgroundDexoptJob.unschedule();
+ verify(mJobScheduler).cancel(anyInt());
+ }
+
+ @Test
+ public void testWantsRescheduleFalsePerformed() throws Exception {
+ when(mDexoptResult.getFinalStatus()).thenReturn(DexoptResult.DEXOPT_PERFORMED);
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenReturn(mDexoptResult);
+
+ mBackgroundDexoptJob.onStartJob(mJobService, mJobParameters);
+ assertThat(mJobFinishedCalled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
+
+ verify(mJobService).jobFinished(any(), eq(false) /* wantsReschedule */);
+ }
+
+ @Test
+ public void testWantsRescheduleFalseFatalError() throws Exception {
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenThrow(RuntimeException.class);
+
+ mBackgroundDexoptJob.onStartJob(mJobService, mJobParameters);
+ assertThat(mJobFinishedCalled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
+
+ verify(mJobService).jobFinished(any(), eq(false) /* wantsReschedule */);
+ }
+
+ @Test
+ public void testWantsRescheduleTrue() throws Exception {
+ when(mDexoptResult.getFinalStatus()).thenReturn(DexoptResult.DEXOPT_CANCELLED);
+ when(mArtManagerLocal.dexoptPackages(any(), any(), any(), any(), any()))
+ .thenReturn(mDexoptResult);
+
+ mBackgroundDexoptJob.onStartJob(mJobService, mJobParameters);
+ assertThat(mJobFinishedCalled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
+
+ verify(mJobService).jobFinished(any(), eq(true) /* wantsReschedule */);
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/DebouncerTest.java b/libartservice/service/javatests/com/android/server/art/DebouncerTest.java
new file mode 100644
index 0000000..bf0bc70
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/DebouncerTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.lenient;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.art.testing.MockClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class DebouncerTest {
+ private MockClock mMockClock;
+ private Debouncer mDebouncer;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockClock = new MockClock();
+ mDebouncer =
+ new Debouncer(100 /* intervalMs */, () -> mMockClock.createScheduledExecutor());
+ }
+
+ @Test
+ public void test() throws Exception {
+ List<Integer> list = new ArrayList<>();
+
+ mDebouncer.maybeRunAsync(() -> list.add(1));
+ mDebouncer.maybeRunAsync(() -> list.add(2));
+ mMockClock.advanceTime(100);
+ mDebouncer.maybeRunAsync(() -> list.add(3));
+ mMockClock.advanceTime(99);
+ mDebouncer.maybeRunAsync(() -> list.add(4));
+ mMockClock.advanceTime(99);
+ mDebouncer.maybeRunAsync(() -> list.add(5));
+ mMockClock.advanceTime(1000);
+
+ assertThat(list).containsExactly(2, 5).inOrder();
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
new file mode 100644
index 0000000..5eb5f71
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo;
+import static com.android.server.art.DexUseManagerLocal.DexLoader;
+import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.art.model.DexContainerFileUseInfo;
+import com.android.server.art.testing.MockClock;
+import com.android.server.art.testing.StaticMockitoRule;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
+
+import dalvik.system.PathClassLoader;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DexUseManagerTest {
+ private static final String LOADING_PKG_NAME = "com.example.loadingpackage";
+ private static final String OWNING_PKG_NAME = "com.example.owningpackage";
+ private static final String BASE_APK = "/data/app/" + OWNING_PKG_NAME + "/base.apk";
+ private static final String SPLIT_APK = "/data/app/" + OWNING_PKG_NAME + "/split_0.apk";
+
+ @Rule
+ public StaticMockitoRule mockitoRule =
+ new StaticMockitoRule(SystemProperties.class, Constants.class, Process.class);
+
+ private final UserHandle mUserHandle = Binder.getCallingUserHandle();
+
+ /**
+ * The default value of `isDexFilePublic` returned by `getSecondaryDexInfo`. The value doesn't
+ * matter because it's undefined, but it's needed for deep equality check, to make the test
+ * simpler.
+ */
+ private final boolean mDefaultIsDexFilePublic = true;
+
+ @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
+ @Mock private DexUseManagerLocal.Injector mInjector;
+ @Mock private IArtd mArtd;
+ @Mock private Context mContext;
+ private DexUseManagerLocal mDexUseManager;
+ private String mCeDir;
+ private String mDeDir;
+ private MockClock mMockClock;
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ // No ISA translation.
+ lenient()
+ .when(SystemProperties.get(argThat(arg -> arg.startsWith("ro.dalvik.vm.isa."))))
+ .thenReturn("");
+
+ lenient().when(Constants.getPreferredAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative64BitAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative32BitAbi()).thenReturn("armeabi-v7a");
+
+ lenient().when(Process.isIsolatedUid(anyInt())).thenReturn(false);
+
+ PackageState loadingPkgState = createPackageState(LOADING_PKG_NAME, "armeabi-v7a");
+ lenient().when(mSnapshot.getPackageState(eq(LOADING_PKG_NAME))).thenReturn(loadingPkgState);
+ PackageState owningPkgState = createPackageState(OWNING_PKG_NAME, "arm64-v8a");
+ lenient().when(mSnapshot.getPackageState(eq(OWNING_PKG_NAME))).thenReturn(owningPkgState);
+
+ lenient()
+ .when(mSnapshot.getPackageStates())
+ .thenReturn(
+ Map.of(LOADING_PKG_NAME, loadingPkgState, OWNING_PKG_NAME, owningPkgState));
+
+ mBroadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+ lenient()
+ .when(mContext.registerReceiver(mBroadcastReceiverCaptor.capture(), any()))
+ .thenReturn(mock(Intent.class));
+
+ mCeDir = Environment
+ .getDataCePackageDirectoryForUser(StorageManager.UUID_DEFAULT,
+ Binder.getCallingUserHandle(), OWNING_PKG_NAME)
+ .toString();
+ mDeDir = Environment
+ .getDataDePackageDirectoryForUser(StorageManager.UUID_DEFAULT,
+ Binder.getCallingUserHandle(), OWNING_PKG_NAME)
+ .toString();
+ mMockClock = new MockClock();
+
+ File tempFile = File.createTempFile("package-dex-usage", ".pb");
+ tempFile.deleteOnExit();
+
+ lenient().when(mInjector.getArtd()).thenReturn(mArtd);
+ lenient().when(mInjector.getCurrentTimeMillis()).thenReturn(0l);
+ lenient().when(mInjector.getFilename()).thenReturn(tempFile.getPath());
+ lenient()
+ .when(mInjector.createScheduledExecutor())
+ .thenAnswer(invocation -> mMockClock.createScheduledExecutor());
+ lenient().when(mInjector.getContext()).thenReturn(mContext);
+
+ mDexUseManager = new DexUseManagerLocal(mInjector);
+ mDexUseManager.systemReady();
+ }
+
+ @Test
+ public void testPrimaryDexOwned() {
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(BASE_APK, "CLC"));
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, BASE_APK))
+ .containsExactly(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */));
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, BASE_APK)).isFalse();
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, SPLIT_APK)).isEmpty();
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, SPLIT_APK))
+ .isFalse();
+ }
+
+ @Test
+ public void testPrimaryDexOwnedIsolated() {
+ when(Process.isIsolatedUid(anyInt())).thenReturn(true);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(BASE_APK, "CLC"));
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, BASE_APK))
+ .containsExactly(DexLoader.create(OWNING_PKG_NAME, true /* isolatedProcess */));
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, BASE_APK)).isTrue();
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, SPLIT_APK)).isEmpty();
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, SPLIT_APK))
+ .isFalse();
+ }
+
+ @Test
+ public void testPrimaryDexOwnedSplitIsolated() {
+ when(Process.isIsolatedUid(anyInt())).thenReturn(true);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(SPLIT_APK, "CLC"));
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, BASE_APK)).isEmpty();
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, BASE_APK)).isFalse();
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, SPLIT_APK))
+ .containsExactly(DexLoader.create(OWNING_PKG_NAME, true /* isolatedProcess */));
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, SPLIT_APK)).isTrue();
+ }
+
+ @Test
+ public void testPrimaryDexOthers() {
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(BASE_APK, "CLC"));
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, BASE_APK))
+ .containsExactly(DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */));
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, BASE_APK)).isTrue();
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, SPLIT_APK)).isEmpty();
+ assertThat(mDexUseManager.isPrimaryDexUsedByOtherApps(OWNING_PKG_NAME, SPLIT_APK))
+ .isFalse();
+ }
+
+ /** Checks that it ignores and dedups things correctly. */
+ @Test
+ public void testPrimaryDexMultipleEntries() throws Exception {
+ verifyPrimaryDexMultipleEntries(false /* saveAndLoad */, false /* shutdown */);
+ }
+
+ /** Checks that it saves data after some time has passed and loads data correctly. */
+ @Test
+ public void testPrimaryDexMultipleEntriesPersisted() throws Exception {
+ verifyPrimaryDexMultipleEntries(true /*saveAndLoad */, false /* shutdown */);
+ }
+
+ /** Checks that it saves data when the device is being shutdown and loads data correctly. */
+ @Test
+ public void testPrimaryDexMultipleEntriesPersistedDueToShutdown() throws Exception {
+ verifyPrimaryDexMultipleEntries(true /*saveAndLoad */, true /* shutdown */);
+ }
+
+ private void verifyPrimaryDexMultipleEntries(boolean saveAndLoad, boolean shutdown)
+ throws Exception {
+ when(mInjector.getCurrentTimeMillis()).thenReturn(1000l);
+
+ // These should be ignored.
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, Utils.PLATFORM_PACKAGE_NAME, Map.of(BASE_APK, "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME,
+ Map.of("/data/app/" + OWNING_PKG_NAME + "/non-existing.apk", "CLC"));
+
+ // Some of these should be deduped.
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(BASE_APK, "CLC", SPLIT_APK, "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(BASE_APK, "CLC", SPLIT_APK, "CLC"));
+
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(BASE_APK, "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(BASE_APK, "CLC"));
+
+ when(Process.isIsolatedUid(anyInt())).thenReturn(true);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(BASE_APK, "CLC"));
+ when(mInjector.getCurrentTimeMillis()).thenReturn(2000l);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(BASE_APK, "CLC"));
+
+ if (saveAndLoad) {
+ if (shutdown) {
+ mBroadcastReceiverCaptor.getValue().onReceive(mContext, mock(Intent.class));
+ } else {
+ // MockClock runs tasks synchronously.
+ mMockClock.advanceTime(DexUseManagerLocal.INTERVAL_MS);
+ }
+ mDexUseManager = new DexUseManagerLocal(mInjector);
+ }
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, BASE_APK))
+ .containsExactly(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */),
+ DexLoader.create(OWNING_PKG_NAME, true /* isolatedProcess */),
+ DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */));
+
+ assertThat(mDexUseManager.getPrimaryDexLoaders(OWNING_PKG_NAME, SPLIT_APK))
+ .containsExactly(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */));
+
+ assertThat(mDexUseManager.getPackageLastUsedAtMs(OWNING_PKG_NAME)).isEqualTo(2000l);
+ }
+
+ @Test
+ public void testSecondaryDexOwned() {
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ List<? extends SecondaryDexInfo> dexInfoList =
+ mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME);
+ assertThat(dexInfoList)
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ "CLC", Set.of("arm64-v8a"),
+ Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */)),
+ false /* isUsedByOtherApps */, mDefaultIsDexFilePublic));
+ assertThat(dexInfoList.get(0).classLoaderContext()).isEqualTo("CLC");
+ }
+
+ @Test
+ public void testSecondaryDexOwnedIsolated() {
+ when(Process.isIsolatedUid(anyInt())).thenReturn(true);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mDeDir + "/foo.apk", "CLC"));
+
+ List<? extends SecondaryDexInfo> dexInfoList =
+ mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME);
+ assertThat(dexInfoList)
+ .containsExactly(DetailedSecondaryDexInfo.create(mDeDir + "/foo.apk", mUserHandle,
+ "CLC", Set.of("arm64-v8a"),
+ Set.of(DexLoader.create(OWNING_PKG_NAME, true /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, mDefaultIsDexFilePublic));
+ assertThat(dexInfoList.get(0).classLoaderContext()).isEqualTo("CLC");
+ }
+
+ @Test
+ public void testSecondaryDexOthers() {
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ List<? extends SecondaryDexInfo> dexInfoList =
+ mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME);
+ assertThat(dexInfoList)
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ "CLC", Set.of("armeabi-v7a"),
+ Set.of(DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, mDefaultIsDexFilePublic));
+ assertThat(dexInfoList.get(0).classLoaderContext()).isEqualTo("CLC");
+ }
+
+ @Test
+ public void testSecondaryDexUnsupportedClc() {
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, LOADING_PKG_NAME,
+ Map.of(mCeDir + "/foo.apk", SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT));
+
+ List<? extends SecondaryDexInfo> dexInfoList =
+ mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME);
+ assertThat(dexInfoList)
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT, Set.of("armeabi-v7a"),
+ Set.of(DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, mDefaultIsDexFilePublic));
+ assertThat(dexInfoList.get(0).classLoaderContext()).isNull();
+ }
+
+ @Test
+ public void testSecondaryDexVariableClc() {
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC2"));
+
+ List<? extends SecondaryDexInfo> dexInfoList =
+ mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME);
+ assertThat(dexInfoList)
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ SecondaryDexInfo.VARYING_CLASS_LOADER_CONTEXTS,
+ Set.of("arm64-v8a", "armeabi-v7a"),
+ Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */),
+ DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, mDefaultIsDexFilePublic));
+ assertThat(dexInfoList.get(0).classLoaderContext()).isNull();
+ }
+
+ /** Checks that it ignores and dedups things correctly. */
+ @Test
+ public void testSecondaryDexMultipleEntries() throws Exception {
+ verifySecondaryDexMultipleEntries(false /*saveAndLoad */, false /* shutdown */);
+ }
+
+ /** Checks that it saves data after some time has passed and loads data correctly. */
+ @Test
+ public void testSecondaryDexMultipleEntriesPersisted() throws Exception {
+ verifySecondaryDexMultipleEntries(true /*saveAndLoad */, false /* shutdown */);
+ }
+
+ /** Checks that it saves data when the device is being shutdown and loads data correctly. */
+ @Test
+ public void testSecondaryDexMultipleEntriesPersistedDueToShutdown() throws Exception {
+ verifySecondaryDexMultipleEntries(true /*saveAndLoad */, true /* shutdown */);
+ }
+
+ private void verifySecondaryDexMultipleEntries(boolean saveAndLoad, boolean shutdown)
+ throws Exception {
+ when(mInjector.getCurrentTimeMillis()).thenReturn(1000l);
+
+ // These should be ignored.
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, Utils.PLATFORM_PACKAGE_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of("/some/non-existing.apk", "CLC"));
+
+ // Some of these should be deduped.
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME,
+ Map.of(mCeDir + "/foo.apk", "CLC", mCeDir + "/bar.apk", "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME,
+ Map.of(mCeDir + "/foo.apk", "UpdatedCLC", mCeDir + "/bar.apk", "UpdatedCLC"));
+
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "UpdatedCLC"));
+
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/bar.apk", "DifferentCLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/bar.apk", "UpdatedDifferentCLC"));
+
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME,
+ Map.of(mCeDir + "/baz.apk", SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT));
+
+ when(Process.isIsolatedUid(anyInt())).thenReturn(true);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+ when(mInjector.getCurrentTimeMillis()).thenReturn(2000l);
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME,
+ Map.of(mCeDir + "/foo.apk", SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT));
+
+ if (saveAndLoad) {
+ if (shutdown) {
+ mBroadcastReceiverCaptor.getValue().onReceive(mContext, mock(Intent.class));
+ } else {
+ // MockClock runs tasks synchronously.
+ mMockClock.advanceTime(DexUseManagerLocal.INTERVAL_MS);
+ }
+ mDexUseManager = new DexUseManagerLocal(mInjector);
+ }
+
+ List<? extends SecondaryDexInfo> dexInfoList =
+ mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME);
+ assertThat(dexInfoList)
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ "UpdatedCLC", Set.of("arm64-v8a", "armeabi-v7a"),
+ Set.of(DexLoader.create(OWNING_PKG_NAME,
+ false /* isolatedProcess */),
+ DexLoader.create(OWNING_PKG_NAME,
+ true /* isolatedProcess */),
+ DexLoader.create(LOADING_PKG_NAME,
+ false /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, mDefaultIsDexFilePublic),
+ DetailedSecondaryDexInfo.create(mCeDir + "/bar.apk", mUserHandle,
+ SecondaryDexInfo.VARYING_CLASS_LOADER_CONTEXTS,
+ Set.of("arm64-v8a", "armeabi-v7a"),
+ Set.of(DexLoader.create(
+ OWNING_PKG_NAME, false /* isolatedProcess */),
+ DexLoader.create(
+ LOADING_PKG_NAME, false /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, mDefaultIsDexFilePublic),
+ DetailedSecondaryDexInfo.create(mCeDir + "/baz.apk", mUserHandle,
+ SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT,
+ Set.of("arm64-v8a"),
+ Set.of(DexLoader.create(
+ OWNING_PKG_NAME, false /* isolatedProcess */)),
+ false /* isUsedByOtherApps */, mDefaultIsDexFilePublic));
+
+ assertThat(mDexUseManager.getSecondaryDexContainerFileUseInfo(OWNING_PKG_NAME))
+ .containsExactly(DexContainerFileUseInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ Set.of(OWNING_PKG_NAME, LOADING_PKG_NAME)),
+ DexContainerFileUseInfo.create(mCeDir + "/bar.apk", mUserHandle,
+ Set.of(OWNING_PKG_NAME, LOADING_PKG_NAME)),
+ DexContainerFileUseInfo.create(
+ mCeDir + "/baz.apk", mUserHandle, Set.of(OWNING_PKG_NAME)));
+
+ assertThat(mDexUseManager.getPackageLastUsedAtMs(OWNING_PKG_NAME)).isEqualTo(2000l);
+ }
+
+ @Test
+ public void testFilteredDetailedSecondaryDexPublic() throws Exception {
+ when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk"))
+ .thenReturn(FileVisibility.OTHER_READABLE);
+
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME))
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ "CLC", Set.of("arm64-v8a", "armeabi-v7a"),
+ Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */),
+ DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, true /* isDexFilePublic */));
+ }
+
+ @Test
+ public void testFilteredDetailedSecondaryDexPrivate() throws Exception {
+ when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk"))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ when(Process.isIsolatedUid(anyInt())).thenReturn(true);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME))
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ "CLC", Set.of("arm64-v8a"),
+ Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */)),
+ false /* isUsedByOtherApps */, false /* isDexFilePublic */));
+ }
+
+ @Test
+ public void testFilteredDetailedSecondaryDexFilteredDueToVisibility() throws Exception {
+ when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk"))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ when(Process.isIsolatedUid(anyInt())).thenReturn(true);
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME)).isEmpty();
+ }
+
+ @Test
+ public void testFilteredDetailedSecondaryDexFilteredDueToNotFound() throws Exception {
+ when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk")).thenReturn(FileVisibility.NOT_FOUND);
+
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME)).isEmpty();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUnknownPackage() {
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, "bogus", Map.of(BASE_APK, "CLC"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testEmptyMap() {
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME, Map.of());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullKey() {
+ var map = new HashMap<String, String>();
+ map.put(null, "CLC");
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME, map);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNonAbsoluteKey() {
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, OWNING_PKG_NAME, Map.of("a/b.jar", "CLC"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullValue() {
+ var map = new HashMap<String, String>();
+ map.put(mCeDir + "/foo.apk", null);
+ mDexUseManager.notifyDexContainersLoaded(mSnapshot, OWNING_PKG_NAME, map);
+ }
+
+ @Test
+ public void testFileNotFound() {
+ // It should fail to load the file.
+ when(mInjector.getFilename()).thenReturn("/nonexisting/file");
+ mDexUseManager = new DexUseManagerLocal(mInjector);
+
+ // Add some arbitrary data to see if it works fine after a failed load.
+ mDexUseManager.notifyDexContainersLoaded(
+ mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC"));
+
+ assertThat(mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME))
+ .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle,
+ "CLC", Set.of("armeabi-v7a"),
+ Set.of(DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)),
+ true /* isUsedByOtherApps */, mDefaultIsDexFilePublic));
+ }
+
+ private AndroidPackage createPackage(String packageName) {
+ AndroidPackage pkg = mock(AndroidPackage.class);
+ lenient().when(pkg.getStorageUuid()).thenReturn(StorageManager.UUID_DEFAULT);
+
+ var baseSplit = mock(AndroidPackageSplit.class);
+ lenient().when(baseSplit.getPath()).thenReturn("/data/app/" + packageName + "/base.apk");
+ lenient().when(baseSplit.isHasCode()).thenReturn(true);
+ lenient().when(baseSplit.getClassLoaderName()).thenReturn(PathClassLoader.class.getName());
+
+ var split0 = mock(AndroidPackageSplit.class);
+ lenient().when(split0.getName()).thenReturn("split_0");
+ lenient().when(split0.getPath()).thenReturn("/data/app/" + packageName + "/split_0.apk");
+ lenient().when(split0.isHasCode()).thenReturn(true);
+
+ lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit, split0));
+
+ return pkg;
+ }
+
+ private PackageState createPackageState(String packageName, String primaryAbi) {
+ PackageState pkgState = mock(PackageState.class);
+ lenient().when(pkgState.getPackageName()).thenReturn(packageName);
+ AndroidPackage pkg = createPackage(packageName);
+ lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg);
+ lenient().when(pkgState.getPrimaryCpuAbi()).thenReturn(primaryAbi);
+ return pkgState;
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/DexoptHelperTest.java b/libartservice/service/javatests/com/android/server/art/DexoptHelperTest.java
new file mode 100644
index 0000000..a7651e6
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/DexoptHelperTest.java
@@ -0,0 +1,749 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.ArtManagerLocal.DexoptDoneCallback;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.DexoptResultStatus;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.apphibernation.AppHibernationManager;
+import android.os.CancellationSignal;
+import android.os.PowerManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.pm.PackageStateModulesUtils;
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.Config;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.model.OperationProgress;
+import com.android.server.art.testing.StaticMockitoRule;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.SharedLibrary;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DexoptHelperTest {
+ private static final String PKG_NAME_FOO = "com.example.foo";
+ private static final String PKG_NAME_BAR = "com.example.bar";
+ private static final String PKG_NAME_LIB1 = "com.example.lib1";
+ private static final String PKG_NAME_LIB2 = "com.example.lib2";
+ private static final String PKG_NAME_LIB3 = "com.example.lib3";
+ private static final String PKG_NAME_LIB4 = "com.example.lib4";
+ private static final String PKG_NAME_LIBBAZ = "com.example.libbaz";
+
+ @Rule
+ public StaticMockitoRule mockitoRule = new StaticMockitoRule(PackageStateModulesUtils.class);
+
+ @Mock private DexoptHelper.Injector mInjector;
+ @Mock private PrimaryDexopter mPrimaryDexopter;
+ @Mock private SecondaryDexopter mSecondaryDexopter;
+ @Mock private AppHibernationManager mAhm;
+ @Mock private PowerManager mPowerManager;
+ @Mock private PowerManager.WakeLock mWakeLock;
+ @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
+ private PackageState mPkgStateFoo;
+ private PackageState mPkgStateBar;
+ private PackageState mPkgStateLib1;
+ private PackageState mPkgStateLib2;
+ private PackageState mPkgStateLib4;
+ private PackageState mPkgStateLibbaz;
+ private AndroidPackage mPkgFoo;
+ private AndroidPackage mPkgBar;
+ private AndroidPackage mPkgLib1;
+ private AndroidPackage mPkgLib2;
+ private AndroidPackage mPkgLib4;
+ private AndroidPackage mPkgLibbaz;
+ private CancellationSignal mCancellationSignal;
+ private ExecutorService mExecutor;
+ private List<DexContainerFileDexoptResult> mPrimaryResults;
+ private List<DexContainerFileDexoptResult> mSecondaryResults;
+ private Config mConfig;
+ private DexoptParams mParams;
+ private List<String> mRequestedPackages;
+ private DexoptHelper mDexoptHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ lenient()
+ .when(mPowerManager.newWakeLock(eq(PowerManager.PARTIAL_WAKE_LOCK), any()))
+ .thenReturn(mWakeLock);
+
+ lenient().when(mAhm.isHibernatingGlobally(any())).thenReturn(false);
+ lenient().when(mAhm.isOatArtifactDeletionEnabled()).thenReturn(true);
+
+ mCancellationSignal = new CancellationSignal();
+ mExecutor = Executors.newSingleThreadExecutor();
+ mConfig = new Config();
+
+ preparePackagesAndLibraries();
+
+ mPrimaryResults =
+ createResults("/data/app/foo/base.apk", DexoptResult.DEXOPT_PERFORMED /* status1 */,
+ DexoptResult.DEXOPT_PERFORMED /* status2 */);
+ mSecondaryResults = createResults("/data/user_de/0/foo/foo.apk",
+ DexoptResult.DEXOPT_PERFORMED /* status1 */,
+ DexoptResult.DEXOPT_PERFORMED /* status2 */);
+
+ lenient()
+ .when(mInjector.getPrimaryDexopter(any(), any(), any(), any()))
+ .thenReturn(mPrimaryDexopter);
+ lenient().when(mPrimaryDexopter.dexopt()).thenReturn(mPrimaryResults);
+
+ lenient()
+ .when(mInjector.getSecondaryDexopter(any(), any(), any(), any()))
+ .thenReturn(mSecondaryDexopter);
+ lenient().when(mSecondaryDexopter.dexopt()).thenReturn(mSecondaryResults);
+
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES,
+ ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
+ .build();
+
+ lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAhm);
+ lenient().when(mInjector.getPowerManager()).thenReturn(mPowerManager);
+ lenient().when(mInjector.getConfig()).thenReturn(mConfig);
+
+ mDexoptHelper = new DexoptHelper(mInjector);
+ }
+
+ @After
+ public void tearDown() {
+ mExecutor.shutdown();
+ }
+
+ @Test
+ public void testDexopt() throws Exception {
+ // Only package libbaz fails.
+ var failingPrimaryDexopter = mock(PrimaryDexopter.class);
+ List<DexContainerFileDexoptResult> partialFailureResults =
+ createResults("/data/app/foo/base.apk", DexoptResult.DEXOPT_PERFORMED /* status1 */,
+ DexoptResult.DEXOPT_FAILED /* status2 */);
+ lenient().when(failingPrimaryDexopter.dexopt()).thenReturn(partialFailureResults);
+ when(mInjector.getPrimaryDexopter(same(mPkgStateLibbaz), any(), any(), any()))
+ .thenReturn(failingPrimaryDexopter);
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getRequestedCompilerFilter()).isEqualTo("speed-profile");
+ assertThat(result.getReason()).isEqualTo("install");
+ assertThat(result.getFinalStatus()).isEqualTo(DexoptResult.DEXOPT_FAILED);
+
+ // The requested packages must come first.
+ assertThat(result.getPackageDexoptResults()).hasSize(6);
+ checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ, DexoptResult.DEXOPT_FAILED,
+ List.of(partialFailureResults, mSecondaryResults));
+ checkPackageResult(result, 3 /* index */, PKG_NAME_LIB1, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 4 /* index */, PKG_NAME_LIB2, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 5 /* index */, PKG_NAME_LIB4, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+
+ // The order matters. It should acquire the wake lock only once, at the beginning, and
+ // release the wake lock at the end. When running in a single thread, it should dexopt
+ // primary dex files and the secondary dex files together for each package, and it should
+ // dexopt requested packages, in the given order, and then dexopt dependencies.
+ InOrder inOrder = inOrder(mInjector, mWakeLock);
+ inOrder.verify(mWakeLock).setWorkSource(any());
+ inOrder.verify(mWakeLock).acquire(anyLong());
+ inOrder.verify(mInjector).getPrimaryDexopter(
+ same(mPkgStateFoo), same(mPkgFoo), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getSecondaryDexopter(
+ same(mPkgStateFoo), same(mPkgFoo), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getPrimaryDexopter(
+ same(mPkgStateBar), same(mPkgBar), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getSecondaryDexopter(
+ same(mPkgStateBar), same(mPkgBar), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getPrimaryDexopter(
+ same(mPkgStateLibbaz), same(mPkgLibbaz), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getSecondaryDexopter(
+ same(mPkgStateLibbaz), same(mPkgLibbaz), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getPrimaryDexopter(
+ same(mPkgStateLib1), same(mPkgLib1), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getSecondaryDexopter(
+ same(mPkgStateLib1), same(mPkgLib1), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getPrimaryDexopter(
+ same(mPkgStateLib2), same(mPkgLib2), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getSecondaryDexopter(
+ same(mPkgStateLib2), same(mPkgLib2), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getPrimaryDexopter(
+ same(mPkgStateLib4), same(mPkgLib4), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mInjector).getSecondaryDexopter(
+ same(mPkgStateLib4), same(mPkgLib4), same(mParams), same(mCancellationSignal));
+ inOrder.verify(mWakeLock).release();
+
+ verifyNoMoreDexopt(6 /* expectedPrimaryTimes */, 6 /* expectedSecondaryTimes */);
+
+ verifyNoMoreInteractions(mWakeLock);
+ }
+
+ @Test
+ public void testDexoptNoDependencies() throws Exception {
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_SECONDARY_DEX,
+ ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
+ .build();
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getPackageDexoptResults()).hasSize(3);
+ checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+
+ verifyNoMoreDexopt(3 /* expectedPrimaryTimes */, 3 /* expectedSecondaryTimes */);
+ }
+
+ @Test
+ public void testDexoptPrimaryOnly() throws Exception {
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES,
+ ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
+ .build();
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getPackageDexoptResults()).hasSize(6);
+ checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+ checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+ checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+ checkPackageResult(result, 3 /* index */, PKG_NAME_LIB1, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+ checkPackageResult(result, 4 /* index */, PKG_NAME_LIB2, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+ checkPackageResult(result, 5 /* index */, PKG_NAME_LIB4, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+
+ verifyNoMoreDexopt(6 /* expectedPrimaryTimes */, 0 /* expectedSecondaryTimes */);
+ }
+
+ @Test
+ public void testDexoptPrimaryOnlyNoDependencies() throws Exception {
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(0,
+ ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
+ .build();
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getPackageDexoptResults()).hasSize(3);
+ checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+ checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+ checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults));
+
+ verifyNoMoreDexopt(3 /* expectedPrimaryTimes */, 0 /* expectedSecondaryTimes */);
+ }
+
+ @Test
+ public void testDexoptCancelledBetweenDex2oatInvocations() throws Exception {
+ when(mPrimaryDexopter.dexopt()).thenAnswer(invocation -> {
+ mCancellationSignal.cancel();
+ return mPrimaryResults;
+ });
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getFinalStatus()).isEqualTo(DexoptResult.DEXOPT_CANCELLED);
+
+ assertThat(result.getPackageDexoptResults()).hasSize(6);
+ checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_CANCELLED,
+ List.of(mPrimaryResults));
+ checkPackageResult(
+ result, 1 /* index */, PKG_NAME_BAR, DexoptResult.DEXOPT_CANCELLED, List.of());
+ checkPackageResult(
+ result, 2 /* index */, PKG_NAME_LIBBAZ, DexoptResult.DEXOPT_CANCELLED, List.of());
+ checkPackageResult(
+ result, 3 /* index */, PKG_NAME_LIB1, DexoptResult.DEXOPT_CANCELLED, List.of());
+ checkPackageResult(
+ result, 4 /* index */, PKG_NAME_LIB2, DexoptResult.DEXOPT_CANCELLED, List.of());
+ checkPackageResult(
+ result, 5 /* index */, PKG_NAME_LIB4, DexoptResult.DEXOPT_CANCELLED, List.of());
+
+ verify(mInjector).getPrimaryDexopter(
+ same(mPkgStateFoo), same(mPkgFoo), same(mParams), same(mCancellationSignal));
+
+ verifyNoMoreDexopt(1 /* expectedPrimaryTimes */, 0 /* expectedSecondaryTimes */);
+ }
+
+ @Test
+ public void testDexoptNotDexoptable() throws Exception {
+ when(PackageStateModulesUtils.isDexoptable(mPkgStateFoo)).thenReturn(false);
+
+ mRequestedPackages = List.of(PKG_NAME_FOO);
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getFinalStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
+ assertThat(result.getPackageDexoptResults()).hasSize(1);
+ checkPackageResult(
+ result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_SKIPPED, List.of());
+
+ verifyNoDexopt();
+ }
+
+ @Test
+ public void testDexoptLibraryNotDexoptable() throws Exception {
+ when(PackageStateModulesUtils.isDexoptable(mPkgStateLib1)).thenReturn(false);
+
+ mRequestedPackages = List.of(PKG_NAME_FOO);
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getFinalStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
+ assertThat(result.getPackageDexoptResults()).hasSize(1);
+ checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+
+ verifyNoMoreDexopt(1 /* expectedPrimaryTimes */, 1 /* expectedSecondaryTimes */);
+ }
+
+ @Test
+ public void testDexoptIsHibernating() throws Exception {
+ lenient().when(mAhm.isHibernatingGlobally(PKG_NAME_FOO)).thenReturn(true);
+
+ mRequestedPackages = List.of(PKG_NAME_FOO);
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getFinalStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
+ checkPackageResult(
+ result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_SKIPPED, List.of());
+
+ verifyNoDexopt();
+ }
+
+ @Test
+ public void testDexoptIsHibernatingButOatArtifactDeletionDisabled() throws Exception {
+ lenient().when(mAhm.isHibernatingGlobally(PKG_NAME_FOO)).thenReturn(true);
+ lenient().when(mAhm.isOatArtifactDeletionEnabled()).thenReturn(false);
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(result.getPackageDexoptResults()).hasSize(6);
+ checkPackageResult(result, 0 /* index */, PKG_NAME_FOO, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 1 /* index */, PKG_NAME_BAR, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 2 /* index */, PKG_NAME_LIBBAZ, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 3 /* index */, PKG_NAME_LIB1, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 4 /* index */, PKG_NAME_LIB2, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ checkPackageResult(result, 5 /* index */, PKG_NAME_LIB4, DexoptResult.DEXOPT_PERFORMED,
+ List.of(mPrimaryResults, mSecondaryResults));
+ }
+
+ @Test
+ public void testDexoptAlwaysReleasesWakeLock() throws Exception {
+ when(mPrimaryDexopter.dexopt()).thenThrow(IllegalStateException.class);
+
+ try {
+ mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+ } catch (Exception ignored) {
+ }
+
+ verify(mWakeLock).release();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDexoptPackageNotFound() throws Exception {
+ when(mSnapshot.getPackageState(any())).thenReturn(null);
+
+ mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ verifyNoDexopt();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDexoptNoPackage() throws Exception {
+ lenient().when(mPkgStateFoo.getAndroidPackage()).thenReturn(null);
+
+ mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ verifyNoDexopt();
+ }
+
+ @Test
+ public void testDexoptSplit() throws Exception {
+ mRequestedPackages = List.of(PKG_NAME_FOO);
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName("split_0")
+ .build();
+
+ mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+ }
+
+ @Test
+ public void testDexoptSplitNotFound() throws Exception {
+ mRequestedPackages = List.of(PKG_NAME_FOO);
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName("split_bogus")
+ .build();
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+ });
+ }
+
+ @Test
+ public void testCallbacks() throws Exception {
+ List<DexoptResult> list1 = new ArrayList<>();
+ mConfig.addDexoptDoneCallback(
+ false /* onlyIncludeUpdates */, Runnable::run, result -> list1.add(result));
+
+ List<DexoptResult> list2 = new ArrayList<>();
+ mConfig.addDexoptDoneCallback(
+ false /* onlyIncludeUpdates */, Runnable::run, result -> list2.add(result));
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(list1).containsExactly(result);
+ assertThat(list2).containsExactly(result);
+ }
+
+ @Test
+ public void testCallbackRemoved() throws Exception {
+ List<DexoptResult> list1 = new ArrayList<>();
+ DexoptDoneCallback callback1 = result -> list1.add(result);
+ mConfig.addDexoptDoneCallback(false /* onlyIncludeUpdates */, Runnable::run, callback1);
+
+ List<DexoptResult> list2 = new ArrayList<>();
+ mConfig.addDexoptDoneCallback(
+ false /* onlyIncludeUpdates */, Runnable::run, result -> list2.add(result));
+
+ mConfig.removeDexoptDoneCallback(callback1);
+
+ DexoptResult result = mDexoptHelper.dexopt(
+ mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+ assertThat(list1).isEmpty();
+ assertThat(list2).containsExactly(result);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCallbackAlreadyAdded() throws Exception {
+ List<DexoptResult> list = new ArrayList<>();
+ DexoptDoneCallback callback = result -> list.add(result);
+ mConfig.addDexoptDoneCallback(false /* onlyIncludeUpdates */, Runnable::run, callback);
+ mConfig.addDexoptDoneCallback(false /* onlyIncludeUpdates */, Runnable::run, callback);
+ }
+
+ // Tests `addDexoptDoneCallback` with `onlyIncludeUpdates` being true and false.
+ @Test
+ public void testCallbackWithFailureResults() throws Exception {
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(0,
+ ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
+ .build();
+
+ // This list should collect all results.
+ List<DexoptResult> listAll = new ArrayList<>();
+ mConfig.addDexoptDoneCallback(
+ false /* onlyIncludeUpdates */, Runnable::run, result -> listAll.add(result));
+
+ // This list should only collect results that have updates.
+ List<DexoptResult> listOnlyIncludeUpdates = new ArrayList<>();
+ mConfig.addDexoptDoneCallback(true /* onlyIncludeUpdates */, Runnable::run,
+ result -> listOnlyIncludeUpdates.add(result));
+
+ // Dexopt partially fails on package "foo".
+ List<DexContainerFileDexoptResult> partialFailureResults =
+ createResults("/data/app/foo/base.apk", DexoptResult.DEXOPT_PERFORMED /* status1 */,
+ DexoptResult.DEXOPT_FAILED /* status2 */);
+ var fooPrimaryDexopter = mock(PrimaryDexopter.class);
+ when(mInjector.getPrimaryDexopter(same(mPkgStateFoo), any(), any(), any()))
+ .thenReturn(fooPrimaryDexopter);
+ when(fooPrimaryDexopter.dexopt()).thenReturn(partialFailureResults);
+
+ // Dexopt totally fails on package "bar".
+ List<DexContainerFileDexoptResult> totalFailureResults =
+ createResults("/data/app/bar/base.apk", DexoptResult.DEXOPT_FAILED /* status1 */,
+ DexoptResult.DEXOPT_FAILED /* status2 */);
+ var barPrimaryDexopter = mock(PrimaryDexopter.class);
+ when(mInjector.getPrimaryDexopter(same(mPkgStateBar), any(), any(), any()))
+ .thenReturn(barPrimaryDexopter);
+ when(barPrimaryDexopter.dexopt()).thenReturn(totalFailureResults);
+
+ DexoptResult resultWithSomeUpdates = mDexoptHelper.dexopt(mSnapshot,
+ List.of(PKG_NAME_FOO, PKG_NAME_BAR), mParams, mCancellationSignal, mExecutor);
+ DexoptResult resultWithNoUpdates = mDexoptHelper.dexopt(
+ mSnapshot, List.of(PKG_NAME_BAR), mParams, mCancellationSignal, mExecutor);
+
+ assertThat(listAll).containsExactly(resultWithSomeUpdates, resultWithNoUpdates);
+
+ assertThat(listOnlyIncludeUpdates).hasSize(1);
+ assertThat(listOnlyIncludeUpdates.get(0)
+ .getPackageDexoptResults()
+ .stream()
+ .map(PackageDexoptResult::getPackageName)
+ .collect(Collectors.toList()))
+ .containsExactly(PKG_NAME_FOO);
+ }
+
+ @Test
+ public void testProgressCallback() throws Exception {
+ mParams = new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_SECONDARY_DEX,
+ ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
+ .build();
+
+ // Delay the executor to verify that the commands passed to the executor are not bound to
+ // changing variables.
+ var progressCallbackExecutor = new DelayedExecutor();
+ Consumer<OperationProgress> progressCallback = mock(Consumer.class);
+
+ mDexoptHelper.dexopt(mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor,
+ progressCallbackExecutor, progressCallback);
+
+ progressCallbackExecutor.runAll();
+
+ InOrder inOrder = inOrder(progressCallback);
+ inOrder.verify(progressCallback)
+ .accept(eq(OperationProgress.create(0 /* current */, 3 /* total */)));
+ inOrder.verify(progressCallback)
+ .accept(eq(OperationProgress.create(1 /* current */, 3 /* total */)));
+ inOrder.verify(progressCallback)
+ .accept(eq(OperationProgress.create(2 /* current */, 3 /* total */)));
+ inOrder.verify(progressCallback)
+ .accept(eq(OperationProgress.create(3 /* current */, 3 /* total */)));
+ }
+
+ private AndroidPackage createPackage(boolean multiSplit) {
+ AndroidPackage pkg = mock(AndroidPackage.class);
+
+ var baseSplit = mock(AndroidPackageSplit.class);
+
+ if (multiSplit) {
+ var split0 = mock(AndroidPackageSplit.class);
+ lenient().when(split0.getName()).thenReturn("split_0");
+
+ lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit, split0));
+ } else {
+ lenient().when(pkg.getSplits()).thenReturn(List.of(baseSplit));
+ }
+
+ return pkg;
+ }
+
+ private PackageState createPackageState(
+ String packageName, List<SharedLibrary> deps, boolean multiSplit) {
+ PackageState pkgState = mock(PackageState.class);
+ lenient().when(pkgState.getPackageName()).thenReturn(packageName);
+ lenient().when(pkgState.getAppId()).thenReturn(12345);
+ lenient().when(pkgState.getSharedLibraryDependencies()).thenReturn(deps);
+ AndroidPackage pkg = createPackage(multiSplit);
+ lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg);
+ lenient().when(PackageStateModulesUtils.isDexoptable(pkgState)).thenReturn(true);
+ return pkgState;
+ }
+
+ private SharedLibrary createLibrary(
+ String libraryName, String packageName, List<SharedLibrary> deps) {
+ SharedLibrary library = mock(SharedLibrary.class);
+ lenient().when(library.getName()).thenReturn(libraryName);
+ lenient().when(library.getPackageName()).thenReturn(packageName);
+ lenient().when(library.getDependencies()).thenReturn(deps);
+ return library;
+ }
+
+ private void preparePackagesAndLibraries() {
+ // Dependency graph:
+ // foo bar
+ // | |
+ // lib1a (lib1) lib1b (lib1) lib1c (lib1)
+ // / \ / \ |
+ // / \ / \ |
+ // libbaz (libbaz) lib2 (lib2) lib4 (lib4) lib3 (lib3)
+ //
+ // "lib1a", "lib1b", and "lib1c" belong to the same package "lib1".
+
+ mRequestedPackages = List.of(PKG_NAME_FOO, PKG_NAME_BAR, PKG_NAME_LIBBAZ);
+
+ SharedLibrary libbaz = createLibrary("libbaz", PKG_NAME_LIBBAZ, List.of());
+ SharedLibrary lib4 = createLibrary("lib4", PKG_NAME_LIB4, List.of());
+ SharedLibrary lib3 = createLibrary("lib3", PKG_NAME_LIB3, List.of());
+ SharedLibrary lib2 = createLibrary("lib2", PKG_NAME_LIB2, List.of());
+ SharedLibrary lib1a = createLibrary("lib1a", PKG_NAME_LIB1, List.of(libbaz, lib2));
+ SharedLibrary lib1b = createLibrary("lib1b", PKG_NAME_LIB1, List.of(lib2, lib4));
+ SharedLibrary lib1c = createLibrary("lib1c", PKG_NAME_LIB1, List.of(lib3));
+
+ mPkgStateFoo = createPackageState(PKG_NAME_FOO, List.of(lib1a), true /* multiSplit */);
+ mPkgFoo = mPkgStateFoo.getAndroidPackage();
+ lenient().when(mSnapshot.getPackageState(PKG_NAME_FOO)).thenReturn(mPkgStateFoo);
+
+ mPkgStateBar = createPackageState(PKG_NAME_BAR, List.of(lib1b), false /* multiSplit */);
+ mPkgBar = mPkgStateBar.getAndroidPackage();
+ lenient().when(mSnapshot.getPackageState(PKG_NAME_BAR)).thenReturn(mPkgStateBar);
+
+ mPkgStateLib1 = createPackageState(
+ PKG_NAME_LIB1, List.of(libbaz, lib2, lib3, lib4), false /* multiSplit */);
+ mPkgLib1 = mPkgStateLib1.getAndroidPackage();
+ lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB1)).thenReturn(mPkgStateLib1);
+
+ mPkgStateLib2 = createPackageState(PKG_NAME_LIB2, List.of(), false /* multiSplit */);
+ mPkgLib2 = mPkgStateLib2.getAndroidPackage();
+ lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB2)).thenReturn(mPkgStateLib2);
+
+ // This should not be considered as a transitive dependency of any requested package, even
+ // though it is a dependency of package "lib1".
+ PackageState pkgStateLib3 =
+ createPackageState(PKG_NAME_LIB3, List.of(), false /* multiSplit */);
+ lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB3)).thenReturn(pkgStateLib3);
+
+ mPkgStateLib4 = createPackageState(PKG_NAME_LIB4, List.of(), false /* multiSplit */);
+ mPkgLib4 = mPkgStateLib4.getAndroidPackage();
+ lenient().when(mSnapshot.getPackageState(PKG_NAME_LIB4)).thenReturn(mPkgStateLib4);
+
+ mPkgStateLibbaz = createPackageState(PKG_NAME_LIBBAZ, List.of(), false /* multiSplit */);
+ mPkgLibbaz = mPkgStateLibbaz.getAndroidPackage();
+ lenient().when(mSnapshot.getPackageState(PKG_NAME_LIBBAZ)).thenReturn(mPkgStateLibbaz);
+ }
+
+ private void verifyNoDexopt() {
+ verify(mInjector, never()).getPrimaryDexopter(any(), any(), any(), any());
+ verify(mInjector, never()).getSecondaryDexopter(any(), any(), any(), any());
+ }
+
+ private void verifyNoMoreDexopt(int expectedPrimaryTimes, int expectedSecondaryTimes) {
+ verify(mInjector, times(expectedPrimaryTimes))
+ .getPrimaryDexopter(any(), any(), any(), any());
+ verify(mInjector, times(expectedSecondaryTimes))
+ .getSecondaryDexopter(any(), any(), any(), any());
+ }
+
+ private List<DexContainerFileDexoptResult> createResults(
+ String dexPath, @DexoptResultStatus int status1, @DexoptResultStatus int status2) {
+ return List.of(DexContainerFileDexoptResult.create(dexPath, true /* isPrimaryAbi */,
+ "arm64-v8a", "verify", status1, 100 /* dex2oatWallTimeMillis */,
+ 400 /* dex2oatCpuTimeMillis */, 0 /* sizeBytes */,
+ 0 /* sizeBeforeBytes */, false /* isSkippedDueToStorageLow */),
+ DexContainerFileDexoptResult.create(dexPath, false /* isPrimaryAbi */,
+ "armeabi-v7a", "verify", status2, 100 /* dex2oatWallTimeMillis */,
+ 400 /* dex2oatCpuTimeMillis */, 0 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */));
+ }
+
+ private void checkPackageResult(DexoptResult result, int index, String packageName,
+ @DexoptResult.DexoptResultStatus int status,
+ List<List<DexContainerFileDexoptResult>> dexContainerFileDexoptResults) {
+ PackageDexoptResult packageResult = result.getPackageDexoptResults().get(index);
+ assertThat(packageResult.getPackageName()).isEqualTo(packageName);
+ assertThat(packageResult.getStatus()).isEqualTo(status);
+ assertThat(packageResult.getDexContainerFileDexoptResults())
+ .containsExactlyElementsIn(dexContainerFileDexoptResults.stream()
+ .flatMap(r -> r.stream())
+ .collect(Collectors.toList()));
+ }
+
+ /** An executor that delays execution until `runAll` is called. */
+ private static class DelayedExecutor implements Executor {
+ private List<Runnable> mCommands = new ArrayList<>();
+
+ public void execute(Runnable command) {
+ mCommands.add(command);
+ }
+
+ public void runAll() {
+ for (Runnable command : mCommands) {
+ command.run();
+ }
+ mCommands.clear();
+ }
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java b/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java
new file mode 100644
index 0000000..9a518ed
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.DexUseManagerLocal.DexLoader;
+import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo;
+import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.art.model.DexoptStatus;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+@SmallTest
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class DumpHelperTest {
+ private static final String PKG_NAME_FOO = "com.example.foo";
+ private static final String PKG_NAME_BAR = "com.example.bar";
+
+ @Mock private DumpHelper.Injector mInjector;
+ @Mock private ArtManagerLocal mArtManagerLocal;
+ @Mock private DexUseManagerLocal mDexUseManagerLocal;
+ @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot;
+
+ private DumpHelper mDumpHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ lenient().when(mInjector.getArtManagerLocal()).thenReturn(mArtManagerLocal);
+ lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManagerLocal);
+
+ LinkedHashMap<String, PackageState> pkgStates = createPackageStates();
+ lenient().when(mSnapshot.getPackageStates()).thenReturn(pkgStates);
+
+ setUpForFoo();
+ setUpForBar();
+
+ mDumpHelper = new DumpHelper(mInjector);
+ }
+
+ @Test
+ public void testDump() throws Exception {
+ String expected = "[com.example.foo]\n"
+ + " path: /data/app/foo/base.apk\n"
+ + " arm64: [status=speed-profile] [reason=bg-dexopt]\n"
+ + " arm: [status=verify] [reason=install]\n"
+ + " path: /data/app/foo/split_0.apk\n"
+ + " arm64: [status=verify] [reason=vdex]\n"
+ + " arm: [status=verify] [reason=vdex]\n"
+ + " used by other apps: [com.example.bar]\n"
+ + " known secondary dex files:\n"
+ + " /data/user_de/0/foo/1.apk\n"
+ + " arm: [status=run-from-apk] [reason=unknown]\n"
+ + " class loader context: =VaryingClassLoaderContexts=\n"
+ + " used by other apps: [com.example.foo (isolated), com.example.baz]\n"
+ + " /data/user_de/0/foo/2.apk\n"
+ + " arm64: [status=speed-profile] [reason=bg-dexopt]\n"
+ + " arm: [status=verify] [reason=vdex]\n"
+ + " class loader context: PCL[]\n"
+ + "[com.example.bar]\n"
+ + " path: /data/app/bar/base.apk\n"
+ + " arm: [status=verify] [reason=install]\n"
+ + " arm64: [status=verify] [reason=install]\n";
+
+ var stringWriter = new StringWriter();
+ mDumpHelper.dump(new PrintWriter(stringWriter), mSnapshot);
+ assertThat(stringWriter.toString()).isEqualTo(expected);
+ }
+
+ private PackageState createPackageState(String packageName, int appId, boolean hasPackage) {
+ var pkgState = mock(PackageState.class);
+ lenient().when(pkgState.getPackageName()).thenReturn(packageName);
+ lenient().when(pkgState.getAppId()).thenReturn(appId);
+ lenient()
+ .when(pkgState.getAndroidPackage())
+ .thenReturn(hasPackage ? mock(AndroidPackage.class) : null);
+ return pkgState;
+ }
+
+ private LinkedHashMap<String, PackageState> createPackageStates() {
+ // Use LinkedHashMap to ensure the determinism of the output.
+ var pkgStates = new LinkedHashMap<String, PackageState>();
+ pkgStates.put(PKG_NAME_FOO,
+ createPackageState(PKG_NAME_FOO, 10001 /* appId */, true /* hasPackage */));
+ pkgStates.put(PKG_NAME_BAR,
+ createPackageState(PKG_NAME_BAR, 10003 /* appId */, true /* hasPackage */));
+ // This should not be included in the output because it has a negative app id.
+ pkgStates.put("com.android.art",
+ createPackageState("com.android.art", -1 /* appId */, true /* hasPackage */));
+ // This should not be included in the output because it does't have AndroidPackage.
+ pkgStates.put("com.example.null",
+ createPackageState("com.example.null", 10010 /* appId */, false /* hasPackage */));
+ return pkgStates;
+ }
+
+ private void setUpForFoo() {
+ // The order of the dex path and the ABI should be kept in the output.
+ var status = DexoptStatus.create(
+ List.of(DexContainerFileDexoptStatus.create("/data/app/foo/base.apk",
+ true /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a",
+ "speed-profile", "bg-dexopt", "location-ignored"),
+ DexContainerFileDexoptStatus.create("/data/app/foo/base.apk",
+ true /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a",
+ "verify", "install", "location-ignored"),
+ DexContainerFileDexoptStatus.create("/data/app/foo/split_0.apk",
+ true /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a",
+ "verify", "vdex", "location-ignored"),
+ DexContainerFileDexoptStatus.create("/data/app/foo/split_0.apk",
+ true /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a",
+ "verify", "vdex", "location-ignored"),
+ DexContainerFileDexoptStatus.create("/data/user_de/0/foo/1.apk",
+ false /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a",
+ "run-from-apk", "unknown", "location-ignored"),
+ DexContainerFileDexoptStatus.create("/data/user_de/0/foo/2.apk",
+ false /* isPrimaryDex */, true /* isPrimaryAbi */, "arm64-v8a",
+ "speed-profile", "bg-dexopt", "location-ignored"),
+ DexContainerFileDexoptStatus.create("/data/user_de/0/foo/2.apk",
+ false /* isPrimaryDex */, false /* isPrimaryAbi */, "armeabi-v7a",
+ "verify", "vdex", "location-ignored")));
+
+ lenient()
+ .when(mArtManagerLocal.getDexoptStatus(any(), eq(PKG_NAME_FOO)))
+ .thenReturn(status);
+
+ // The output should not show "used by other apps:".
+ lenient()
+ .when(mDexUseManagerLocal.getPrimaryDexLoaders(
+ PKG_NAME_FOO, "/data/app/foo/base.apk"))
+ .thenReturn(Set.of());
+
+ // The output should not show "foo" in "used by other apps:".
+ lenient()
+ .when(mDexUseManagerLocal.getPrimaryDexLoaders(
+ PKG_NAME_FOO, "/data/app/foo/split_0.apk"))
+ .thenReturn(Set.of(DexLoader.create(PKG_NAME_FOO, false /* isolatedProcess */),
+ DexLoader.create(PKG_NAME_BAR, false /* isolatedProcess */)));
+
+ var info1 = mock(SecondaryDexInfo.class);
+ lenient().when(info1.dexPath()).thenReturn("/data/user_de/0/foo/1.apk");
+ lenient()
+ .when(info1.displayClassLoaderContext())
+ .thenReturn(SecondaryDexInfo.VARYING_CLASS_LOADER_CONTEXTS);
+ var loaders = new LinkedHashSet<DexLoader>();
+ // The output should show "foo" with "(isolated)" in "used by other apps:".
+ loaders.add(DexLoader.create(PKG_NAME_FOO, true /* isolatedProcess */));
+ loaders.add(DexLoader.create("com.example.baz", false /* isolatedProcess */));
+ lenient().when(info1.loaders()).thenReturn(loaders);
+
+ var info2 = mock(SecondaryDexInfo.class);
+ lenient().when(info2.dexPath()).thenReturn("/data/user_de/0/foo/2.apk");
+ lenient().when(info2.displayClassLoaderContext()).thenReturn("PCL[]");
+ // The output should not show "used by other apps:".
+ lenient()
+ .when(info2.loaders())
+ .thenReturn(Set.of(DexLoader.create(PKG_NAME_FOO, false /* isolatedProcess */)));
+
+ lenient()
+ .doReturn(List.of(info1, info2))
+ .when(mDexUseManagerLocal)
+ .getSecondaryDexInfo(PKG_NAME_FOO);
+ }
+
+ private void setUpForBar() {
+ // The order of the ABI should be kept in the output, despite that it's different from the
+ // order for package "foo".
+ // The output should not show "known secondary dex files:".
+ var status = DexoptStatus.create(
+ List.of(DexContainerFileDexoptStatus.create("/data/app/bar/base.apk",
+ true /* isPrimaryDex */, true /* isPrimaryAbi */, "armeabi-v7a",
+ "verify", "install", "location-ignored"),
+ DexContainerFileDexoptStatus.create("/data/app/bar/base.apk",
+ true /* isPrimaryDex */, false /* isPrimaryAbi */, "arm64-v8a",
+ "verify", "install", "location-ignored")));
+
+ lenient()
+ .when(mArtManagerLocal.getDexoptStatus(any(), eq(PKG_NAME_BAR)))
+ .thenReturn(status);
+
+ lenient()
+ .when(mDexUseManagerLocal.getPrimaryDexLoaders(
+ PKG_NAME_BAR, "/data/app/bar/base.apk"))
+ .thenReturn(Set.of());
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexUtilsTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexUtilsTest.java
new file mode 100644
index 0000000..3a83c6b
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexUtilsTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.PrimaryDexUtils.DetailedPrimaryDexInfo;
+import static com.android.server.art.PrimaryDexUtils.PrimaryDexInfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.SharedLibrary;
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.DexClassLoader;
+import dalvik.system.PathClassLoader;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
+public class PrimaryDexUtilsTest {
+ @Before
+ public void setUp() {}
+
+ @Test
+ public void testGetDexInfo() {
+ List<PrimaryDexInfo> infos =
+ PrimaryDexUtils.getDexInfo(createPackage(false /* isIsolatedSplitLoading */));
+ checkBasicInfo(infos);
+ }
+
+ @Test
+ public void testGetDetailedDexInfo() {
+ List<DetailedPrimaryDexInfo> infos = PrimaryDexUtils.getDetailedDexInfo(
+ createPackageState(), createPackage(false /* isIsolatedSplitLoading */));
+ checkBasicInfo(infos);
+
+ String sharedLibrariesContext = "{"
+ + "PCL[library_2.jar]{PCL[library_1_dex_1.jar:library_1_dex_2.jar]}#"
+ + "PCL[library_3.jar]#"
+ + "PCL[library_4.jar]{PCL[library_1_dex_1.jar:library_1_dex_2.jar]}"
+ + "}";
+
+ assertThat(infos.get(0).classLoaderContext()).isEqualTo("PCL[]" + sharedLibrariesContext);
+ assertThat(infos.get(1).classLoaderContext())
+ .isEqualTo("PCL[base.apk]" + sharedLibrariesContext);
+ assertThat(infos.get(2).classLoaderContext()).isEqualTo(null);
+ assertThat(infos.get(3).classLoaderContext())
+ .isEqualTo("PCL[base.apk:split_0.apk:split_1.apk]" + sharedLibrariesContext);
+ assertThat(infos.get(4).classLoaderContext())
+ .isEqualTo("PCL[base.apk:split_0.apk:split_1.apk:split_2.apk]"
+ + sharedLibrariesContext);
+ }
+
+ @Test
+ public void testGetDetailedDexInfoIsolated() {
+ List<DetailedPrimaryDexInfo> infos = PrimaryDexUtils.getDetailedDexInfo(
+ createPackageState(), createPackage(true /* isIsolatedSplitLoading */));
+ checkBasicInfo(infos);
+
+ String sharedLibrariesContext = "{"
+ + "PCL[library_2.jar]{PCL[library_1_dex_1.jar:library_1_dex_2.jar]}#"
+ + "PCL[library_3.jar]#"
+ + "PCL[library_4.jar]{PCL[library_1_dex_1.jar:library_1_dex_2.jar]}"
+ + "}";
+
+ assertThat(infos.get(0).classLoaderContext()).isEqualTo("PCL[]" + sharedLibrariesContext);
+ assertThat(infos.get(1).classLoaderContext())
+ .isEqualTo("PCL[];DLC[split_2.apk];PCL[base.apk]" + sharedLibrariesContext);
+ assertThat(infos.get(2).classLoaderContext()).isEqualTo(null);
+ assertThat(infos.get(3).classLoaderContext())
+ .isEqualTo("DLC[];PCL[base.apk]" + sharedLibrariesContext);
+ assertThat(infos.get(4).classLoaderContext()).isEqualTo("PCL[]");
+ assertThat(infos.get(5).classLoaderContext()).isEqualTo("PCL[];PCL[split_3.apk]");
+ }
+
+ private <T extends PrimaryDexInfo> void checkBasicInfo(List<T> infos) {
+ assertThat(infos.get(0).dexPath()).isEqualTo("/data/app/foo/base.apk");
+ assertThat(infos.get(0).hasCode()).isTrue();
+ assertThat(infos.get(0).splitName()).isNull();
+
+ assertThat(infos.get(1).dexPath()).isEqualTo("/data/app/foo/split_0.apk");
+ assertThat(infos.get(1).hasCode()).isTrue();
+ assertThat(infos.get(1).splitName()).isEqualTo("split_0");
+
+ assertThat(infos.get(2).dexPath()).isEqualTo("/data/app/foo/split_1.apk");
+ assertThat(infos.get(2).hasCode()).isFalse();
+ assertThat(infos.get(2).splitName()).isEqualTo("split_1");
+
+ assertThat(infos.get(3).dexPath()).isEqualTo("/data/app/foo/split_2.apk");
+ assertThat(infos.get(3).hasCode()).isTrue();
+ assertThat(infos.get(3).splitName()).isEqualTo("split_2");
+
+ assertThat(infos.get(4).dexPath()).isEqualTo("/data/app/foo/split_3.apk");
+ assertThat(infos.get(4).hasCode()).isTrue();
+ assertThat(infos.get(4).splitName()).isEqualTo("split_3");
+
+ assertThat(infos.get(5).dexPath()).isEqualTo("/data/app/foo/split_4.apk");
+ assertThat(infos.get(5).hasCode()).isTrue();
+ assertThat(infos.get(5).splitName()).isEqualTo("split_4");
+ }
+
+ private AndroidPackage createPackage(boolean isIsolatedSplitLoading) {
+ AndroidPackage pkg = mock(AndroidPackage.class);
+
+ var baseSplit = mock(AndroidPackageSplit.class);
+ lenient().when(baseSplit.getPath()).thenReturn("/data/app/foo/base.apk");
+ lenient().when(baseSplit.isHasCode()).thenReturn(true);
+ lenient().when(baseSplit.getClassLoaderName()).thenReturn(PathClassLoader.class.getName());
+
+ var split0 = mock(AndroidPackageSplit.class);
+ lenient().when(split0.getName()).thenReturn("split_0");
+ lenient().when(split0.getPath()).thenReturn("/data/app/foo/split_0.apk");
+ lenient().when(split0.isHasCode()).thenReturn(true);
+
+ var split1 = mock(AndroidPackageSplit.class);
+ lenient().when(split1.getName()).thenReturn("split_1");
+ lenient().when(split1.getPath()).thenReturn("/data/app/foo/split_1.apk");
+ lenient().when(split1.isHasCode()).thenReturn(false);
+
+ var split2 = mock(AndroidPackageSplit.class);
+ lenient().when(split2.getName()).thenReturn("split_2");
+ lenient().when(split2.getPath()).thenReturn("/data/app/foo/split_2.apk");
+ lenient().when(split2.isHasCode()).thenReturn(true);
+
+ var split3 = mock(AndroidPackageSplit.class);
+ lenient().when(split3.getName()).thenReturn("split_3");
+ lenient().when(split3.getPath()).thenReturn("/data/app/foo/split_3.apk");
+ lenient().when(split3.isHasCode()).thenReturn(true);
+
+ var split4 = mock(AndroidPackageSplit.class);
+ lenient().when(split4.getName()).thenReturn("split_4");
+ lenient().when(split4.getPath()).thenReturn("/data/app/foo/split_4.apk");
+ lenient().when(split4.isHasCode()).thenReturn(true);
+
+ var splits = List.of(baseSplit, split0, split1, split2, split3, split4);
+ lenient().when(pkg.getSplits()).thenReturn(splits);
+
+ if (isIsolatedSplitLoading) {
+ // split_0: PCL(PathClassLoader), depends on split_2.
+ // split_1: no code.
+ // split_2: DLC(DelegateLastClassLoader), depends on base.
+ // split_3: PCL(DexClassLoader), no dependency.
+ // split_4: PCL(null), depends on split_3.
+ lenient().when(split0.getClassLoaderName()).thenReturn(PathClassLoader.class.getName());
+ lenient().when(split1.getClassLoaderName()).thenReturn(null);
+ lenient()
+ .when(split2.getClassLoaderName())
+ .thenReturn(DelegateLastClassLoader.class.getName());
+ lenient().when(split3.getClassLoaderName()).thenReturn(DexClassLoader.class.getName());
+ lenient().when(split4.getClassLoaderName()).thenReturn(null);
+
+ lenient().when(split0.getDependencies()).thenReturn(List.of(split2));
+ lenient().when(split2.getDependencies()).thenReturn(List.of(baseSplit));
+ lenient().when(split4.getDependencies()).thenReturn(List.of(split3));
+ lenient().when(pkg.isIsolatedSplitLoading()).thenReturn(true);
+ } else {
+ lenient().when(pkg.isIsolatedSplitLoading()).thenReturn(false);
+ }
+
+ return pkg;
+ }
+
+ private PackageState createPackageState() {
+ PackageState pkgState = mock(PackageState.class);
+
+ lenient().when(pkgState.getPackageName()).thenReturn("com.example.foo");
+
+ // Base depends on library 2, 3, 4.
+ // Library 2, 4 depends on library 1.
+ List<SharedLibrary> usesLibraryInfos = new ArrayList<>();
+
+ SharedLibrary library1 = mock(SharedLibrary.class);
+ lenient()
+ .when(library1.getAllCodePaths())
+ .thenReturn(List.of("library_1_dex_1.jar", "library_1_dex_2.jar"));
+ lenient().when(library1.getDependencies()).thenReturn(null);
+
+ SharedLibrary library2 = mock(SharedLibrary.class);
+ lenient().when(library2.getAllCodePaths()).thenReturn(List.of("library_2.jar"));
+ lenient().when(library2.getDependencies()).thenReturn(List.of(library1));
+ usesLibraryInfos.add(library2);
+
+ SharedLibrary library3 = mock(SharedLibrary.class);
+ lenient().when(library3.getAllCodePaths()).thenReturn(List.of("library_3.jar"));
+ lenient().when(library3.getDependencies()).thenReturn(null);
+ usesLibraryInfos.add(library3);
+
+ SharedLibrary library4 = mock(SharedLibrary.class);
+ lenient().when(library4.getAllCodePaths()).thenReturn(List.of("library_4.jar"));
+ lenient().when(library4.getDependencies()).thenReturn(List.of(library1));
+ usesLibraryInfos.add(library4);
+
+ lenient().when(pkgState.getSharedLibraryDependencies()).thenReturn(usesLibraryInfos);
+
+ return pkgState;
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
new file mode 100644
index 0000000..6340efc
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterParameterizedTest.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.AidlUtils.buildFsPermission;
+import static com.android.server.art.AidlUtils.buildOutputArtifacts;
+import static com.android.server.art.AidlUtils.buildPermissionSettings;
+import static com.android.server.art.OutputArtifacts.PermissionSettings;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.testing.TestingUtils.deepEq;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.testing.OnSuccessRule;
+import com.android.server.art.testing.TestingUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(Parameterized.class)
+public class PrimaryDexopterParameterizedTest extends PrimaryDexopterTestBase {
+ @Rule
+ public OnSuccessRule onSuccessRule = new OnSuccessRule(() -> {
+ // Don't do this on failure because it will make the failure hard to understand.
+ verifyNoMoreInteractions(mArtd);
+ });
+
+ private DexoptParams mDexoptParams;
+
+ private PrimaryDexopter mPrimaryDexopter;
+
+ @Parameter(0) public Params mParams;
+
+ @Parameters(name = "{0}")
+ public static Iterable<Params> data() {
+ List<Params> list = new ArrayList<>();
+ Params params;
+
+ // Baseline.
+ params = new Params();
+ list.add(params);
+
+ params = new Params();
+ params.mRequestedCompilerFilter = "speed";
+ params.mExpectedCompilerFilter = "speed";
+ list.add(params);
+
+ params = new Params();
+ params.mIsSystem = true;
+ params.mExpectedIsInDalvikCache = true;
+ list.add(params);
+
+ params = new Params();
+ params.mIsSystem = true;
+ params.mIsUpdatedSystemApp = true;
+ list.add(params);
+
+ params = new Params();
+ params.mIsSystem = true;
+ params.mHiddenApiEnforcementPolicy = ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+ params.mExpectedIsInDalvikCache = true;
+ params.mExpectedIsHiddenApiPolicyEnabled = false;
+ list.add(params);
+
+ params = new Params();
+ params.mIsDebuggable = true;
+ params.mRequestedCompilerFilter = "speed";
+ params.mExpectedCompilerFilter = "verify";
+ params.mExpectedIsDebuggable = true;
+ list.add(params);
+
+ params = new Params();
+ params.mIsVmSafeMode = true;
+ params.mRequestedCompilerFilter = "speed";
+ params.mExpectedCompilerFilter = "verify";
+ list.add(params);
+
+ params = new Params();
+ params.mIsUseEmbeddedDex = true;
+ params.mRequestedCompilerFilter = "speed";
+ params.mExpectedCompilerFilter = "verify";
+ list.add(params);
+
+ params = new Params();
+ params.mAlwaysDebuggable = true;
+ params.mExpectedIsDebuggable = true;
+ list.add(params);
+
+ params = new Params();
+ params.mIsSystemUi = true;
+ params.mExpectedCompilerFilter = "speed";
+ list.add(params);
+
+ params = new Params();
+ params.mForce = true;
+ params.mShouldDowngrade = false;
+ params.mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+ list.add(params);
+
+ params = new Params();
+ params.mForce = true;
+ params.mShouldDowngrade = true;
+ params.mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+ list.add(params);
+
+ params = new Params();
+ params.mShouldDowngrade = true;
+ params.mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_WORSE;
+ list.add(params);
+
+ params = new Params();
+ // This should not change the result.
+ params.mSkipIfStorageLow = true;
+ list.add(params);
+
+ return list;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(mParams.mIsSystemUi);
+
+ lenient()
+ .when(SystemProperties.getBoolean(eq("dalvik.vm.always_debuggable"), anyBoolean()))
+ .thenReturn(mParams.mAlwaysDebuggable);
+
+ lenient().when(mPkg.isVmSafeMode()).thenReturn(mParams.mIsVmSafeMode);
+ lenient().when(mPkg.isDebuggable()).thenReturn(mParams.mIsDebuggable);
+ lenient().when(mPkg.getTargetSdkVersion()).thenReturn(123);
+ lenient()
+ .when(mPkgState.getHiddenApiEnforcementPolicy())
+ .thenReturn(mParams.mHiddenApiEnforcementPolicy);
+ lenient().when(mPkg.isUseEmbeddedDex()).thenReturn(mParams.mIsUseEmbeddedDex);
+ lenient().when(mPkgState.isSystem()).thenReturn(mParams.mIsSystem);
+ lenient().when(mPkgState.isUpdatedSystemApp()).thenReturn(mParams.mIsUpdatedSystemApp);
+
+ mDexoptParams =
+ new DexoptParams.Builder("install")
+ .setCompilerFilter(mParams.mRequestedCompilerFilter)
+ .setPriorityClass(ArtFlags.PRIORITY_INTERACTIVE)
+ .setFlags(mParams.mForce ? ArtFlags.FLAG_FORCE : 0, ArtFlags.FLAG_FORCE)
+ .setFlags(mParams.mShouldDowngrade ? ArtFlags.FLAG_SHOULD_DOWNGRADE : 0,
+ ArtFlags.FLAG_SHOULD_DOWNGRADE)
+ .setFlags(mParams.mSkipIfStorageLow ? ArtFlags.FLAG_SKIP_IF_STORAGE_LOW : 0,
+ ArtFlags.FLAG_SKIP_IF_STORAGE_LOW)
+ .build();
+
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+ }
+
+ @Test
+ public void testDexopt() throws Exception {
+ PermissionSettings permissionSettings = buildPermissionSettings(
+ buildFsPermission(Process.SYSTEM_UID /* uid */, Process.SYSTEM_UID /* gid */,
+ false /* isOtherReadable */, true /* isOtherExecutable */),
+ buildFsPermission(Process.SYSTEM_UID /* uid */, SHARED_GID /* gid */,
+ true /* isOtherReadable */),
+ null /* seContext */);
+ DexoptOptions dexoptOptions = new DexoptOptions();
+ dexoptOptions.compilationReason = "install";
+ dexoptOptions.targetSdkVersion = 123;
+ dexoptOptions.debuggable = mParams.mExpectedIsDebuggable;
+ dexoptOptions.generateAppImage = false;
+ dexoptOptions.hiddenApiPolicyEnabled = mParams.mExpectedIsHiddenApiPolicyEnabled;
+
+ when(mArtd.createCancellationSignal()).thenReturn(mock(IArtdCancellationSignal.class));
+ when(mArtd.getDmFileVisibility(any())).thenReturn(FileVisibility.NOT_FOUND);
+
+ // The first one is normal.
+ doReturn(dexoptIsNeeded())
+ .when(mArtd)
+ .getDexoptNeeded("/data/app/foo/base.apk", "arm64", "PCL[]",
+ mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger);
+ doReturn(createArtdDexoptResult(false /* cancelled */, 100 /* wallTimeMs */,
+ 400 /* cpuTimeMs */, 30000 /* sizeBytes */, 32000 /* sizeBeforeBytes */))
+ .when(mArtd)
+ .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/base.apk", "arm64",
+ mParams.mExpectedIsInDalvikCache, permissionSettings)),
+ eq("/data/app/foo/base.apk"), eq("arm64"), eq("PCL[]"),
+ eq(mParams.mExpectedCompilerFilter), isNull() /* profile */,
+ isNull() /* inputVdex */, isNull() /* dmFile */,
+ eq(PriorityClass.INTERACTIVE), deepEq(dexoptOptions), any());
+
+ // The second one fails on `dexopt`.
+ doReturn(dexoptIsNeeded())
+ .when(mArtd)
+ .getDexoptNeeded("/data/app/foo/base.apk", "arm", "PCL[]",
+ mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger);
+ doThrow(ServiceSpecificException.class)
+ .when(mArtd)
+ .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/base.apk", "arm",
+ mParams.mExpectedIsInDalvikCache, permissionSettings)),
+ eq("/data/app/foo/base.apk"), eq("arm"), eq("PCL[]"),
+ eq(mParams.mExpectedCompilerFilter), isNull() /* profile */,
+ isNull() /* inputVdex */, isNull() /* dmFile */,
+ eq(PriorityClass.INTERACTIVE), deepEq(dexoptOptions), any());
+
+ // The third one doesn't need dexopt.
+ doReturn(dexoptIsNotNeeded())
+ .when(mArtd)
+ .getDexoptNeeded("/data/app/foo/split_0.apk", "arm64", "PCL[base.apk]",
+ mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger);
+
+ // The fourth one is normal.
+ doReturn(dexoptIsNeeded())
+ .when(mArtd)
+ .getDexoptNeeded("/data/app/foo/split_0.apk", "arm", "PCL[base.apk]",
+ mParams.mExpectedCompilerFilter, mParams.mExpectedDexoptTrigger);
+ doReturn(createArtdDexoptResult(false /* cancelled */, 200 /* wallTimeMs */,
+ 200 /* cpuTimeMs */, 10000 /* sizeBytes */, 0 /* sizeBeforeBytes */))
+ .when(mArtd)
+ .dexopt(deepEq(buildOutputArtifacts("/data/app/foo/split_0.apk", "arm",
+ mParams.mExpectedIsInDalvikCache, permissionSettings)),
+ eq("/data/app/foo/split_0.apk"), eq("arm"), eq("PCL[base.apk]"),
+ eq(mParams.mExpectedCompilerFilter), isNull() /* profile */,
+ isNull() /* inputVdex */, isNull() /* dmFile */,
+ eq(PriorityClass.INTERACTIVE), deepEq(dexoptOptions), any());
+
+ assertThat(mPrimaryDexopter.dexopt())
+ .comparingElementsUsing(TestingUtils.<DexContainerFileDexoptResult>deepEquality())
+ .containsExactly(
+ DexContainerFileDexoptResult.create("/data/app/foo/base.apk",
+ true /* isPrimaryAbi */, "arm64-v8a",
+ mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_PERFORMED,
+ 100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */,
+ 30000 /* sizeBytes */, 32000 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */),
+ DexContainerFileDexoptResult.create("/data/app/foo/base.apk",
+ false /* isPrimaryAbi */, "armeabi-v7a",
+ mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_FAILED,
+ 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */,
+ 0 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */),
+ DexContainerFileDexoptResult.create("/data/app/foo/split_0.apk",
+ true /* isPrimaryAbi */, "arm64-v8a",
+ mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_SKIPPED,
+ 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */,
+ 0 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */),
+ DexContainerFileDexoptResult.create("/data/app/foo/split_0.apk",
+ false /* isPrimaryAbi */, "armeabi-v7a",
+ mParams.mExpectedCompilerFilter, DexoptResult.DEXOPT_PERFORMED,
+ 200 /* dex2oatWallTimeMillis */, 200 /* dex2oatCpuTimeMillis */,
+ 10000 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */));
+ }
+
+ private static class Params {
+ // Package information.
+ public boolean mIsSystem = false;
+ public boolean mIsUpdatedSystemApp = false;
+ public int mHiddenApiEnforcementPolicy = ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED;
+ public boolean mIsVmSafeMode = false;
+ public boolean mIsDebuggable = false;
+ public boolean mIsSystemUi = false;
+ public boolean mIsUseEmbeddedDex = false;
+
+ // Options.
+ public String mRequestedCompilerFilter = "verify";
+ public boolean mForce = false;
+ public boolean mShouldDowngrade = false;
+ public boolean mSkipIfStorageLow = false;
+
+ // System properties.
+ public boolean mAlwaysDebuggable = false;
+
+ // Expectations.
+ public String mExpectedCompilerFilter = "verify";
+ public int mExpectedDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+ public boolean mExpectedIsInDalvikCache = false;
+ public boolean mExpectedIsDebuggable = false;
+ public boolean mExpectedIsHiddenApiPolicyEnabled = true;
+
+ public String toString() {
+ return String.format("isSystem=%b,isUpdatedSystemApp=%b,mHiddenApiEnforcementPolicy=%d"
+ + ",isVmSafeMode=%b,isDebuggable=%b,isSystemUi=%b,isUseEmbeddedDex=%b,"
+ + "requestedCompilerFilter=%s,force=%b,shouldDowngrade=%b,"
+ + "mSkipIfStorageLow=%b,alwaysDebuggable=%b => targetCompilerFilter=%s,"
+ + "expectedDexoptTrigger=%d,expectedIsInDalvikCache=%b,"
+ + "expectedIsDebuggable=%b,expectedIsHiddenApiPolicyEnabled=%b",
+ mIsSystem, mIsUpdatedSystemApp, mHiddenApiEnforcementPolicy, mIsVmSafeMode,
+ mIsDebuggable, mIsSystemUi, mIsUseEmbeddedDex, mRequestedCompilerFilter, mForce,
+ mShouldDowngrade, mSkipIfStorageLow, mAlwaysDebuggable, mExpectedCompilerFilter,
+ mExpectedDexoptTrigger, mExpectedIsInDalvikCache, mExpectedIsDebuggable,
+ mExpectedIsHiddenApiPolicyEnabled);
+ }
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
new file mode 100644
index 0000000..7e7d905
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTest.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.GetDexoptNeededResult.ArtifactsLocation;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.testing.TestingUtils.deepEq;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Process;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.testing.TestingUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PrimaryDexopterTest extends PrimaryDexopterTestBase {
+ private final String mDexPath = "/data/app/foo/base.apk";
+ private final ProfilePath mRefProfile =
+ AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "primary");
+ private final ProfilePath mPrebuiltProfile = AidlUtils.buildProfilePathForPrebuilt(mDexPath);
+ private final ProfilePath mDmProfile = AidlUtils.buildProfilePathForDm(mDexPath);
+ private final DexMetadataPath mDmFile = AidlUtils.buildDexMetadataPath(mDexPath);
+ private final OutputProfile mPublicOutputProfile = AidlUtils.buildOutputProfileForPrimary(
+ PKG_NAME, "primary", Process.SYSTEM_UID, SHARED_GID, true /* isOtherReadable */);
+ private final OutputProfile mPrivateOutputProfile = AidlUtils.buildOutputProfileForPrimary(
+ PKG_NAME, "primary", Process.SYSTEM_UID, SHARED_GID, false /* isOtherReadable */);
+
+ private final String mSplit0DexPath = "/data/app/foo/split_0.apk";
+ private final ProfilePath mSplit0RefProfile =
+ AidlUtils.buildProfilePathForPrimaryRef(PKG_NAME, "split_0.split");
+
+ private final int mDefaultDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+ private final int mBetterOrSameDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.COMPILER_FILTER_IS_SAME
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+ private final int mForceDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE
+ | DexoptTrigger.COMPILER_FILTER_IS_SAME | DexoptTrigger.COMPILER_FILTER_IS_WORSE
+ | DexoptTrigger.NEED_EXTRACTION;
+
+ private final MergeProfileOptions mMergeProfileOptions = new MergeProfileOptions();
+
+ private final ArtdDexoptResult mArtdDexoptResult =
+ createArtdDexoptResult(false /* cancelled */);
+
+ private DexoptParams mDexoptParams =
+ new DexoptParams.Builder("install").setCompilerFilter("speed-profile").build();
+
+ private PrimaryDexopter mPrimaryDexopter;
+
+ private List<ProfilePath> mUsedProfiles;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // By default, none of the profiles are usable.
+ lenient().when(mArtd.isProfileUsable(any(), any())).thenReturn(false);
+ lenient().when(mArtd.copyAndRewriteProfile(any(), any(), any())).thenReturn(false);
+
+ // By default, no DM file exists.
+ lenient().when(mArtd.getDmFileVisibility(any())).thenReturn(FileVisibility.NOT_FOUND);
+
+ // Dexopt is by default needed and successful.
+ lenient()
+ .when(mArtd.getDexoptNeeded(any(), any(), any(), any(), anyInt()))
+ .thenReturn(dexoptIsNeeded());
+ lenient()
+ .when(mArtd.dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(),
+ any(), any()))
+ .thenReturn(mArtdDexoptResult);
+
+ lenient()
+ .when(mArtd.createCancellationSignal())
+ .thenReturn(mock(IArtdCancellationSignal.class));
+
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+
+ mUsedProfiles = new ArrayList<>();
+ }
+
+ @Test
+ public void testDexoptInputVdex() throws Exception {
+ // null.
+ doReturn(dexoptIsNeeded(ArtifactsLocation.NONE_OR_ERROR))
+ .when(mArtd)
+ .getDexoptNeeded(eq(mDexPath), eq("arm64"), any(), any(), anyInt());
+ doReturn(mArtdDexoptResult)
+ .when(mArtd)
+ .dexopt(any(), eq(mDexPath), eq("arm64"), any(), any(), any(), isNull(), any(),
+ anyInt(), any(), any());
+
+ // ArtifactsPath, isInDalvikCache=true.
+ doReturn(dexoptIsNeeded(ArtifactsLocation.DALVIK_CACHE))
+ .when(mArtd)
+ .getDexoptNeeded(eq(mDexPath), eq("arm"), any(), any(), anyInt());
+ doReturn(mArtdDexoptResult)
+ .when(mArtd)
+ .dexopt(any(), eq(mDexPath), eq("arm"), any(), any(), any(),
+ deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath(
+ mDexPath, "arm", true /* isInDalvikCache */))),
+ any(), anyInt(), any(), any());
+
+ // ArtifactsPath, isInDalvikCache=false.
+ doReturn(dexoptIsNeeded(ArtifactsLocation.NEXT_TO_DEX))
+ .when(mArtd)
+ .getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), any(), anyInt());
+ doReturn(mArtdDexoptResult)
+ .when(mArtd)
+ .dexopt(any(), eq(mSplit0DexPath), eq("arm64"), any(), any(), any(),
+ deepEq(VdexPath.artifactsPath(AidlUtils.buildArtifactsPath(
+ mSplit0DexPath, "arm64", false /* isInDalvikCache */))),
+ any(), anyInt(), any(), any());
+
+ // DexMetadataPath.
+ doReturn(dexoptIsNeeded(ArtifactsLocation.DM))
+ .when(mArtd)
+ .getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), any(), anyInt());
+ doReturn(mArtdDexoptResult)
+ .when(mArtd)
+ .dexopt(any(), eq(mSplit0DexPath), eq("arm"), any(), any(), any(), isNull(), any(),
+ anyInt(), any(), any());
+
+ mPrimaryDexopter.dexopt();
+ }
+
+ @Test
+ public void testDexoptDm() throws Exception {
+ lenient()
+ .when(mArtd.getDmFileVisibility(deepEq(mDmFile)))
+ .thenReturn(FileVisibility.OTHER_READABLE);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd, times(2))
+ .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), deepEq(mDmFile),
+ anyInt(),
+ argThat(dexoptOptions
+ -> dexoptOptions.compilationReason.equals("install-dm")),
+ any());
+ verify(mArtd, times(2))
+ .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), isNull(),
+ anyInt(),
+ argThat(dexoptOptions -> dexoptOptions.compilationReason.equals("install")),
+ any());
+ }
+
+ @Test
+ public void testDexoptUsesRefProfile() throws Exception {
+ makeProfileUsable(mRefProfile);
+ when(mArtd.getProfileVisibility(deepEq(mRefProfile)))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+
+ // Other profiles are also usable, but they shouldn't be used.
+ makeProfileUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", mRefProfile,
+ false /* isOtherReadable */, true /* generateAppImage */);
+
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", mRefProfile,
+ false /* isOtherReadable */, true /* generateAppImage */);
+
+ // There is no profile for split 0, so it should fall back to "verify".
+ verify(mArtd).getDexoptNeeded(
+ eq(mSplit0DexPath), eq("arm64"), any(), eq("verify"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "verify");
+
+ verify(mArtd).getDexoptNeeded(
+ eq(mSplit0DexPath), eq("arm"), any(), eq("verify"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "verify");
+
+ verifyProfileNotUsed(mPrebuiltProfile);
+ verifyProfileNotUsed(mDmProfile);
+ }
+
+ @Test
+ public void testDexoptUsesPublicRefProfile() throws Exception {
+ // The ref profile is usable and public.
+ makeProfileUsable(mRefProfile);
+ when(mArtd.getProfileVisibility(deepEq(mRefProfile)))
+ .thenReturn(FileVisibility.OTHER_READABLE);
+
+ // Other profiles are also usable, but they shouldn't be used.
+ makeProfileUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
+
+ mPrimaryDexopter.dexopt();
+
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", mRefProfile,
+ true /* isOtherReadable */, true /* generateAppImage */);
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", mRefProfile,
+ true /* isOtherReadable */, true /* generateAppImage */);
+
+ verifyProfileNotUsed(mPrebuiltProfile);
+ verifyProfileNotUsed(mDmProfile);
+ }
+
+ @Test
+ public void testDexoptUsesPrebuiltProfile() throws Exception {
+ makeProfileNotUsable(mRefProfile);
+ makeProfileUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
+
+ mPrimaryDexopter.dexopt();
+
+ InOrder inOrder = inOrder(mArtd);
+
+ inOrder.verify(mArtd).copyAndRewriteProfile(
+ deepEq(mPrebuiltProfile), deepEq(mPublicOutputProfile), eq(mDexPath));
+
+ checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm64",
+ ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath),
+ true /* isOtherReadable */, true /* generateAppImage */);
+ checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm",
+ ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath),
+ true /* isOtherReadable */, true /* generateAppImage */);
+
+ inOrder.verify(mArtd).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath));
+
+ verifyProfileNotUsed(mRefProfile);
+ verifyProfileNotUsed(mDmProfile);
+ }
+
+ @Test
+ public void testDexoptMergesProfiles() throws Exception {
+ when(mPkgState.getStateForUser(eq(UserHandle.of(0)))).thenReturn(mPkgUserStateInstalled);
+ when(mPkgState.getStateForUser(eq(UserHandle.of(2)))).thenReturn(mPkgUserStateInstalled);
+
+ when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(true);
+
+ makeProfileUsable(mRefProfile);
+ when(mArtd.getProfileVisibility(deepEq(mRefProfile)))
+ .thenReturn(FileVisibility.OTHER_READABLE);
+
+ mPrimaryDexopter.dexopt();
+
+ InOrder inOrder = inOrder(mArtd);
+
+ inOrder.verify(mArtd).mergeProfiles(
+ deepEq(List.of(AidlUtils.buildProfilePathForPrimaryCur(
+ 0 /* userId */, PKG_NAME, "primary"),
+ AidlUtils.buildProfilePathForPrimaryCur(
+ 2 /* userId */, PKG_NAME, "primary"))),
+ deepEq(mRefProfile), deepEq(mPrivateOutputProfile), deepEq(List.of(mDexPath)),
+ deepEq(mMergeProfileOptions));
+
+ // It should use `mBetterOrSameDexoptTrigger` and the merged profile for both ISAs.
+ inOrder.verify(mArtd).getDexoptNeeded(eq(mDexPath), eq("arm64"), any(), eq("speed-profile"),
+ eq(mBetterOrSameDexoptTrigger));
+ checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm64",
+ ProfilePath.tmpProfilePath(mPrivateOutputProfile.profilePath),
+ false /* isOtherReadable */, true /* generateAppImage */);
+
+ inOrder.verify(mArtd).getDexoptNeeded(eq(mDexPath), eq("arm"), any(), eq("speed-profile"),
+ eq(mBetterOrSameDexoptTrigger));
+ checkDexoptWithProfile(inOrder.verify(mArtd), mDexPath, "arm",
+ ProfilePath.tmpProfilePath(mPrivateOutputProfile.profilePath),
+ false /* isOtherReadable */, true /* generateAppImage */);
+
+ inOrder.verify(mArtd).commitTmpProfile(deepEq(mPrivateOutputProfile.profilePath));
+
+ inOrder.verify(mArtd).deleteProfile(deepEq(
+ AidlUtils.buildProfilePathForPrimaryCur(0 /* userId */, PKG_NAME, "primary")));
+ inOrder.verify(mArtd).deleteProfile(deepEq(
+ AidlUtils.buildProfilePathForPrimaryCur(2 /* userId */, PKG_NAME, "primary")));
+ }
+
+ @Test
+ public void testDexoptMergesProfilesMergeFailed() throws Exception {
+ when(mPkgState.getStateForUser(eq(UserHandle.of(0)))).thenReturn(mPkgUserStateInstalled);
+ when(mPkgState.getStateForUser(eq(UserHandle.of(2)))).thenReturn(mPkgUserStateInstalled);
+
+ when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(false);
+
+ makeProfileUsable(mRefProfile);
+ when(mArtd.getProfileVisibility(deepEq(mRefProfile)))
+ .thenReturn(FileVisibility.OTHER_READABLE);
+
+ mPrimaryDexopter.dexopt();
+
+ // It should still use "speed-profile", but with the existing reference profile only.
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64", mRefProfile,
+ true /* isOtherReadable */, true /* generateAppImage */);
+
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm", mRefProfile,
+ true /* isOtherReadable */, true /* generateAppImage */);
+
+ verify(mArtd, never()).deleteProfile(any());
+ verify(mArtd, never()).commitTmpProfile(any());
+ }
+
+ @Test
+ public void testDexoptUsesDmProfile() throws Exception {
+ makeProfileNotUsable(mRefProfile);
+ makeProfileNotUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd).copyAndRewriteProfile(
+ deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath));
+
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64",
+ ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath),
+ true /* isOtherReadable */, true /* generateAppImage */);
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm",
+ ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath),
+ true /* isOtherReadable */, true /* generateAppImage */);
+
+ verifyProfileNotUsed(mRefProfile);
+ verifyProfileNotUsed(mPrebuiltProfile);
+ }
+
+ @Test
+ public void testDexoptDeletesProfileOnFailure() throws Exception {
+ makeProfileNotUsable(mRefProfile);
+ makeProfileNotUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
+
+ when(mArtd.dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(),
+ any(), any()))
+ .thenThrow(ServiceSpecificException.class);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd).deleteProfile(
+ deepEq(ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath)));
+ verify(mArtd, never()).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath));
+ }
+
+ @Test
+ public void testDexoptNeedsToBeShared() throws Exception {
+ when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mDexPath)))
+ .thenReturn(true);
+ when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mSplit0DexPath)))
+ .thenReturn(true);
+
+ // The ref profile is usable but shouldn't be used.
+ makeProfileUsable(mRefProfile);
+
+ makeProfileNotUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
+
+ // The existing artifacts are private.
+ when(mArtd.getArtifactsVisibility(
+ argThat(artifactsPath -> artifactsPath.dexPath == mDexPath)))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd).copyAndRewriteProfile(
+ deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath));
+
+ // It should re-compile anyway.
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mForceDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm64",
+ ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath),
+ true /* isOtherReadable */, true /* generateAppImage */);
+
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mForceDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mDexPath, "arm",
+ ProfilePath.tmpProfilePath(mPublicOutputProfile.profilePath),
+ true /* isOtherReadable */, true /* generateAppImage */);
+
+ checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "speed");
+ checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "speed");
+
+ verifyProfileNotUsed(mRefProfile);
+ verifyProfileNotUsed(mPrebuiltProfile);
+ }
+
+ @Test
+ public void testDexoptNeedsToBeSharedArtifactsArePublic() throws Exception {
+ // Same setup as above, but the existing artifacts are public.
+ when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mDexPath)))
+ .thenReturn(true);
+ when(mDexUseManager.isPrimaryDexUsedByOtherApps(eq(PKG_NAME), eq(mSplit0DexPath)))
+ .thenReturn(true);
+
+ makeProfileUsable(mRefProfile);
+ makeProfileNotUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
+ when(mArtd.getArtifactsVisibility(
+ argThat(artifactsPath -> artifactsPath.dexPath == mDexPath)))
+ .thenReturn(FileVisibility.OTHER_READABLE);
+
+ mPrimaryDexopter.dexopt();
+
+ // It should use the default dexopt trigger.
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
+ verify(mArtd).getDexoptNeeded(
+ eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
+ }
+
+ @Test
+ public void testDexoptUsesProfileForSplit() throws Exception {
+ makeProfileUsable(mSplit0RefProfile);
+ when(mArtd.getProfileVisibility(deepEq(mSplit0RefProfile)))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), eq("speed-profile"),
+ eq(mDefaultDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mSplit0DexPath, "arm64", mSplit0RefProfile,
+ false /* isOtherReadable */, false /* generateAppImage */);
+
+ verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), eq("speed-profile"),
+ eq(mDefaultDexoptTrigger));
+ checkDexoptWithProfile(verify(mArtd), mSplit0DexPath, "arm", mSplit0RefProfile,
+ false /* isOtherReadable */, false /* generateAppImage */);
+ }
+
+ @Test
+ public void testDexoptCancelledBeforeDexopt() throws Exception {
+ mCancellationSignal.cancel();
+
+ var artdCancellationSignal = mock(IArtdCancellationSignal.class);
+ when(mArtd.createCancellationSignal()).thenReturn(artdCancellationSignal);
+
+ doAnswer(invocation -> {
+ verify(artdCancellationSignal).cancel();
+ return createArtdDexoptResult(true /* cancelled */);
+ })
+ .when(mArtd)
+ .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(),
+ same(artdCancellationSignal));
+
+ // The result should only contain one element: the result of the first file with
+ // DEXOPT_CANCELLED.
+ assertThat(mPrimaryDexopter.dexopt()
+ .stream()
+ .map(DexContainerFileDexoptResult::getStatus)
+ .collect(Collectors.toList()))
+ .containsExactly(DexoptResult.DEXOPT_CANCELLED);
+
+ // It shouldn't continue after being cancelled on the first file.
+ verify(mArtd, times(1)).createCancellationSignal();
+ verify(mArtd, times(1))
+ .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(),
+ any());
+ }
+
+ @Test
+ public void testDexoptCancelledDuringDexopt() throws Exception {
+ Semaphore dexoptStarted = new Semaphore(0);
+ Semaphore dexoptCancelled = new Semaphore(0);
+ final long TIMEOUT_SEC = 1;
+
+ var artdCancellationSignal = mock(IArtdCancellationSignal.class);
+ when(mArtd.createCancellationSignal()).thenReturn(artdCancellationSignal);
+
+ doAnswer(invocation -> {
+ dexoptStarted.release();
+ assertThat(dexoptCancelled.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
+ return createArtdDexoptResult(true /* cancelled */);
+ })
+ .when(mArtd)
+ .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(),
+ same(artdCancellationSignal));
+ doAnswer(invocation -> {
+ dexoptCancelled.release();
+ return null;
+ })
+ .when(artdCancellationSignal)
+ .cancel();
+
+ Future<List<DexContainerFileDexoptResult>> results =
+ ForkJoinPool.commonPool().submit(() -> { return mPrimaryDexopter.dexopt(); });
+
+ assertThat(dexoptStarted.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)).isTrue();
+
+ mCancellationSignal.cancel();
+
+ assertThat(results.get()
+ .stream()
+ .map(DexContainerFileDexoptResult::getStatus)
+ .collect(Collectors.toList()))
+ .containsExactly(DexoptResult.DEXOPT_CANCELLED);
+
+ // It shouldn't continue after being cancelled on the first file.
+ verify(mArtd, times(1)).createCancellationSignal();
+ verify(mArtd, times(1))
+ .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(),
+ any());
+ }
+
+ @Test
+ public void testDexoptBaseApk() throws Exception {
+ mDexoptParams =
+ new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName(null)
+ .build();
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd, times(2))
+ .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(),
+ any(), any());
+ verify(mArtd, never())
+ .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), any(),
+ anyInt(), any(), any());
+ }
+
+ @Test
+ public void testDexoptSplitApk() throws Exception {
+ mDexoptParams =
+ new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName("split_0")
+ .build();
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+
+ mPrimaryDexopter.dexopt();
+
+ verify(mArtd, never())
+ .dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), any(), anyInt(),
+ any(), any());
+ verify(mArtd, times(2))
+ .dexopt(any(), eq(mSplit0DexPath), any(), any(), any(), any(), any(), any(),
+ anyInt(), any(), any());
+ }
+
+ @Test
+ public void testDexoptStorageLow() throws Exception {
+ when(mStorageManager.getAllocatableBytes(any())).thenReturn(1l, 0l, 0l, 1l);
+
+ mDexoptParams =
+ new DexoptParams.Builder("install")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_SKIP_IF_STORAGE_LOW)
+ .build();
+ mPrimaryDexopter =
+ new PrimaryDexopter(mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+
+ List<DexContainerFileDexoptResult> results = mPrimaryDexopter.dexopt();
+ assertThat(results.get(0).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
+ assertThat(results.get(0).isSkippedDueToStorageLow()).isFalse();
+ assertThat(results.get(1).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
+ assertThat(results.get(1).isSkippedDueToStorageLow()).isTrue();
+ assertThat(results.get(2).getStatus()).isEqualTo(DexoptResult.DEXOPT_SKIPPED);
+ assertThat(results.get(2).isSkippedDueToStorageLow()).isTrue();
+ assertThat(results.get(3).getStatus()).isEqualTo(DexoptResult.DEXOPT_PERFORMED);
+ assertThat(results.get(3).isSkippedDueToStorageLow()).isFalse();
+
+ verify(mArtd, times(2))
+ .dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(), any(),
+ any());
+ }
+
+ private void checkDexoptWithProfile(IArtd artd, String dexPath, String isa, ProfilePath profile,
+ boolean isOtherReadable, boolean generateAppImage) throws Exception {
+ artd.dexopt(argThat(artifacts
+ -> artifacts.permissionSettings.fileFsPermission.isOtherReadable
+ == isOtherReadable),
+ eq(dexPath), eq(isa), any(), eq("speed-profile"), deepEq(profile), any(), any(),
+ anyInt(),
+ argThat(dexoptOptions -> dexoptOptions.generateAppImage == generateAppImage),
+ any());
+ }
+
+ private void checkDexoptWithNoProfile(
+ IArtd artd, String dexPath, String isa, String compilerFilter) throws Exception {
+ artd.dexopt(
+ argThat(artifacts
+ -> artifacts.permissionSettings.fileFsPermission.isOtherReadable == true),
+ eq(dexPath), eq(isa), any(), eq(compilerFilter), isNull(), any(), any(), anyInt(),
+ argThat(dexoptOptions -> dexoptOptions.generateAppImage == false), any());
+ }
+
+ private void verifyProfileNotUsed(ProfilePath profile) throws Exception {
+ assertThat(mUsedProfiles)
+ .comparingElementsUsing(TestingUtils.<ProfilePath>deepEquality())
+ .doesNotContain(profile);
+ }
+
+ private void makeProfileUsable(ProfilePath profile) throws Exception {
+ lenient().when(mArtd.isProfileUsable(deepEq(profile), any())).thenAnswer(invocation -> {
+ mUsedProfiles.add(invocation.<ProfilePath>getArgument(0));
+ return true;
+ });
+ lenient()
+ .when(mArtd.copyAndRewriteProfile(deepEq(profile), any(), any()))
+ .thenAnswer(invocation -> {
+ mUsedProfiles.add(invocation.<ProfilePath>getArgument(0));
+ return true;
+ });
+ }
+
+ private void makeProfileNotUsable(ProfilePath profile) throws Exception {
+ lenient().when(mArtd.isProfileUsable(deepEq(profile), any())).thenReturn(false);
+ lenient()
+ .when(mArtd.copyAndRewriteProfile(deepEq(profile), any(), any()))
+ .thenReturn(false);
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java
new file mode 100644
index 0000000..adb00ff
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexopterTestBase.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.GetDexoptNeededResult.ArtifactsLocation;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.same;
+
+import android.os.CancellationSignal;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+
+import com.android.modules.utils.pm.PackageStateModulesUtils;
+import com.android.server.art.testing.StaticMockitoRule;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageUserState;
+
+import dalvik.system.PathClassLoader;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PrimaryDexopterTestBase {
+ protected static final String PKG_NAME = "com.example.foo";
+ protected static final int UID = 12345;
+ protected static final int SHARED_GID = UserHandle.getSharedAppGid(UID);
+
+ @Rule
+ public StaticMockitoRule mockitoRule = new StaticMockitoRule(
+ SystemProperties.class, Constants.class, PackageStateModulesUtils.class);
+
+ @Mock protected PrimaryDexopter.Injector mInjector;
+ @Mock protected IArtd mArtd;
+ @Mock protected UserManager mUserManager;
+ @Mock protected DexUseManagerLocal mDexUseManager;
+ @Mock protected StorageManager mStorageManager;
+ protected PackageState mPkgState;
+ protected AndroidPackage mPkg;
+ protected PackageUserState mPkgUserStateNotInstalled;
+ protected PackageUserState mPkgUserStateInstalled;
+ protected CancellationSignal mCancellationSignal;
+
+ @Before
+ public void setUp() throws Exception {
+ lenient().when(mInjector.getArtd()).thenReturn(mArtd);
+ lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(false);
+ lenient().when(mInjector.getUserManager()).thenReturn(mUserManager);
+ lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager);
+ lenient().when(mInjector.getStorageManager()).thenReturn(mStorageManager);
+
+ lenient()
+ .when(SystemProperties.get("dalvik.vm.systemuicompilerfilter"))
+ .thenReturn("speed");
+ lenient()
+ .when(SystemProperties.getBoolean(eq("dalvik.vm.always_debuggable"), anyBoolean()))
+ .thenReturn(false);
+ lenient().when(SystemProperties.get("dalvik.vm.appimageformat")).thenReturn("lz4");
+ lenient().when(SystemProperties.get("pm.dexopt.shared")).thenReturn("speed");
+
+ // No ISA translation.
+ lenient()
+ .when(SystemProperties.get(argThat(arg -> arg.startsWith("ro.dalvik.vm.isa."))))
+ .thenReturn("");
+
+ lenient().when(Constants.getPreferredAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative64BitAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative32BitAbi()).thenReturn("armeabi-v7a");
+
+ lenient()
+ .when(mUserManager.getUserHandles(anyBoolean()))
+ .thenReturn(List.of(UserHandle.of(0), UserHandle.of(1), UserHandle.of(2)));
+
+ lenient().when(mDexUseManager.isPrimaryDexUsedByOtherApps(any(), any())).thenReturn(false);
+
+ lenient().when(mStorageManager.getAllocatableBytes(any())).thenReturn(1l);
+
+ mPkgUserStateNotInstalled = createPackageUserState(false /* installed */);
+ mPkgUserStateInstalled = createPackageUserState(true /* installed */);
+ mPkgState = createPackageState();
+ mPkg = mPkgState.getAndroidPackage();
+ mCancellationSignal = new CancellationSignal();
+ }
+
+ private AndroidPackage createPackage() {
+ // This package has the base APK and one split APK that has code.
+ AndroidPackage pkg = mock(AndroidPackage.class);
+ var baseSplit = mock(AndroidPackageSplit.class);
+ lenient().when(baseSplit.getPath()).thenReturn("/data/app/foo/base.apk");
+ lenient().when(baseSplit.isHasCode()).thenReturn(true);
+ lenient().when(baseSplit.getClassLoaderName()).thenReturn(PathClassLoader.class.getName());
+
+ var split0 = mock(AndroidPackageSplit.class);
+ lenient().when(split0.getName()).thenReturn("split_0");
+ lenient().when(split0.getPath()).thenReturn("/data/app/foo/split_0.apk");
+ lenient().when(split0.isHasCode()).thenReturn(true);
+
+ var split1 = mock(AndroidPackageSplit.class);
+ lenient().when(split1.getName()).thenReturn("split_1");
+ lenient().when(split1.getPath()).thenReturn("/data/app/foo/split_1.apk");
+ lenient().when(split1.isHasCode()).thenReturn(false);
+
+ var splits = List.of(baseSplit, split0, split1);
+ lenient().when(pkg.getSplits()).thenReturn(splits);
+
+ lenient().when(pkg.isVmSafeMode()).thenReturn(false);
+ lenient().when(pkg.isDebuggable()).thenReturn(false);
+ lenient().when(pkg.getTargetSdkVersion()).thenReturn(123);
+ lenient().when(pkg.isSignedWithPlatformKey()).thenReturn(false);
+ lenient().when(pkg.isUsesNonSdkApi()).thenReturn(false);
+ return pkg;
+ }
+
+ private PackageState createPackageState() {
+ PackageState pkgState = mock(PackageState.class);
+ lenient().when(pkgState.getPackageName()).thenReturn(PKG_NAME);
+ lenient().when(pkgState.getPrimaryCpuAbi()).thenReturn("arm64-v8a");
+ lenient().when(pkgState.getSecondaryCpuAbi()).thenReturn("armeabi-v7a");
+ lenient().when(pkgState.isSystem()).thenReturn(false);
+ lenient().when(pkgState.isUpdatedSystemApp()).thenReturn(false);
+ lenient().when(pkgState.getAppId()).thenReturn(UID);
+ lenient().when(pkgState.getSharedLibraryDependencies()).thenReturn(new ArrayList<>());
+ lenient().when(pkgState.getStateForUser(any())).thenReturn(mPkgUserStateNotInstalled);
+ AndroidPackage pkg = createPackage();
+ lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg);
+ lenient()
+ .when(PackageStateModulesUtils.isLoadableInOtherProcesses(
+ same(pkgState), anyBoolean()))
+ .thenReturn(false);
+ return pkgState;
+ }
+
+ private PackageUserState createPackageUserState(boolean isInstalled) {
+ PackageUserState pkgUserState = mock(PackageUserState.class);
+ lenient().when(pkgUserState.isInstalled()).thenReturn(isInstalled);
+ return pkgUserState;
+ }
+
+ protected GetDexoptNeededResult dexoptIsNotNeeded() {
+ var result = new GetDexoptNeededResult();
+ result.isDexoptNeeded = false;
+ return result;
+ }
+
+ protected GetDexoptNeededResult dexoptIsNeeded() {
+ return dexoptIsNeeded(ArtifactsLocation.NONE_OR_ERROR);
+ }
+
+ protected GetDexoptNeededResult dexoptIsNeeded(@ArtifactsLocation byte location) {
+ var result = new GetDexoptNeededResult();
+ result.isDexoptNeeded = true;
+ result.artifactsLocation = location;
+ if (location != ArtifactsLocation.NONE_OR_ERROR) {
+ result.isVdexUsable = true;
+ }
+ return result;
+ }
+
+ protected ArtdDexoptResult createArtdDexoptResult(boolean cancelled, long wallTimeMs,
+ long cpuTimeMs, long sizeBytes, long sizeBeforeBytes) {
+ var result = new ArtdDexoptResult();
+ result.cancelled = cancelled;
+ result.wallTimeMs = wallTimeMs;
+ result.cpuTimeMs = cpuTimeMs;
+ result.sizeBytes = sizeBytes;
+ result.sizeBeforeBytes = sizeBeforeBytes;
+ return result;
+ }
+
+ protected ArtdDexoptResult createArtdDexoptResult(boolean cancelled) {
+ return createArtdDexoptResult(cancelled, 0 /* wallTimeMs */, 0 /* cpuTimeMs */,
+ 0 /* sizeBytes */, 0 /* sizeBeforeBytes */);
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/ReasonMappingTest.java b/libartservice/service/javatests/com/android/server/art/ReasonMappingTest.java
new file mode 100644
index 0000000..55fd0b4
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/ReasonMappingTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+
+import android.os.SystemProperties;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.testing.StaticMockitoRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ReasonMappingTest {
+ @Rule public StaticMockitoRule mockitoRule = new StaticMockitoRule(SystemProperties.class);
+
+ @Test
+ public void testGetCompilerFilterForReason() {
+ when(SystemProperties.get("pm.dexopt.foo")).thenReturn("speed");
+ assertThat(ReasonMapping.getCompilerFilterForReason("foo")).isEqualTo("speed");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetCompilerFilterForReasonInvalidFilter() throws Exception {
+ when(SystemProperties.get("pm.dexopt.foo")).thenReturn("invalid-filter");
+ ReasonMapping.getCompilerFilterForReason("foo");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetCompilerFilterForReasonInvalidReason() throws Exception {
+ ReasonMapping.getCompilerFilterForReason("foo");
+ }
+
+ @Test
+ public void testGetCompilerFilterForShared() {
+ when(SystemProperties.get("pm.dexopt.shared")).thenReturn("speed");
+ assertThat(ReasonMapping.getCompilerFilterForShared()).isEqualTo("speed");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetCompilerFilterForSharedProfileGuidedFilter() throws Exception {
+ when(SystemProperties.get("pm.dexopt.shared")).thenReturn("speed-profile");
+ ReasonMapping.getCompilerFilterForShared();
+ }
+
+ @Test
+ public void testGetPriorityClassForReason() throws Exception {
+ assertThat(ReasonMapping.getPriorityClassForReason("install"))
+ .isEqualTo(ArtFlags.PRIORITY_INTERACTIVE);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetPriorityClassForReasonInvalidReason() throws Exception {
+ ReasonMapping.getPriorityClassForReason("foo");
+ }
+
+ @Test
+ public void testGetConcurrencyForReason() {
+ when(SystemProperties.getInt(eq("pm.dexopt.bg-dexopt.concurrency"), anyInt()))
+ .thenReturn(3);
+ assertThat(ReasonMapping.getConcurrencyForReason("bg-dexopt")).isEqualTo(3);
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java
new file mode 100644
index 0000000..d2ca3af
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo;
+import static com.android.server.art.GetDexoptNeededResult.ArtifactsLocation;
+import static com.android.server.art.OutputArtifacts.PermissionSettings;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.testing.TestingUtils.deepEq;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.CancellationSignal;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
+import com.android.server.art.testing.StaticMockitoRule;
+import com.android.server.art.testing.TestingUtils;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateUnserialized;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SecondaryDexopterTest {
+ private static final String PKG_NAME = "com.example.foo";
+ private static final int APP_ID = 12345;
+ private static final UserHandle USER_HANDLE = UserHandle.of(2);
+ private static final int UID = USER_HANDLE.getUid(APP_ID);
+ private static final String APP_DATA_DIR = "/data/user/2/" + PKG_NAME;
+ private static final String DEX_1 = APP_DATA_DIR + "/1.apk";
+ private static final String DEX_2 = APP_DATA_DIR + "/2.apk";
+ private static final String DEX_3 = APP_DATA_DIR + "/3.apk";
+
+ private final DexoptParams mDexoptParams =
+ new DexoptParams.Builder("bg-dexopt")
+ .setCompilerFilter("speed-profile")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX)
+ .build();
+
+ private final ProfilePath mDex1RefProfile = AidlUtils.buildProfilePathForSecondaryRef(DEX_1);
+ private final ProfilePath mDex1CurProfile = AidlUtils.buildProfilePathForSecondaryCur(DEX_1);
+ private final ProfilePath mDex2RefProfile = AidlUtils.buildProfilePathForSecondaryRef(DEX_2);
+ private final ProfilePath mDex3RefProfile = AidlUtils.buildProfilePathForSecondaryRef(DEX_3);
+ private final OutputProfile mDex1PrivateOutputProfile =
+ AidlUtils.buildOutputProfileForSecondary(DEX_1, UID, UID, false /* isOtherReadable */);
+
+ private final int mDefaultDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+ private final int mBetterOrSameDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
+ | DexoptTrigger.COMPILER_FILTER_IS_SAME
+ | DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE | DexoptTrigger.NEED_EXTRACTION;
+
+ private final MergeProfileOptions mMergeProfileOptions = new MergeProfileOptions();
+
+ @Rule
+ public StaticMockitoRule mockitoRule =
+ new StaticMockitoRule(SystemProperties.class, Constants.class);
+
+ @Mock private SecondaryDexopter.Injector mInjector;
+ @Mock private IArtd mArtd;
+ @Mock private DexUseManagerLocal mDexUseManager;
+ private PackageState mPkgState;
+ private AndroidPackage mPkg;
+ private CancellationSignal mCancellationSignal;
+
+ private SecondaryDexopter mSecondaryDexopter;
+
+ @Before
+ public void setUp() throws Exception {
+ lenient()
+ .when(SystemProperties.getBoolean(eq("dalvik.vm.always_debuggable"), anyBoolean()))
+ .thenReturn(false);
+ lenient().when(SystemProperties.get("dalvik.vm.appimageformat")).thenReturn("lz4");
+ lenient().when(SystemProperties.get("pm.dexopt.shared")).thenReturn("speed");
+
+ // No ISA translation.
+ lenient()
+ .when(SystemProperties.get(argThat(arg -> arg.startsWith("ro.dalvik.vm.isa."))))
+ .thenReturn("");
+
+ lenient().when(Constants.getPreferredAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative64BitAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative32BitAbi()).thenReturn("armeabi-v7a");
+
+ lenient().when(mInjector.getArtd()).thenReturn(mArtd);
+ lenient().when(mInjector.isSystemUiPackage(any())).thenReturn(false);
+ lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager);
+
+ List<DetailedSecondaryDexInfo> secondaryDexInfo = createSecondaryDexInfo();
+ lenient()
+ .when(mDexUseManager.getFilteredDetailedSecondaryDexInfo(eq(PKG_NAME)))
+ .thenReturn(secondaryDexInfo);
+
+ mPkgState = createPackageState();
+ mPkg = mPkgState.getAndroidPackage();
+ mCancellationSignal = new CancellationSignal();
+
+ prepareProfiles();
+
+ // Dexopt is always needed and successful.
+ lenient()
+ .when(mArtd.getDexoptNeeded(any(), any(), any(), any(), anyInt()))
+ .thenReturn(dexoptIsNeeded());
+ lenient()
+ .when(mArtd.dexopt(any(), any(), any(), any(), any(), any(), any(), any(), anyInt(),
+ any(), any()))
+ .thenReturn(createArtdDexoptResult());
+
+ lenient()
+ .when(mArtd.createCancellationSignal())
+ .thenReturn(mock(IArtdCancellationSignal.class));
+
+ mSecondaryDexopter = new SecondaryDexopter(
+ mInjector, mPkgState, mPkg, mDexoptParams, mCancellationSignal);
+ }
+
+ @Test
+ public void testDexopt() throws Exception {
+ assertThat(mSecondaryDexopter.dexopt())
+ .comparingElementsUsing(TestingUtils.<DexContainerFileDexoptResult>deepEquality())
+ .containsExactly(
+ DexContainerFileDexoptResult.create(DEX_1, true /* isPrimaryAbi */,
+ "arm64-v8a", "speed-profile", DexoptResult.DEXOPT_PERFORMED,
+ 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */,
+ 0 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */),
+ DexContainerFileDexoptResult.create(DEX_2, true /* isPrimaryAbi */,
+ "arm64-v8a", "speed", DexoptResult.DEXOPT_PERFORMED,
+ 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */,
+ 0 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */),
+ DexContainerFileDexoptResult.create(DEX_2, false /* isPrimaryAbi */,
+ "armeabi-v7a", "speed", DexoptResult.DEXOPT_PERFORMED,
+ 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */,
+ 0 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */),
+ DexContainerFileDexoptResult.create(DEX_3, true /* isPrimaryAbi */,
+ "arm64-v8a", "verify", DexoptResult.DEXOPT_PERFORMED,
+ 0 /* dex2oatWallTimeMillis */, 0 /* dex2oatCpuTimeMillis */,
+ 0 /* sizeBytes */, 0 /* sizeBeforeBytes */,
+ false /* isSkippedDueToStorageLow */));
+
+ // It should use profile for dex 1.
+
+ verify(mArtd).mergeProfiles(deepEq(List.of(mDex1CurProfile)), deepEq(mDex1RefProfile),
+ deepEq(mDex1PrivateOutputProfile), deepEq(List.of(DEX_1)),
+ deepEq(mMergeProfileOptions));
+
+ verify(mArtd).getDexoptNeeded(
+ eq(DEX_1), eq("arm64"), any(), eq("speed-profile"), eq(mBetterOrSameDexoptTrigger));
+ checkDexoptWithPrivateProfile(verify(mArtd), DEX_1, "arm64",
+ ProfilePath.tmpProfilePath(mDex1PrivateOutputProfile.profilePath), "CLC_FOR_DEX_1");
+
+ verify(mArtd).commitTmpProfile(deepEq(mDex1PrivateOutputProfile.profilePath));
+
+ verify(mArtd).deleteProfile(deepEq(mDex1CurProfile));
+
+ // It should use "speed" for dex 2 for both ISAs and make the artifacts public.
+
+ verify(mArtd, never()).isProfileUsable(deepEq(mDex2RefProfile), any());
+ verify(mArtd, never()).mergeProfiles(any(), deepEq(mDex2RefProfile), any(), any(), any());
+
+ verify(mArtd).getDexoptNeeded(
+ eq(DEX_2), eq("arm64"), any(), eq("speed"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithNoProfile(
+ verify(mArtd), DEX_2, "arm64", "speed", "CLC_FOR_DEX_2", true /* isPublic */);
+
+ verify(mArtd).getDexoptNeeded(
+ eq(DEX_2), eq("arm"), any(), eq("speed"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithNoProfile(
+ verify(mArtd), DEX_2, "arm", "speed", "CLC_FOR_DEX_2", true /* isPublic */);
+
+ // It should use "verify" for dex 3 and make the artifacts private.
+
+ verify(mArtd, never()).isProfileUsable(deepEq(mDex3RefProfile), any());
+ verify(mArtd, never()).mergeProfiles(any(), deepEq(mDex3RefProfile), any(), any(), any());
+
+ verify(mArtd).getDexoptNeeded(
+ eq(DEX_3), eq("arm64"), isNull(), eq("verify"), eq(mDefaultDexoptTrigger));
+ checkDexoptWithNoProfile(verify(mArtd), DEX_3, "arm64", "verify",
+ null /* classLoaderContext */, false /* isPublic */);
+ }
+
+ private AndroidPackage createPackage() {
+ var pkg = mock(AndroidPackage.class);
+ lenient().when(pkg.isVmSafeMode()).thenReturn(false);
+ lenient().when(pkg.isDebuggable()).thenReturn(false);
+ lenient().when(pkg.getTargetSdkVersion()).thenReturn(123);
+ lenient().when(pkg.isSignedWithPlatformKey()).thenReturn(false);
+ lenient().when(pkg.isUsesNonSdkApi()).thenReturn(false);
+ return pkg;
+ }
+
+ private PackageState createPackageState() {
+ var pkgState = mock(PackageState.class);
+ lenient().when(pkgState.getPackageName()).thenReturn(PKG_NAME);
+ lenient().when(pkgState.getPrimaryCpuAbi()).thenReturn("arm64-v8a");
+ lenient().when(pkgState.getSecondaryCpuAbi()).thenReturn("armeabi-v7a");
+ lenient().when(pkgState.getAppId()).thenReturn(APP_ID);
+ lenient().when(pkgState.getSeInfo()).thenReturn("se-info");
+ AndroidPackage pkg = createPackage();
+ lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg);
+ return pkgState;
+ }
+
+ private List<DetailedSecondaryDexInfo> createSecondaryDexInfo() throws Exception {
+ // This should be compiled with profile.
+ var dex1Info = mock(DetailedSecondaryDexInfo.class);
+ lenient().when(dex1Info.dexPath()).thenReturn(DEX_1);
+ lenient().when(dex1Info.userHandle()).thenReturn(USER_HANDLE);
+ lenient().when(dex1Info.classLoaderContext()).thenReturn("CLC_FOR_DEX_1");
+ lenient().when(dex1Info.abiNames()).thenReturn(Set.of("arm64-v8a"));
+ lenient().when(dex1Info.isUsedByOtherApps()).thenReturn(false);
+ lenient().when(dex1Info.isDexFilePublic()).thenReturn(true);
+
+ // This should be compiled without profile because it's used by other apps.
+ var dex2Info = mock(DetailedSecondaryDexInfo.class);
+ lenient().when(dex2Info.dexPath()).thenReturn(DEX_2);
+ lenient().when(dex2Info.userHandle()).thenReturn(USER_HANDLE);
+ lenient().when(dex2Info.classLoaderContext()).thenReturn("CLC_FOR_DEX_2");
+ lenient().when(dex2Info.abiNames()).thenReturn(Set.of("arm64-v8a", "armeabi-v7a"));
+ lenient().when(dex2Info.isUsedByOtherApps()).thenReturn(true);
+ lenient().when(dex2Info.isDexFilePublic()).thenReturn(true);
+
+ // This should be compiled with verify because the class loader context is invalid.
+ var dex3Info = mock(DetailedSecondaryDexInfo.class);
+ lenient().when(dex3Info.dexPath()).thenReturn(DEX_3);
+ lenient().when(dex3Info.userHandle()).thenReturn(USER_HANDLE);
+ lenient().when(dex3Info.classLoaderContext()).thenReturn(null);
+ lenient().when(dex3Info.abiNames()).thenReturn(Set.of("arm64-v8a"));
+ lenient().when(dex3Info.isUsedByOtherApps()).thenReturn(false);
+ lenient().when(dex3Info.isDexFilePublic()).thenReturn(false);
+
+ return List.of(dex1Info, dex2Info, dex3Info);
+ }
+
+ private void prepareProfiles() throws Exception {
+ // Profile for dex file 1 is usable.
+ lenient().when(mArtd.isProfileUsable(deepEq(mDex1RefProfile), any())).thenReturn(true);
+ lenient()
+ .when(mArtd.getProfileVisibility(deepEq(mDex1RefProfile)))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+
+ // Profiles for dex file 2 and 3 are also usable, but shouldn't be used.
+ lenient().when(mArtd.isProfileUsable(deepEq(mDex2RefProfile), any())).thenReturn(true);
+ lenient()
+ .when(mArtd.getProfileVisibility(deepEq(mDex2RefProfile)))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+ lenient().when(mArtd.isProfileUsable(deepEq(mDex3RefProfile), any())).thenReturn(true);
+ lenient()
+ .when(mArtd.getProfileVisibility(deepEq(mDex3RefProfile)))
+ .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+
+ lenient().when(mArtd.mergeProfiles(any(), any(), any(), any(), any())).thenReturn(true);
+ }
+
+ private GetDexoptNeededResult dexoptIsNeeded() {
+ var result = new GetDexoptNeededResult();
+ result.isDexoptNeeded = true;
+ result.artifactsLocation = ArtifactsLocation.NONE_OR_ERROR;
+ result.isVdexUsable = false;
+ return result;
+ }
+
+ private ArtdDexoptResult createArtdDexoptResult() {
+ var result = new ArtdDexoptResult();
+ result.cancelled = false;
+ result.wallTimeMs = 0;
+ result.cpuTimeMs = 0;
+ result.sizeBytes = 0;
+ result.sizeBeforeBytes = 0;
+ return result;
+ }
+
+ private void checkDexoptWithPrivateProfile(IArtd artd, String dexPath, String isa,
+ ProfilePath profile, String classLoaderContext) throws Exception {
+ PermissionSettings permissionSettings = buildPermissionSettings(false /* isPublic */);
+ OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(
+ dexPath, isa, false /* isInDalvikCache */, permissionSettings);
+ artd.dexopt(deepEq(outputArtifacts), eq(dexPath), eq(isa), eq(classLoaderContext),
+ eq("speed-profile"), deepEq(profile), any(), isNull() /* dmFile */, anyInt(),
+ argThat(dexoptOptions -> dexoptOptions.generateAppImage == false), any());
+ }
+
+ private void checkDexoptWithNoProfile(IArtd artd, String dexPath, String isa,
+ String compilerFilter, String classLoaderContext, boolean isPublic) throws Exception {
+ PermissionSettings permissionSettings = buildPermissionSettings(isPublic);
+ OutputArtifacts outputArtifacts = AidlUtils.buildOutputArtifacts(
+ dexPath, isa, false /* isInDalvikCache */, permissionSettings);
+ artd.dexopt(deepEq(outputArtifacts), eq(dexPath), eq(isa), eq(classLoaderContext),
+ eq(compilerFilter), isNull(), any(), isNull() /* dmFile */, anyInt(),
+ argThat(dexoptOptions -> dexoptOptions.generateAppImage == false), any());
+ }
+
+ private PermissionSettings buildPermissionSettings(boolean isPublic) {
+ FsPermission dirFsPermission = AidlUtils.buildFsPermission(UID /* uid */, UID /* gid */,
+ false /* isOtherReadable */, true /* isOtherExecutable */);
+ FsPermission fileFsPermission =
+ AidlUtils.buildFsPermission(UID /* uid */, UID /* gid */, isPublic);
+ return AidlUtils.buildPermissionSettings(
+ dirFsPermission, fileFsPermission, AidlUtils.buildSeContext("se-info", UID));
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/UtilsTest.java b/libartservice/service/javatests/com/android/server/art/UtilsTest.java
new file mode 100644
index 0000000..ff09dc4
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/UtilsTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.SystemProperties;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.art.Utils;
+import com.android.server.art.testing.StaticMockitoRule;
+import com.android.server.pm.pkg.PackageState;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ForkJoinPool;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UtilsTest {
+ @Rule
+ public StaticMockitoRule mockitoRule =
+ new StaticMockitoRule(SystemProperties.class, Constants.class);
+
+ @Before
+ public void setUp() throws Exception {
+ lenient().when(SystemProperties.get(eq("ro.dalvik.vm.isa.x86_64"))).thenReturn("arm64");
+ lenient().when(SystemProperties.get(eq("ro.dalvik.vm.isa.x86"))).thenReturn("arm");
+
+ // In reality, the preferred ABI should be either the native 64 bit ABI or the native 32 bit
+ // ABI, but we use a different value here to make sure the value is used only if expected.
+ lenient().when(Constants.getPreferredAbi()).thenReturn("x86");
+ lenient().when(Constants.getNative64BitAbi()).thenReturn("arm64-v8a");
+ lenient().when(Constants.getNative32BitAbi()).thenReturn("armeabi-v7a");
+ }
+
+ @Test
+ public void testCollectionIsEmptyTrue() {
+ assertThat(Utils.isEmpty(List.of())).isTrue();
+ }
+
+ @Test
+ public void testCollectionIsEmptyFalse() {
+ assertThat(Utils.isEmpty(List.of(1))).isFalse();
+ }
+
+ @Test
+ public void testSparseArrayIsEmptyTrue() {
+ assertThat(Utils.isEmpty(new SparseArray<Integer>())).isTrue();
+ }
+
+ @Test
+ public void testSparseArrayIsEmptyFalse() {
+ SparseArray<Integer> array = new SparseArray<>();
+ array.put(1, 1);
+ assertThat(Utils.isEmpty(array)).isFalse();
+ }
+
+ @Test
+ public void testArrayIsEmptyTrue() {
+ assertThat(Utils.isEmpty(new int[0])).isTrue();
+ }
+
+ @Test
+ public void testArrayIsEmptyFalse() {
+ assertThat(Utils.isEmpty(new int[] {1})).isFalse();
+ }
+
+ @Test
+ public void testGetAllAbis() {
+ var pkgState = mock(PackageState.class);
+ when(pkgState.getPrimaryCpuAbi()).thenReturn("armeabi-v7a");
+ when(pkgState.getSecondaryCpuAbi()).thenReturn("arm64-v8a");
+
+ assertThat(Utils.getAllAbis(pkgState))
+ .containsExactly(Utils.Abi.create("armeabi-v7a", "arm", true /* isPrimaryAbi */),
+ Utils.Abi.create("arm64-v8a", "arm64", false /* isPrimaryAbi */));
+ }
+
+ @Test
+ public void testGetAllAbisTranslated() {
+ var pkgState = mock(PackageState.class);
+ when(pkgState.getPrimaryCpuAbi()).thenReturn("x86_64");
+ when(pkgState.getSecondaryCpuAbi()).thenReturn("x86");
+
+ assertThat(Utils.getAllAbis(pkgState))
+ .containsExactly(Utils.Abi.create("arm64-v8a", "arm64", true /* isPrimaryAbi */),
+ Utils.Abi.create("armeabi-v7a", "arm", false /* isPrimaryAbi */));
+ }
+
+ @Test
+ public void testGetAllAbisPrimaryOnly() {
+ var pkgState = mock(PackageState.class);
+ when(pkgState.getPrimaryCpuAbi()).thenReturn("armeabi-v7a");
+ when(pkgState.getSecondaryCpuAbi()).thenReturn(null);
+
+ assertThat(Utils.getAllAbis(pkgState))
+ .containsExactly(Utils.Abi.create("armeabi-v7a", "arm", true /* isPrimaryAbi */));
+ }
+
+ @Test
+ public void testGetAllAbisNone() {
+ var pkgState = mock(PackageState.class);
+ when(pkgState.getPrimaryCpuAbi()).thenReturn(null);
+ when(pkgState.getSecondaryCpuAbi()).thenReturn(null);
+
+ assertThat(Utils.getAllAbis(pkgState))
+ .containsExactly(Utils.Abi.create("x86", "x86", true /* isPrimaryAbi */));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetAllAbisInvalidNativeIsa() {
+ lenient().when(SystemProperties.get(eq("ro.dalvik.vm.isa.x86_64"))).thenReturn("x86");
+
+ var pkgState = mock(PackageState.class);
+ when(pkgState.getPrimaryCpuAbi()).thenReturn("x86_64");
+ when(pkgState.getSecondaryCpuAbi()).thenReturn(null);
+
+ Utils.getAllAbis(pkgState);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetAllAbisUnsupportedTranslation() {
+ lenient().when(SystemProperties.get(eq("ro.dalvik.vm.isa.x86_64"))).thenReturn("");
+
+ var pkgState = mock(PackageState.class);
+ when(pkgState.getPrimaryCpuAbi()).thenReturn("x86_64");
+ when(pkgState.getSecondaryCpuAbi()).thenReturn(null);
+
+ Utils.getAllAbis(pkgState);
+ }
+
+ @Test
+ public void testImplies() {
+ assertThat(Utils.implies(false, false)).isTrue();
+ assertThat(Utils.implies(false, true)).isTrue();
+ assertThat(Utils.implies(true, false)).isFalse();
+ assertThat(Utils.implies(true, true)).isTrue();
+ }
+
+ @Test
+ public void testCheck() {
+ Utils.check(true);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCheckFailed() throws Exception {
+ Utils.check(false);
+ }
+
+ @Test
+ public void testExecuteAndWait() {
+ Executor executor = ForkJoinPool.commonPool();
+ List<String> results = new ArrayList<>();
+ Utils.executeAndWait(executor, () -> {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ results.add("foo");
+ });
+ assertThat(results).containsExactly("foo");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testExecuteAndWaitPropagatesException() {
+ Executor executor = ForkJoinPool.commonPool();
+ Utils.executeAndWait(executor, () -> { throw new IllegalArgumentException(); });
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java b/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
new file mode 100644
index 0000000..aeff58c
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/model/DexoptParamsTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.model;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DexoptParamsTest {
+ @Test
+ public void testBuild() {
+ new DexoptParams.Builder("install").build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuildEmptyReason() {
+ new DexoptParams.Builder("").build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuildInvalidCompilerFilter() {
+ new DexoptParams.Builder("install").setCompilerFilter("invalid").build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuildInvalidPriorityClass() {
+ new DexoptParams.Builder("install").setPriorityClass(101).build();
+ }
+
+ @Test
+ public void testBuildCustomReason() {
+ new DexoptParams.Builder("custom").setCompilerFilter("speed").setPriorityClass(90).build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuildCustomReasonEmptyCompilerFilter() {
+ new DexoptParams.Builder("custom").setPriorityClass(ArtFlags.PRIORITY_INTERACTIVE).build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBuildCustomReasonEmptyPriorityClass() {
+ new DexoptParams.Builder("custom").setCompilerFilter("speed").build();
+ }
+
+ @Test
+ public void testSingleSplit() {
+ new DexoptParams.Builder("install")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName("split_0")
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSingleSplitNoPrimaryFlag() {
+ new DexoptParams.Builder("install")
+ .setFlags(ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName("split_0")
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSingleSplitSecondaryFlag() {
+ new DexoptParams.Builder("install")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_FOR_SECONDARY_DEX
+ | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName("split_0")
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSingleSplitDependenciesFlag() {
+ new DexoptParams.Builder("install")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES
+ | ArtFlags.FLAG_FOR_SINGLE_SPLIT)
+ .setSplitName("split_0")
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSplitNameNoSingleSplitFlag() {
+ new DexoptParams.Builder("install")
+ .setFlags(ArtFlags.FLAG_FOR_PRIMARY_DEX)
+ .setSplitName("split_0")
+ .build();
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/testing/MockClock.java b/libartservice/service/javatests/com/android/server/art/testing/MockClock.java
new file mode 100644
index 0000000..7b4b23b
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/testing/MockClock.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.testing;
+
+import android.annotation.NonNull;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.concurrent.RunnableScheduledFuture;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class MockClock {
+ private long mCurrentTimeMs = 0;
+ @NonNull private List<ScheduledExecutor> mExecutors = new ArrayList<>();
+
+ @NonNull
+ public ScheduledExecutor createScheduledExecutor() {
+ var executor = new ScheduledExecutor();
+ mExecutors.add(executor);
+ return executor;
+ }
+
+ public long getCurrentTimeMs() {
+ return mCurrentTimeMs;
+ }
+
+ public void advanceTime(long timeMs) {
+ mCurrentTimeMs += timeMs;
+ for (ScheduledExecutor executor : mExecutors) {
+ executor.notifyUpdate();
+ }
+ }
+
+ public class ScheduledExecutor extends ScheduledThreadPoolExecutor {
+ // The second element of the pair is the scheduled time.
+ @NonNull
+ private PriorityQueue<Pair<RunnableScheduledFuture<?>, Long>> tasks = new PriorityQueue<>(
+ 1 /* initialCapacity */, Comparator.comparingLong(pair -> pair.second));
+
+ public ScheduledExecutor() {
+ super(1 /* corePoolSize */);
+ }
+
+ @NonNull
+ public ScheduledFuture<?> schedule(
+ @NonNull Runnable command, long delay, @NonNull TimeUnit unit) {
+ // Use `Long.MAX_VALUE` to prevent the task from being automatically run.
+ var task = (RunnableScheduledFuture<?>) super.schedule(
+ command, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+ tasks.add(Pair.create(task, getCurrentTimeMs() + unit.toMillis(delay)));
+ return task;
+ }
+
+ public void notifyUpdate() {
+ while (!tasks.isEmpty()) {
+ Pair<RunnableScheduledFuture<?>, Long> pair = tasks.peek();
+ RunnableScheduledFuture<?> task = pair.first;
+ long scheduledTimeMs = pair.second;
+ if (getCurrentTimeMs() >= scheduledTimeMs) {
+ if (!task.isDone() && !task.isCancelled()) {
+ task.run();
+ }
+ tasks.poll();
+ // Remove the task from the queue of the executor. Terminate the executor if
+ // it's shutdown and the queue is empty.
+ super.remove(task);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/testing/OnSuccessRule.java b/libartservice/service/javatests/com/android/server/art/testing/OnSuccessRule.java
new file mode 100644
index 0000000..350a8cb
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/testing/OnSuccessRule.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.testing;
+
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+/** A JUnit rule that invokes a runnable on success. */
+public class OnSuccessRule implements MethodRule {
+ private RunnableThrowingException mRunnable;
+
+ public OnSuccessRule(RunnableThrowingException runnable) {
+ mRunnable = runnable;
+ }
+
+ @Override
+ public Statement apply(Statement base, FrameworkMethod method, Object target) {
+ return new Statement() {
+ public void evaluate() throws Throwable {
+ base.evaluate();
+ mRunnable.run();
+ }
+ };
+ }
+
+ public interface RunnableThrowingException {
+ void run() throws Exception;
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/testing/StaticMockitoRule.java b/libartservice/service/javatests/com/android/server/art/testing/StaticMockitoRule.java
new file mode 100644
index 0000000..595370b
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/testing/StaticMockitoRule.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.testing;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+/**
+ * Similar to {@link MockitoRule}, but uses {@StaticMockitoSession}, which allows mocking static
+ * methods.
+ */
+public class StaticMockitoRule implements MethodRule {
+ private Class<?>[] mClasses;
+
+ public StaticMockitoRule(Class<?>... classes) {
+ mClasses = classes;
+ }
+
+ @Override
+ public Statement apply(Statement base, FrameworkMethod method, Object target) {
+ return new Statement() {
+ public void evaluate() throws Throwable {
+ StaticMockitoSessionBuilder builder =
+ mockitoSession()
+ .name(target.getClass().getSimpleName() + "." + method.getName())
+ .initMocks(target)
+ .strictness(Strictness.STRICT_STUBS);
+
+ for (Class<?> clazz : mClasses) {
+ builder.mockStatic(clazz);
+ }
+
+ StaticMockitoSession session = builder.startMocking();
+ Throwable testFailure = evaluateSafely(base);
+ session.finishMocking(testFailure);
+ if (testFailure != null) {
+ throw testFailure;
+ }
+ }
+
+ private Throwable evaluateSafely(Statement base) {
+ try {
+ base.evaluate();
+ return null;
+ } catch (Throwable throwable) {
+ return throwable;
+ }
+ }
+ };
+ }
+}
diff --git a/libartservice/service/javatests/com/android/server/art/testing/TestingUtils.java b/libartservice/service/javatests/com/android/server/art/testing/TestingUtils.java
new file mode 100644
index 0000000..36f1ce4
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/testing/TestingUtils.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.testing;
+
+import static org.mockito.Mockito.argThat;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.google.common.truth.Correspondence;
+import com.google.common.truth.Truth;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+public final class TestingUtils {
+ private static final String TAG = "TestingUtils";
+
+ private TestingUtils() {}
+
+ /**
+ * Recursively compares two objects using reflection. Returns true if the two objects are equal.
+ * For simplicity, this method only supports types that every field is a primitive type, a
+ * string, a {@link List}, or a supported type.
+ */
+ public static boolean deepEquals(
+ @Nullable Object a, @Nullable Object b, @NonNull StringBuilder errorMsg) {
+ try {
+ if (a == null && b == null) {
+ return true;
+ }
+ if (a == null || b == null) {
+ errorMsg.append(String.format("Nullability mismatch: %s != %s",
+ a == null ? "null" : "nonnull", b == null ? "null" : "nonnull"));
+ return false;
+ }
+ if (a instanceof List && b instanceof List) {
+ return listDeepEquals((List<?>) a, (List<?>) b, errorMsg);
+ }
+ if (a.getClass() != b.getClass()) {
+ errorMsg.append(
+ String.format("Type mismatch: %s != %s", a.getClass(), b.getClass()));
+ return false;
+ }
+ if (a.getClass() == String.class) {
+ if (!a.equals(b)) {
+ errorMsg.append(String.format("%s != %s", a, b));
+ }
+ return a.equals(b);
+ }
+ if (a.getClass().isArray()) {
+ throw new UnsupportedOperationException("Array type is not supported");
+ }
+ for (Field field : a.getClass().getDeclaredFields()) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ continue;
+ }
+ field.setAccessible(true);
+ if (field.getType().isPrimitive()) {
+ if (!field.get(a).equals(field.get(b))) {
+ errorMsg.append(String.format("Field %s mismatch: %s != %s",
+ field.getName(), field.get(a), field.get(b)));
+ return false;
+ }
+ } else if (!deepEquals(field.get(a), field.get(b), errorMsg)) {
+ errorMsg.insert(0, String.format("Field %s mismatch: ", field.getName()));
+ return false;
+ }
+ }
+ return true;
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Same as above, but ignores any error message. */
+ public static boolean deepEquals(@Nullable Object a, @Nullable Object b) {
+ var errorMsgIgnored = new StringBuilder();
+ return deepEquals(a, b, errorMsgIgnored);
+ }
+
+ /**
+ * A Mockito argument matcher that uses {@link #deepEquals} to compare objects and logs any
+ * mismatch.
+ */
+ public static <T> T deepEq(@Nullable T expected) {
+ return argThat(arg -> {
+ var errorMsg = new StringBuilder();
+ boolean result = deepEquals(arg, expected, errorMsg);
+ if (!result) {
+ Log.e(TAG, errorMsg.toString());
+ }
+ return result;
+ });
+ }
+
+ /**
+ * A Mockito argument matcher that matches a list containing expected in any order.
+ */
+ @SafeVarargs
+ public static <ListType extends List<ItemType>, ItemType> ListType inAnyOrder(
+ @Nullable ItemType... expected) {
+ return argThat(argument -> {
+ try {
+ Truth.assertThat(argument).containsExactlyElementsIn(expected);
+ return true;
+ } catch (AssertionError error) {
+ return false;
+ }
+ });
+ }
+
+ /**
+ * {@link #inAnyOrder(Object[])} but using {@link #deepEquals(Object, Object)}} for comparisons.
+ *
+ * @see #inAnyOrder(Object[])
+ */
+ @SafeVarargs
+ public static <ListType extends List<ItemType>, ItemType> ListType inAnyOrderDeepEquals(
+ @Nullable ItemType... expected) {
+ return argThat(argument -> {
+ try {
+ Truth.assertThat(argument)
+ .comparingElementsUsing(deepEquality())
+ .containsExactlyElementsIn(expected);
+ return true;
+ } catch (AssertionError error) {
+ return false;
+ }
+ });
+ }
+
+ /**
+ * A Truth correspondence that uses {@link #deepEquals} to compare objects and reports any
+ * mismatch.
+ */
+ public static <T> Correspondence<T, T> deepEquality() {
+ return Correspondence.<T, T>from(TestingUtils::deepEquals, "deeply equals")
+ .formattingDiffsUsing((actual, expected) -> {
+ var errorMsg = new StringBuilder();
+ deepEquals(actual, expected, errorMsg);
+ return errorMsg.toString();
+ });
+ }
+
+ private static boolean listDeepEquals(
+ @NonNull List<?> a, @NonNull List<?> b, @NonNull StringBuilder errorMsg) {
+ if (a.size() != b.size()) {
+ errorMsg.append(String.format("List length mismatch: %d != %d", a.size(), b.size()));
+ return false;
+ }
+ for (int i = 0; i < a.size(); i++) {
+ if (!deepEquals(a.get(i), b.get(i), errorMsg)) {
+ errorMsg.insert(0, String.format("Element %d mismatch: ", i));
+ return false;
+ }
+ }
+ return true;
+ };
+}
diff --git a/libartservice/service/javatests/com/android/server/art/testing/TestingUtilsTest.java b/libartservice/service/javatests/com/android/server/art/testing/TestingUtilsTest.java
new file mode 100644
index 0000000..518f0e4
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/testing/TestingUtilsTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 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 com.android.server.art.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.NonNull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.internal.progress.ThreadSafeMockingProgress;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.function.Consumer;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TestingUtilsTest {
+ @Before
+ @After
+ public void resetMockito() {
+ ThreadSafeMockingProgress.mockingProgress().reset();
+ }
+
+ @Test
+ public void testDeepEquals() {
+ var a = new Foo();
+ var b = new Foo();
+ assertThat(TestingUtils.deepEquals(a, b)).isTrue();
+ }
+
+ @Test
+ public void testDeepEqualsNull() {
+ assertThat(TestingUtils.deepEquals(null, null)).isTrue();
+ }
+
+ @Test
+ public void testDeepEqualsNullabilityMismatch() {
+ var a = new Foo();
+ assertThat(TestingUtils.deepEquals(a, null)).isFalse();
+ }
+
+ @Test
+ public void testDeepEqualsTypeMismatch() {
+ var a = new Foo();
+ var b = new Bar();
+ assertThat(TestingUtils.deepEquals(a, b)).isFalse();
+ }
+
+ @Test
+ public void testDeepEqualsPrimitiveFieldMismatch() {
+ var a = new Foo();
+ var b = new Foo();
+ b.mA = 11111111;
+ assertThat(TestingUtils.deepEquals(a, b)).isFalse();
+ }
+
+ @Test
+ public void testDeepEqualsStringFieldMismatch() {
+ var a = new Foo();
+ var b = new Foo();
+ b.mB = "def";
+ assertThat(TestingUtils.deepEquals(a, b)).isFalse();
+ }
+
+ @Test
+ public void deepEqualsNestedFieldMismatch() {
+ var a = new Foo();
+ var b = new Foo();
+ b.mC.setB(11111111);
+ assertThat(TestingUtils.deepEquals(a, b)).isFalse();
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testDeepEqualsArrayNotSupported() throws Exception {
+ int[] a = new int[] {1};
+ int[] b = new int[] {2};
+ TestingUtils.deepEquals(a, b);
+ }
+
+ @Test
+ public void testListDeepEquals() throws Exception {
+ var a = new ArrayList<Integer>();
+ a.add(1);
+ a.add(2);
+ a.add(3);
+ a.add(4);
+ a.add(5);
+ var b = List.of(1, 2, 3, 4, 5);
+ assertThat(TestingUtils.deepEquals(a, b)).isTrue();
+ }
+
+ @Test
+ public void testListDeepEqualsSizeMismatch() throws Exception {
+ var a = new ArrayList<Integer>();
+ a.add(1);
+ var b = new ArrayList<Integer>();
+ b.add(1);
+ b.add(2);
+ assertThat(TestingUtils.deepEquals(a, b)).isFalse();
+ }
+
+ @Test
+ public void testListDeepEqualsElementMismatch() throws Exception {
+ var a = new ArrayList<Integer>();
+ a.add(1);
+ var b = new ArrayList<Integer>();
+ b.add(2);
+ assertThat(TestingUtils.deepEquals(a, b)).isFalse();
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testDeepEqualsOtherContainerNotSupported() throws Exception {
+ var a = new HashSet<Integer>();
+ a.add(1);
+ var b = new HashSet<Integer>();
+ b.add(2);
+ TestingUtils.deepEquals(a, b);
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ @Test
+ public void testInAnyOrderDuplicates() {
+ testInAnyOrderInternal(List.of(1, 1), List.of(1), false, TestingUtils::inAnyOrder);
+ testInAnyOrderInternal(List.of(1, 1), List.of(1, 1), true, TestingUtils::inAnyOrder);
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ @Test
+ public void testInAnyOrderDeepEqualsDuplicates() {
+ testInAnyOrderInternal(
+ Arrays.asList(1, 1), List.of(1), false, TestingUtils::inAnyOrderDeepEquals);
+ testInAnyOrderInternal(
+ Arrays.asList(1, 1), List.of(1, 1), true, TestingUtils::inAnyOrderDeepEquals);
+ }
+
+ private void testInAnyOrderInternal(@NonNull List<Integer> first, @NonNull List<Integer> second,
+ boolean expected, @NonNull Consumer<Integer[]> inAnyOrderBlock) {
+ inAnyOrderBlock.accept(first.toArray(new Integer[0]));
+ var matchers = ThreadSafeMockingProgress.mockingProgress()
+ .getArgumentMatcherStorage()
+ .pullLocalizedMatchers();
+ assertThat(matchers).hasSize(1);
+ // noinspection unchecked
+ var matcher = (ArgumentMatcher<List<Integer>>) matchers.get(0).getMatcher();
+ assertThat(matcher.matches(second)).isEqualTo(expected);
+ ThreadSafeMockingProgress.mockingProgress().reset();
+ }
+}
+
+class Foo {
+ public int mA = 1234567;
+ public String mB = "abc";
+ public Bar mC = new Bar();
+}
+
+class Bar {
+ public static int sA = 10000000;
+ private int mB = 7654321;
+ public void setB(int b) {
+ mB = b;
+ }
+}
diff --git a/libartservice/service/proguard.flags b/libartservice/service/proguard.flags
new file mode 100644
index 0000000..8ef413f
--- /dev/null
+++ b/libartservice/service/proguard.flags
@@ -0,0 +1,10 @@
+# Proto field names are used by MessageLiteToString.toString through reflection.
+-keepclassmembers class * extends
+ com.android.server.art.jarjar.com.google.protobuf.GeneratedMessageLite {
+ *** get*();
+ *** set*(***);
+ *** has*();
+}
+
+# A job service is referenced by the framework through reflection.
+-keep class * extends android.app.job.JobService { *; }
diff --git a/libartservice/service/proto/common.proto b/libartservice/service/proto/common.proto
new file mode 100644
index 0000000..c8bb565
--- /dev/null
+++ b/libartservice/service/proto/common.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+syntax = "proto3";
+
+package com.android.server.art.proto;
+option java_multiple_files = true;
+
+// Wrapper message for `int32`, to distinguish between the absence of a field
+// and its default value.
+message Int32Value {
+ int32 value = 1;
+}
diff --git a/libartservice/service/proto/dex_use.proto b/libartservice/service/proto/dex_use.proto
new file mode 100644
index 0000000..1dd962d
--- /dev/null
+++ b/libartservice/service/proto/dex_use.proto
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+syntax = "proto3";
+
+package com.android.server.art.proto;
+option java_multiple_files = true;
+
+import "art/libartservice/service/proto/common.proto";
+
+// The protobuf representation of `DexUseManagerLocal.DexUse`. See classes in
+// java/com/android/server/art/DexUseManagerLocal.java for details.
+// This proto is persisted on disk and both forward and backward compatibility are considerations.
+message DexUseProto {
+ repeated PackageDexUseProto package_dex_use = 1;
+}
+
+message PackageDexUseProto {
+ string owning_package_name = 1;
+ repeated PrimaryDexUseProto primary_dex_use = 2;
+ repeated SecondaryDexUseProto secondary_dex_use = 3;
+}
+
+message PrimaryDexUseProto {
+ string dex_file = 1;
+ repeated PrimaryDexUseRecordProto record = 2;
+}
+
+message PrimaryDexUseRecordProto {
+ string loading_package_name = 1;
+ bool isolated_process = 2;
+ int64 last_used_at_ms = 3;
+}
+
+message SecondaryDexUseProto {
+ string dex_file = 1;
+ Int32Value user_id = 2; // Must be explicitly set.
+ repeated SecondaryDexUseRecordProto record = 3;
+}
+
+message SecondaryDexUseRecordProto {
+ string loading_package_name = 1;
+ bool isolated_process = 2;
+ string class_loader_context = 3;
+ string abi_name = 4;
+ int64 last_used_at_ms = 5;
+}
diff --git a/libartservice/tests/Android.bp b/libartservice/tests/Android.bp
deleted file mode 100644
index dc110a1..0000000
--- a/libartservice/tests/Android.bp
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Copyright (C) 2021 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.
-//
-
-//########################################################################
-// Build ArtServiceTests package
-//########################################################################
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "art_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["art_license"],
-}
-
-java_test {
- name: "ArtServiceTests",
-
- // Include all test java files.
- srcs: [
- "src/**/*.java",
- ],
-
- static_libs: [
- "androidx.test.runner",
- "service-art.impl",
- ],
-
- test_suites: ["general-tests"],
-}
diff --git a/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java b/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java
deleted file mode 100644
index 515849b..0000000
--- a/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 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 com.android.server.art;
-
-import static org.junit.Assert.assertTrue;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.art.ArtManagerLocal;
-
-import junit.framework.TestCase;
-
-public class ArtManagerLocalTests extends TestCase {
- private ArtManagerLocal mArtManagerLocal;
-
- public void setup() {
- mArtManagerLocal = new ArtManagerLocal();
- }
-
- public void testScaffolding() {
- assertTrue(true);
- }
-}
\ No newline at end of file
diff --git a/libarttools/Android.bp b/libarttools/Android.bp
index 3df40a5..13fa205 100644
--- a/libarttools/Android.bp
+++ b/libarttools/Android.bp
@@ -49,12 +49,18 @@
art_cc_defaults {
name: "art_libarttools_tests_defaults",
srcs: [
+ "tools/art_exec_test.cc",
+ "tools/cmdline_builder_test.cc",
+ "tools/system_properties_test.cc",
"tools/tools_test.cc",
],
shared_libs: [
"libbase",
"libarttools",
],
+ static_libs: [
+ "libgmock",
+ ],
}
// Version of ART gtest `art_libarttools_tests` bundled with the ART APEX on target.
@@ -76,3 +82,39 @@
"art_libarttools_tests_defaults",
],
}
+
+// A defaults that contains libprocessgroup and all its dependencies.
+cc_defaults {
+ name: "art_libprocessgroup_defaults",
+ shared_libs: [
+ "libbase",
+ "libcgrouprc",
+ ],
+ static_libs: [
+ "libjsoncpp",
+ "libprocessgroup",
+ ],
+}
+
+cc_binary {
+ name: "art_exec",
+ defaults: [
+ "art_defaults",
+ "art_libprocessgroup_defaults",
+ ],
+ srcs: [
+ "tools/art_exec.cc",
+ ],
+ shared_libs: [
+ "libartbase",
+ "libbase",
+ ],
+ static_libs: [
+ "libc++fs",
+ "libcap",
+ ],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
diff --git a/libarttools/tools/art_exec.cc b/libarttools/tools/art_exec.cc
new file mode 100644
index 0000000..7fecda6
--- /dev/null
+++ b/libarttools/tools/art_exec.cc
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 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 <stdlib.h>
+#include <sys/capability.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <iostream>
+#include <iterator>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <system_error>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/parseint.h"
+#include "android-base/result.h"
+#include "android-base/strings.h"
+#include "base/macros.h"
+#include "base/scoped_cap.h"
+#include "fmt/format.h"
+#include "processgroup/processgroup.h"
+#include "system/thread_defs.h"
+
+namespace {
+
+using ::android::base::ConsumePrefix;
+using ::android::base::Join;
+using ::android::base::ParseInt;
+using ::android::base::Result;
+using ::android::base::Split;
+
+using ::fmt::literals::operator""_format; // NOLINT
+
+constexpr const char* kUsage =
+ R"(A wrapper binary that configures the process and executes a command.
+
+By default, it closes all open file descriptors except stdin, stdout, and stderr. `--keep-fds` can
+be passed to keep some more file descriptors open.
+
+Usage: art_exec [OPTIONS]... -- [COMMAND]...
+
+Supported options:
+ --help: Print this text.
+ --set-task-profile=PROFILES: Apply a set of task profiles (see
+ https://source.android.com/devices/tech/perf/cgroups). Requires root access. PROFILES can be a
+ comma-separated list of task profile names.
+ --set-priority=PRIORITY: Apply the process priority. Currently, the only supported value of
+ PRIORITY is "background".
+ --drop-capabilities: Drop all root capabilities. Note that this has effect only if `art_exec` runs
+ with some root capabilities but not as the root user.
+ --keep-fds=FILE_DESCRIPTORS: A semicolon-separated list of file descriptors to keep open.
+ --env=KEY=VALUE: Set an environment variable. This flag can be passed multiple times to set
+ multiple environment variables.
+)";
+
+constexpr int kErrorUsage = 100;
+constexpr int kErrorOther = 101;
+
+struct Options {
+ int command_pos = -1;
+ std::vector<std::string> task_profiles;
+ std::optional<int> priority = std::nullopt;
+ bool drop_capabilities = false;
+ std::unordered_set<int> keep_fds{fileno(stdin), fileno(stdout), fileno(stderr)};
+ std::unordered_map<std::string, std::string> envs;
+};
+
+[[noreturn]] void Usage(const std::string& error_msg) {
+ LOG(ERROR) << error_msg;
+ std::cerr << error_msg << "\n" << kUsage << "\n";
+ exit(kErrorUsage);
+}
+
+Options ParseOptions(int argc, char** argv) {
+ Options options;
+ for (int i = 1; i < argc; i++) {
+ std::string_view arg = argv[i];
+ if (arg == "--help") {
+ std::cerr << kUsage << "\n";
+ exit(0);
+ } else if (ConsumePrefix(&arg, "--set-task-profile=")) {
+ options.task_profiles = Split(std::string(arg), ",");
+ if (options.task_profiles.empty()) {
+ Usage("Empty task profile list");
+ }
+ } else if (ConsumePrefix(&arg, "--set-priority=")) {
+ if (arg == "background") {
+ options.priority = ANDROID_PRIORITY_BACKGROUND;
+ } else {
+ Usage("Unknown priority " + std::string(arg));
+ }
+ } else if (arg == "--drop-capabilities") {
+ options.drop_capabilities = true;
+ } else if (ConsumePrefix(&arg, "--keep-fds=")) {
+ for (const std::string& fd_str : Split(std::string(arg), ":")) {
+ int fd;
+ if (!ParseInt(fd_str, &fd)) {
+ Usage("Invalid fd " + fd_str);
+ }
+ options.keep_fds.insert(fd);
+ }
+ } else if (ConsumePrefix(&arg, "--env=")) {
+ size_t pos = arg.find('=');
+ if (pos == std::string_view::npos) {
+ Usage("Malformed environment variable. Must contain '='");
+ }
+ options.envs[std::string(arg.substr(/*pos=*/0, /*n=*/pos))] =
+ std::string(arg.substr(pos + 1));
+ } else if (arg == "--") {
+ if (i + 1 >= argc) {
+ Usage("Missing command after '--'");
+ }
+ options.command_pos = i + 1;
+ return options;
+ } else {
+ Usage("Unknown option " + std::string(arg));
+ }
+ }
+ Usage("Missing '--'");
+}
+
+Result<void> DropInheritableCaps() {
+ art::ScopedCap cap(cap_get_proc());
+ if (cap.Get() == nullptr) {
+ return ErrnoErrorf("Failed to call cap_get_proc");
+ }
+ if (cap_clear_flag(cap.Get(), CAP_INHERITABLE) != 0) {
+ return ErrnoErrorf("Failed to call cap_clear_flag");
+ }
+ if (cap_set_proc(cap.Get()) != 0) {
+ return ErrnoErrorf("Failed to call cap_set_proc");
+ }
+ return {};
+}
+
+Result<void> CloseFds(const std::unordered_set<int>& keep_fds) {
+ std::vector<int> open_fds;
+ std::error_code ec;
+ for (const std::filesystem::directory_entry& dir_entry :
+ std::filesystem::directory_iterator("/proc/self/fd", ec)) {
+ int fd;
+ if (!ParseInt(dir_entry.path().filename(), &fd)) {
+ return Errorf("Invalid entry in /proc/self/fd {}", dir_entry.path().filename());
+ }
+ open_fds.push_back(fd);
+ }
+ if (ec) {
+ return Errorf("Failed to list open FDs: {}", ec.message());
+ }
+ for (int fd : open_fds) {
+ if (keep_fds.find(fd) == keep_fds.end()) {
+ if (close(fd) != 0) {
+ Result<void> error = ErrnoErrorf("Failed to close FD {}", fd);
+ if (std::filesystem::exists("/proc/self/fd/{}"_format(fd))) {
+ return error;
+ }
+ }
+ }
+ }
+ return {};
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv);
+
+ Options options = ParseOptions(argc, argv);
+
+ if (auto result = CloseFds(options.keep_fds); !result.ok()) {
+ LOG(ERROR) << "Failed to close open FDs: " << result.error();
+ return kErrorOther;
+ }
+
+ if (!options.task_profiles.empty()) {
+ if (!SetTaskProfiles(/*tid=*/0, options.task_profiles)) {
+ LOG(ERROR) << "Failed to set task profile";
+ return kErrorOther;
+ }
+ }
+
+ if (options.priority.has_value()) {
+ if (setpriority(PRIO_PROCESS, /*who=*/0, options.priority.value()) != 0) {
+ PLOG(ERROR) << "Failed to setpriority";
+ return kErrorOther;
+ }
+ }
+
+ if (options.drop_capabilities) {
+ if (auto result = DropInheritableCaps(); !result.ok()) {
+ LOG(ERROR) << "Failed to drop inheritable capabilities: " << result.error();
+ return kErrorOther;
+ }
+ }
+
+ for (const auto& [key, value] : options.envs) {
+ setenv(key.c_str(), value.c_str(), /*overwrite=*/1);
+ }
+
+ execv(argv[options.command_pos], argv + options.command_pos);
+
+ std::vector<const char*> command_args(argv + options.command_pos, argv + argc);
+ PLOG(FATAL) << "Failed to execute (" << Join(command_args, ' ') << ")";
+ UNREACHABLE();
+}
diff --git a/libarttools/tools/art_exec_test.cc b/libarttools/tools/art_exec_test.cc
new file mode 100644
index 0000000..0e77633
--- /dev/null
+++ b/libarttools/tools/art_exec_test.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2021 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 <sys/capability.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <csignal>
+#include <filesystem>
+#include <functional>
+#include <string>
+#include <utility>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/scopeguard.h"
+#include "android-base/strings.h"
+#include "base/common_art_test.h"
+#include "base/file_utils.h"
+#include "base/globals.h"
+#include "base/macros.h"
+#include "base/os.h"
+#include "base/scoped_cap.h"
+#include "exec_utils.h"
+#include "fmt/format.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "system/thread_defs.h"
+
+namespace art {
+namespace {
+
+using ::android::base::make_scope_guard;
+using ::android::base::ScopeGuard;
+using ::android::base::Split;
+using ::testing::Contains;
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+// clang-tidy incorrectly complaints about the using declaration while the user-defined literal is
+// actually being used.
+using ::fmt::literals::operator""_format; // NOLINT
+
+constexpr uid_t kRoot = 0;
+constexpr uid_t kNobody = 9999;
+
+// This test executes a few Linux system commands such as "ls", which are linked against system
+// libraries. In many ART gtests we set LD_LIBRARY_PATH to make the test binaries link to libraries
+// from the ART module first, and if that setting is propagated to the system commands they may also
+// try to link to those libraries instead of the system ones they are built against. This is
+// particularly noticeable when 32-bit tests run on a 64-bit system. Hence we need to set
+// LD_LIBRARY_PATH to an empty string here.
+// TODO(b/247108425): Remove this when ART gtests no longer use LD_LIBRARY_PATH.
+constexpr const char* kEmptyLdLibraryPath = "--env=LD_LIBRARY_PATH=";
+
+std::string GetArtBin(const std::string& name) { return "{}/bin/{}"_format(GetArtRoot(), name); }
+
+std::string GetBin(const std::string& name) { return "{}/bin/{}"_format(GetAndroidRoot(), name); }
+
+// Executes the command, waits for it to finish, and keeps it in a waitable state until the current
+// scope exits.
+std::pair<pid_t, ScopeGuard<std::function<void()>>> ScopedExecAndWait(
+ std::vector<std::string>& args) {
+ std::vector<char*> execv_args;
+ execv_args.reserve(args.size() + 1);
+ for (std::string& arg : args) {
+ execv_args.push_back(arg.data());
+ }
+ execv_args.push_back(nullptr);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ execv(execv_args[0], execv_args.data());
+ UNREACHABLE();
+ } else if (pid > 0) {
+ siginfo_t info;
+ CHECK_EQ(TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED | WNOWAIT)), 0);
+ CHECK_EQ(info.si_code, CLD_EXITED);
+ CHECK_EQ(info.si_status, 0);
+ std::function<void()> cleanup([=] {
+ siginfo_t info;
+ CHECK_EQ(TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)), 0);
+ });
+ return std::make_pair(pid, make_scope_guard(std::move(cleanup)));
+ } else {
+ LOG(FATAL) << "Failed to call fork";
+ UNREACHABLE();
+ }
+}
+
+// Grants the current process the given root capability.
+void SetCap(cap_flag_t flag, cap_value_t value) {
+ ScopedCap cap(cap_get_proc());
+ CHECK_NE(cap.Get(), nullptr);
+ cap_value_t caps[]{value};
+ CHECK_EQ(cap_set_flag(cap.Get(), flag, /*ncap=*/1, caps, CAP_SET), 0);
+ CHECK_EQ(cap_set_proc(cap.Get()), 0);
+}
+
+// Returns true if the given process has the given root capability.
+bool GetCap(pid_t pid, cap_flag_t flag, cap_value_t value) {
+ ScopedCap cap(cap_get_pid(pid));
+ CHECK_NE(cap.Get(), nullptr);
+ cap_flag_value_t flag_value;
+ CHECK_EQ(cap_get_flag(cap.Get(), value, flag, &flag_value), 0);
+ return flag_value == CAP_SET;
+}
+
+class ArtExecTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ testing::Test::SetUp();
+ if (!kIsTargetAndroid) {
+ GTEST_SKIP() << "art_exec is for device only";
+ }
+ if (getuid() != kRoot) {
+ GTEST_SKIP() << "art_exec requires root";
+ }
+ art_exec_bin_ = GetArtBin("art_exec");
+ }
+
+ std::string art_exec_bin_;
+};
+
+TEST_F(ArtExecTest, Command) {
+ std::string error_msg;
+ int ret = ExecAndReturnCode({art_exec_bin_, "--", GetBin("sh"), "-c", "exit 123"}, &error_msg);
+ ASSERT_EQ(ret, 123) << error_msg;
+}
+
+TEST_F(ArtExecTest, SetTaskProfiles) {
+ std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
+ ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
+ ASSERT_GE(scratch_file.GetFd(), 0);
+
+ std::vector<std::string> args{art_exec_bin_,
+ "--set-task-profile=ProcessCapacityHigh",
+ kEmptyLdLibraryPath,
+ "--",
+ GetBin("sh"),
+ "-c",
+ "cat /proc/self/cgroup > " + filename};
+ auto [pid, scope_guard] = ScopedExecAndWait(args);
+ std::string cgroup;
+ ASSERT_TRUE(android::base::ReadFileToString(filename, &cgroup));
+ EXPECT_THAT(cgroup, HasSubstr(":cpuset:/foreground\n"));
+}
+
+TEST_F(ArtExecTest, SetPriority) {
+ std::vector<std::string> args{
+ art_exec_bin_, "--set-priority=background", kEmptyLdLibraryPath, "--", GetBin("true")};
+ auto [pid, scope_guard] = ScopedExecAndWait(args);
+ EXPECT_EQ(getpriority(PRIO_PROCESS, pid), ANDROID_PRIORITY_BACKGROUND);
+}
+
+TEST_F(ArtExecTest, DropCapabilities) {
+ // Switch to a non-root user, but still keep the CAP_FOWNER capability available and inheritable.
+ // The order of the following calls matters.
+ CHECK_EQ(cap_setuid(kNobody), 0);
+ SetCap(CAP_INHERITABLE, CAP_FOWNER);
+ SetCap(CAP_EFFECTIVE, CAP_FOWNER);
+ ASSERT_EQ(cap_set_ambient(CAP_FOWNER, CAP_SET), 0);
+
+ // Make sure the test is set up correctly (i.e., the child process should normally have the
+ // inherited root capability: CAP_FOWNER).
+ {
+ std::vector<std::string> args{art_exec_bin_, kEmptyLdLibraryPath, "--", GetBin("true")};
+ auto [pid, scope_guard] = ScopedExecAndWait(args);
+ ASSERT_TRUE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER));
+ }
+
+ {
+ std::vector<std::string> args{
+ art_exec_bin_, "--drop-capabilities", kEmptyLdLibraryPath, "--", GetBin("true")};
+ auto [pid, scope_guard] = ScopedExecAndWait(args);
+ EXPECT_FALSE(GetCap(pid, CAP_EFFECTIVE, CAP_FOWNER));
+ }
+}
+
+TEST_F(ArtExecTest, CloseFds) {
+ std::unique_ptr<File> file1(OS::OpenFileForReading("/dev/zero"));
+ std::unique_ptr<File> file2(OS::OpenFileForReading("/dev/zero"));
+ std::unique_ptr<File> file3(OS::OpenFileForReading("/dev/zero"));
+ ASSERT_NE(file1, nullptr);
+ ASSERT_NE(file2, nullptr);
+ ASSERT_NE(file3, nullptr);
+
+ std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
+ ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
+ ASSERT_GE(scratch_file.GetFd(), 0);
+
+ std::vector<std::string> args{art_exec_bin_,
+ "--keep-fds={}:{}"_format(file3->Fd(), file2->Fd()),
+ kEmptyLdLibraryPath,
+ "--",
+ GetBin("sh"),
+ "-c",
+ "("
+ "readlink /proc/self/fd/{} || echo;"
+ "readlink /proc/self/fd/{} || echo;"
+ "readlink /proc/self/fd/{} || echo;"
+ ") > {}"_format(file1->Fd(), file2->Fd(), file3->Fd(), filename)};
+
+ ScopedExecAndWait(args);
+
+ std::string open_fds;
+ ASSERT_TRUE(android::base::ReadFileToString(filename, &open_fds));
+
+ // `file1` should be closed, while the other two should be open. There's a blank line at the end.
+ EXPECT_THAT(Split(open_fds, "\n"), ElementsAre(Not("/dev/zero"), "/dev/zero", "/dev/zero", ""));
+}
+
+TEST_F(ArtExecTest, Env) {
+ std::string filename = "/data/local/tmp/art-exec-test-XXXXXX";
+ ScratchFile scratch_file(new File(mkstemp(filename.data()), filename, /*check_usage=*/false));
+ ASSERT_GE(scratch_file.GetFd(), 0);
+
+ std::vector<std::string> args{art_exec_bin_,
+ "--env=FOO=BAR",
+ kEmptyLdLibraryPath,
+ "--",
+ GetBin("sh"),
+ "-c",
+ "env > " + filename};
+
+ ScopedExecAndWait(args);
+
+ std::string envs;
+ ASSERT_TRUE(android::base::ReadFileToString(filename, &envs));
+
+ EXPECT_THAT(Split(envs, "\n"), Contains("FOO=BAR"));
+}
+
+} // namespace
+} // namespace art
diff --git a/libarttools/tools/cmdline_builder.h b/libarttools/tools/cmdline_builder.h
new file mode 100644
index 0000000..fd11ee8
--- /dev/null
+++ b/libarttools/tools/cmdline_builder.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTTOOLS_TOOLS_CMDLINE_BUILDER_H_
+#define ART_LIBARTTOOLS_TOOLS_CMDLINE_BUILDER_H_
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+
+namespace art {
+namespace tools {
+
+namespace internal {
+
+constexpr bool ContainsOneFormatSpecifier(std::string_view format, char specifier) {
+ int count = 0;
+ size_t pos = 0;
+ while ((pos = format.find('%', pos)) != std::string_view::npos) {
+ if (pos == format.length() - 1) {
+ // Invalid trailing '%'.
+ return false;
+ }
+ if (format[pos + 1] == specifier) {
+ count++;
+ } else if (format[pos + 1] != '%') {
+ // "%%" is okay. Otherwise, it's a wrong specifier.
+ return false;
+ }
+ pos += 2;
+ }
+ return count == 1;
+}
+
+} // namespace internal
+
+// A util class that builds cmdline arguments.
+class CmdlineBuilder {
+ public:
+ // Returns all arguments.
+ const std::vector<std::string>& Get() const { return elements_; }
+
+ // Adds an argument as-is.
+ CmdlineBuilder& Add(std::string_view arg) {
+ elements_.push_back(std::string(arg));
+ return *this;
+ }
+
+ // Same as above but adds a runtime argument.
+ CmdlineBuilder& AddRuntime(std::string_view arg) { return Add("--runtime-arg").Add(arg); }
+
+ // Adds a string value formatted by the format string.
+ //
+ // Usage: Add("--flag=%s", "value")
+ CmdlineBuilder& Add(const char* arg_format, const std::string& value)
+ __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
+ "'arg' must be a string literal that contains '%s'"))) {
+ return Add(android::base::StringPrintf(arg_format, value.c_str()));
+ }
+
+ // Same as above but adds a runtime argument.
+ CmdlineBuilder& AddRuntime(const char* arg_format, const std::string& value)
+ __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
+ "'arg' must be a string literal that contains '%s'"))) {
+ return AddRuntime(android::base::StringPrintf(arg_format, value.c_str()));
+ }
+
+ // Adds an integer value formatted by the format string.
+ //
+ // Usage: Add("--flag=%d", 123)
+ CmdlineBuilder& Add(const char* arg_format, int value)
+ __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 'd'),
+ "'arg' must be a string literal that contains '%d'"))) {
+ return Add(android::base::StringPrintf(arg_format, value));
+ }
+
+ // Same as above but adds a runtime argument.
+ CmdlineBuilder& AddRuntime(const char* arg_format, int value)
+ __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 'd'),
+ "'arg' must be a string literal that contains '%d'"))) {
+ return AddRuntime(android::base::StringPrintf(arg_format, value));
+ }
+
+ // Adds a string value formatted by the format string if the value is non-empty. Does nothing
+ // otherwise.
+ //
+ // Usage: AddIfNonEmpty("--flag=%s", "value")
+ CmdlineBuilder& AddIfNonEmpty(const char* arg_format, const std::string& value)
+ __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
+ "'arg' must be a string literal that contains '%s'"))) {
+ if (!value.empty()) {
+ Add(android::base::StringPrintf(arg_format, value.c_str()));
+ }
+ return *this;
+ }
+
+ // Same as above but adds a runtime argument.
+ CmdlineBuilder& AddRuntimeIfNonEmpty(const char* arg_format, const std::string& value)
+ __attribute__((enable_if(internal::ContainsOneFormatSpecifier(arg_format, 's'),
+ "'arg' must be a string literal that contains '%s'"))) {
+ if (!value.empty()) {
+ AddRuntime(android::base::StringPrintf(arg_format, value.c_str()));
+ }
+ return *this;
+ }
+
+ // Adds an argument as-is if the boolean value is true. Does nothing otherwise.
+ CmdlineBuilder& AddIf(bool value, std::string_view arg) {
+ if (value) {
+ Add(arg);
+ }
+ return *this;
+ }
+
+ // Same as above but adds a runtime argument.
+ CmdlineBuilder& AddRuntimeIf(bool value, std::string_view arg) {
+ if (value) {
+ AddRuntime(arg);
+ }
+ return *this;
+ }
+
+ // Concatenates this builder with another. Returns the concatenated result and nullifies the input
+ // builder.
+ CmdlineBuilder& Concat(CmdlineBuilder&& other) {
+ elements_.reserve(elements_.size() + other.elements_.size());
+ std::move(other.elements_.begin(), other.elements_.end(), std::back_inserter(elements_));
+ other.elements_.clear();
+ return *this;
+ }
+
+ private:
+ std::vector<std::string> elements_;
+};
+
+} // namespace tools
+} // namespace art
+
+#endif // ART_LIBARTTOOLS_TOOLS_CMDLINE_BUILDER_H_
diff --git a/libarttools/tools/cmdline_builder_test.cc b/libarttools/tools/cmdline_builder_test.cc
new file mode 100644
index 0000000..5551860
--- /dev/null
+++ b/libarttools/tools/cmdline_builder_test.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 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 "cmdline_builder.h"
+
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace tools {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+
+class CmdlineBuilderTest : public testing::Test {
+ protected:
+ CmdlineBuilder args_;
+};
+
+TEST_F(CmdlineBuilderTest, ContainsOneFormatSpecifier) {
+ EXPECT_TRUE(internal::ContainsOneFormatSpecifier("--flag=%s", 's'));
+ EXPECT_TRUE(internal::ContainsOneFormatSpecifier("--flag=[%s]", 's'));
+ EXPECT_TRUE(internal::ContainsOneFormatSpecifier("--flag=%s%%", 's'));
+ EXPECT_TRUE(internal::ContainsOneFormatSpecifier("--flag=[%s%%]", 's'));
+ EXPECT_TRUE(internal::ContainsOneFormatSpecifier("--flag=%%%s", 's'));
+ EXPECT_FALSE(internal::ContainsOneFormatSpecifier("--flag=", 's'));
+ EXPECT_FALSE(internal::ContainsOneFormatSpecifier("--flag=%s%s", 's'));
+ EXPECT_FALSE(internal::ContainsOneFormatSpecifier("--flag=%s%", 's'));
+ EXPECT_FALSE(internal::ContainsOneFormatSpecifier("--flag=%d", 's'));
+ EXPECT_FALSE(internal::ContainsOneFormatSpecifier("--flag=%s%d", 's'));
+ EXPECT_FALSE(internal::ContainsOneFormatSpecifier("--flag=%%s", 's'));
+}
+
+TEST_F(CmdlineBuilderTest, Add) {
+ args_.Add("--flag");
+ EXPECT_THAT(args_.Get(), ElementsAre("--flag"));
+}
+
+TEST_F(CmdlineBuilderTest, AddRuntime) {
+ args_.AddRuntime("--flag");
+ EXPECT_THAT(args_.Get(), ElementsAre("--runtime-arg", "--flag"));
+}
+
+TEST_F(CmdlineBuilderTest, AddString) {
+ args_.Add("--flag=[%s]", "foo");
+ EXPECT_THAT(args_.Get(), ElementsAre("--flag=[foo]"));
+}
+
+TEST_F(CmdlineBuilderTest, AddRuntimeString) {
+ args_.AddRuntime("--flag=[%s]", "foo");
+ EXPECT_THAT(args_.Get(), ElementsAre("--runtime-arg", "--flag=[foo]"));
+}
+
+TEST_F(CmdlineBuilderTest, AddInt) {
+ args_.Add("--flag=[%d]", 123);
+ EXPECT_THAT(args_.Get(), ElementsAre("--flag=[123]"));
+}
+
+TEST_F(CmdlineBuilderTest, AddRuntimeInt) {
+ args_.AddRuntime("--flag=[%d]", 123);
+ EXPECT_THAT(args_.Get(), ElementsAre("--runtime-arg", "--flag=[123]"));
+}
+
+TEST_F(CmdlineBuilderTest, AddIfNonEmpty) {
+ args_.AddIfNonEmpty("--flag=[%s]", "foo");
+ EXPECT_THAT(args_.Get(), ElementsAre("--flag=[foo]"));
+}
+
+TEST_F(CmdlineBuilderTest, AddIfNonEmptyEmpty) {
+ args_.AddIfNonEmpty("--flag=[%s]", "");
+ EXPECT_THAT(args_.Get(), IsEmpty());
+}
+
+TEST_F(CmdlineBuilderTest, AddRuntimeIfNonEmpty) {
+ args_.AddRuntimeIfNonEmpty("--flag=[%s]", "foo");
+ EXPECT_THAT(args_.Get(), ElementsAre("--runtime-arg", "--flag=[foo]"));
+}
+
+TEST_F(CmdlineBuilderTest, AddRuntimeIfNonEmptyEmpty) {
+ args_.AddRuntimeIfNonEmpty("--flag=[%s]", "");
+ EXPECT_THAT(args_.Get(), IsEmpty());
+}
+
+TEST_F(CmdlineBuilderTest, AddIfTrue) {
+ args_.AddIf(true, "--flag");
+ EXPECT_THAT(args_.Get(), ElementsAre("--flag"));
+}
+
+TEST_F(CmdlineBuilderTest, AddIfFalse) {
+ args_.AddIf(false, "--flag");
+ EXPECT_THAT(args_.Get(), IsEmpty());
+}
+
+TEST_F(CmdlineBuilderTest, AddRuntimeIfTrue) {
+ args_.AddRuntimeIf(true, "--flag");
+ EXPECT_THAT(args_.Get(), ElementsAre("--runtime-arg", "--flag"));
+}
+
+TEST_F(CmdlineBuilderTest, AddRuntimeIfFalse) {
+ args_.AddRuntimeIf(false, "--flag");
+ EXPECT_THAT(args_.Get(), IsEmpty());
+}
+
+TEST_F(CmdlineBuilderTest, Concat) {
+ args_.Add("--flag1");
+ args_.Add("--flag2");
+
+ CmdlineBuilder other;
+ other.Add("--flag3");
+ other.Add("--flag4");
+
+ args_.Concat(std::move(other));
+ EXPECT_THAT(args_.Get(), ElementsAre("--flag1", "--flag2", "--flag3", "--flag4"));
+ EXPECT_THAT(other.Get(), IsEmpty());
+}
+
+} // namespace
+} // namespace tools
+} // namespace art
diff --git a/libarttools/tools/system_properties.h b/libarttools/tools/system_properties.h
new file mode 100644
index 0000000..06b7bcb
--- /dev/null
+++ b/libarttools/tools/system_properties.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTTOOLS_TOOLS_SYSTEM_PROPERTIES_H_
+#define ART_LIBARTTOOLS_TOOLS_SYSTEM_PROPERTIES_H_
+
+#include <string>
+
+#include "android-base/parsebool.h"
+#include "android-base/properties.h"
+
+namespace art {
+namespace tools {
+
+// A class for getting system properties with fallback lookup support. Different from
+// android::base::GetProperty, this class is mockable.
+class SystemProperties {
+ public:
+ virtual ~SystemProperties() = default;
+
+ // Returns the current value of the system property `key`, or `default_value` if the property
+ // doesn't have a value.
+ std::string Get(const std::string& key, const std::string& default_value) const {
+ std::string value = GetProperty(key);
+ if (!value.empty()) {
+ return value;
+ }
+ return default_value;
+ }
+
+ // Same as above, but allows specifying one or more fallback keys. The last argument is a string
+ // default value that will be used if none of the given keys has a value.
+ //
+ // Usage:
+ //
+ // Look up for "key_1", then "key_2", then "key_3". If none of them has a value, return "default":
+ // Get("key_1", "key_2", "key_3", /*default_value=*/"default")
+ template <typename... Args>
+ std::string Get(const std::string& key, const std::string& fallback_key, Args... args) const {
+ return Get(key, Get(fallback_key, args...));
+ }
+
+ // Returns the current value of the system property `key` with zero or more fallback keys, or an
+ // empty string if none of the given keys has a value.
+ //
+ // Usage:
+ //
+ // Look up for "key_1". If it doesn't have a value, return an empty string:
+ // GetOrEmpty("key_1")
+ //
+ // Look up for "key_1", then "key_2", then "key_3". If none of them has a value, return an empty
+ // string:
+ // GetOrEmpty("key_1", "key_2", "key_3")
+ template <typename... Args>
+ std::string GetOrEmpty(const std::string& key, Args... fallback_keys) const {
+ return Get(key, fallback_keys..., /*default_value=*/"");
+ }
+
+ // Returns the current value of the boolean system property `key`, or `default_value` if the
+ // property doesn't have a value. See `android::base::ParseBool` for how the value is parsed.
+ bool GetBool(const std::string& key, bool default_value) const {
+ android::base::ParseBoolResult result = android::base::ParseBool(GetProperty(key));
+ if (result != android::base::ParseBoolResult::kError) {
+ return result == android::base::ParseBoolResult::kTrue;
+ }
+ return default_value;
+ }
+
+ // Same as above, but allows specifying one or more fallback keys. The last argument is a bool
+ // default value that will be used if none of the given keys has a value.
+ //
+ // Usage:
+ //
+ // Look up for "key_1", then "key_2", then "key_3". If none of them has a value, return true:
+ // Get("key_1", "key_2", "key_3", /*default_value=*/true)
+ template <typename... Args>
+ bool GetBool(const std::string& key, const std::string& fallback_key, Args... args) const {
+ return GetBool(key, GetBool(fallback_key, args...));
+ }
+
+ protected:
+ // The single source of truth of system properties. Can be mocked in unit tests.
+ virtual std::string GetProperty(const std::string& key) const {
+ return android::base::GetProperty(key, /*default_value=*/"");
+ }
+};
+
+} // namespace tools
+} // namespace art
+
+#endif // ART_LIBARTTOOLS_TOOLS_SYSTEM_PROPERTIES_H_
diff --git a/libarttools/tools/system_properties_test.cc b/libarttools/tools/system_properties_test.cc
new file mode 100644
index 0000000..80300f0
--- /dev/null
+++ b/libarttools/tools/system_properties_test.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 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 "system_properties.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace tools {
+namespace {
+
+using ::testing::Return;
+
+class MockSystemProperties : public SystemProperties {
+ public:
+ MOCK_METHOD(std::string, GetProperty, (const std::string& key), (const, override));
+};
+
+class SystemPropertiesTest : public testing::Test {
+ protected:
+ MockSystemProperties system_properties_;
+};
+
+TEST_F(SystemPropertiesTest, Get) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("value_1"));
+ EXPECT_EQ(system_properties_.Get("key_1", /*default_value=*/"default"), "value_1");
+}
+
+TEST_F(SystemPropertiesTest, GetWithFallback) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_CALL(system_properties_, GetProperty("key_2")).WillOnce(Return("value_2"));
+ EXPECT_CALL(system_properties_, GetProperty("key_3")).WillOnce(Return("value_3"));
+ EXPECT_EQ(system_properties_.Get("key_1", "key_2", "key_3", /*default_value=*/"default"),
+ "value_2");
+}
+
+TEST_F(SystemPropertiesTest, GetDefault) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_EQ(system_properties_.Get("key_1", /*default_value=*/"default"), "default");
+}
+
+TEST_F(SystemPropertiesTest, GetOrEmpty) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("value_1"));
+ EXPECT_EQ(system_properties_.GetOrEmpty("key_1"), "value_1");
+}
+
+TEST_F(SystemPropertiesTest, GetOrEmptyWithFallback) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_CALL(system_properties_, GetProperty("key_2")).WillOnce(Return("value_2"));
+ EXPECT_CALL(system_properties_, GetProperty("key_3")).WillOnce(Return("value_3"));
+ EXPECT_EQ(system_properties_.GetOrEmpty("key_1", "key_2", "key_3"), "value_2");
+}
+
+TEST_F(SystemPropertiesTest, GetOrEmptyDefault) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_EQ(system_properties_.GetOrEmpty("key_1"), "");
+}
+
+TEST_F(SystemPropertiesTest, GetBoolTrue) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("true"));
+ EXPECT_EQ(system_properties_.GetBool("key_1", /*default_value=*/false), true);
+}
+
+TEST_F(SystemPropertiesTest, GetBoolFalse) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return("false"));
+ EXPECT_EQ(system_properties_.GetBool("key_1", /*default_value=*/true), false);
+}
+
+TEST_F(SystemPropertiesTest, GetBoolWithFallback) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_CALL(system_properties_, GetProperty("key_2")).WillOnce(Return("true"));
+ EXPECT_CALL(system_properties_, GetProperty("key_3")).WillOnce(Return("false"));
+ EXPECT_EQ(system_properties_.GetBool("key_1", "key_2", "key_3", /*default_value=*/false), true);
+}
+
+TEST_F(SystemPropertiesTest, GetBoolDefault) {
+ EXPECT_CALL(system_properties_, GetProperty("key_1")).WillOnce(Return(""));
+ EXPECT_EQ(system_properties_.GetBool("key_1", /*default_value=*/true), true);
+}
+
+} // namespace
+} // namespace tools
+} // namespace art
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index fdc57d0..9d9d8ce 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -288,14 +288,18 @@
":art-gtest-jars-GetMethodSignature",
":art-gtest-jars-Lookup",
":art-gtest-jars-Main",
+ ":art-gtest-jars-MainEmptyUncompressed",
":art-gtest-jars-MultiDex",
":art-gtest-jars-Nested",
],
header_libs: ["jni_headers"],
- shared_libs: [
- "libbacktrace",
+ static_libs: [
"libziparchive",
],
+ shared_libs: [
+ "libunwindstack",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
+ ],
}
// Version of ART gtest `art_libdexfile_tests` bundled with the ART APEX on target.
@@ -398,19 +402,6 @@
"art_libdexfile_external_tests_defaults",
],
- // Support multilib variants (using different suffix per sub-architecture), which is needed on
- // build targets with secondary architectures, as the CTS test suite packaging logic flattens
- // all test artifacts into a single `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
test_config_template: ":art-gtests-target-standalone-cts-template",
test_suites: ["cts"], // For backed-by API coverage.
}
@@ -495,20 +486,6 @@
"art_standalone_test_defaults",
"art_libdexfile_support_tests_defaults",
],
-
- // Support multilib variants (using different suffix per sub-architecture), which is needed on
- // build targets with secondary architectures, as the MTS test suite packaging logic flattens
- // all test artifacts into a single `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
test_suites: [
"mts-art",
],
diff --git a/libdexfile/art_standalone_libdexfile_tests.xml b/libdexfile/art_standalone_libdexfile_tests.xml
index 2c0e591..8e3ef2d 100644
--- a/libdexfile/art_standalone_libdexfile_tests.xml
+++ b/libdexfile/art_standalone_libdexfile_tests.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_libdexfile_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_libdexfile_tests->/data/local/tmp/art_standalone_libdexfile_tests/art_standalone_libdexfile_tests" />
@@ -25,6 +26,7 @@
<option name="push" value="art-gtest-jars-GetMethodSignature.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-GetMethodSignature.jar" />
<option name="push" value="art-gtest-jars-Lookup.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Lookup.jar" />
<option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Main.jar" />
+ <option name="push" value="art-gtest-jars-MainEmptyUncompressed.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-MainEmptyUncompressed.jar" />
<option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-MultiDex.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Nested.jar" />
</target_preparer>
diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index a7dd13e..1057bbc 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -19,9 +19,9 @@
#include <sys/stat.h>
#include "android-base/stringprintf.h"
-
#include "base/file_magic.h"
#include "base/file_utils.h"
+#include "base/logging.h"
#include "base/mem_map.h"
#include "base/mman.h" // For the PROT_* and MAP_* constants.
#include "base/stl_util.h"
@@ -114,18 +114,22 @@
return false;
}
+ if (zip_file_only_contains_uncompressed_dex != nullptr) {
+ // Start by assuming everything is uncompressed.
+ *zip_file_only_contains_uncompressed_dex = true;
+ }
+
uint32_t idx = 0;
std::string zip_entry_name = GetMultiDexClassesDexName(idx);
std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
if (zip_entry.get() == nullptr) {
- *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
- zip_entry_name.c_str(), error_msg->c_str());
- return false;
- }
-
- if (zip_file_only_contains_uncompressed_dex != nullptr) {
- // Start by assuming everything is uncompressed.
- *zip_file_only_contains_uncompressed_dex = true;
+ // A zip file with no dex code should be accepted. It's likely a config split APK, which we
+ // are currently passing from higher levels.
+ VLOG(dex) << StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)",
+ filename,
+ zip_entry_name.c_str(),
+ error_msg->c_str());
+ return true;
}
do {
diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
index 1863c1b..bf1d306 100644
--- a/libdexfile/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -103,6 +103,20 @@
EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
}
+TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksumsEmptyZip) {
+ std::string error_msg;
+ std::vector<uint32_t> checksums;
+ std::vector<std::string> dex_locations;
+ std::string multidex_file = GetTestDexFileName("MainEmptyUncompressed");
+ const ArtDexFileLoader dex_file_loader;
+ EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(
+ multidex_file.c_str(), &checksums, &dex_locations, &error_msg))
+ << error_msg;
+
+ EXPECT_EQ(dex_locations.size(), 0);
+ EXPECT_EQ(checksums.size(), 0);
+}
+
TEST_F(ArtDexFileLoaderTest, ClassDefs) {
std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
ASSERT_TRUE(raw.get() != nullptr);
diff --git a/libdexfile/dex/compact_dex_file.cc b/libdexfile/dex/compact_dex_file.cc
index a5044aa..205b829 100644
--- a/libdexfile/dex/compact_dex_file.cc
+++ b/libdexfile/dex/compact_dex_file.cc
@@ -22,9 +22,6 @@
namespace art {
-constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize];
-constexpr uint8_t CompactDexFile::kDexMagicVersion[];
-
void CompactDexFile::WriteMagic(uint8_t* magic) {
std::copy_n(kDexMagic, kDexMagicSize, magic);
}
diff --git a/libdexfile/dex/compact_offset_table.cc b/libdexfile/dex/compact_offset_table.cc
index 8601b19..deec124 100644
--- a/libdexfile/dex/compact_offset_table.cc
+++ b/libdexfile/dex/compact_offset_table.cc
@@ -21,8 +21,6 @@
namespace art {
-constexpr size_t CompactOffsetTable::kElementsPerIndex;
-
CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
uint32_t minimum_offset,
uint32_t table_offset)
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index dc5f27e..c9088ed 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -132,6 +132,11 @@
// any of the sections via a pointer.
CHECK_ALIGNED(begin_, alignof(Header));
+ if (DataSize() < sizeof(Header)) {
+ // Don't go further if the data doesn't even contain a header.
+ return;
+ }
+
InitializeSectionsFromMapList();
}
@@ -174,11 +179,13 @@
}
void DexFile::InitializeSectionsFromMapList() {
- const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
- if (header_->map_off_ == 0 || header_->map_off_ > DataSize()) {
+ static_assert(sizeof(MapList) <= sizeof(Header));
+ DCHECK_GE(DataSize(), sizeof(MapList));
+ if (header_->map_off_ == 0 || header_->map_off_ > DataSize() - sizeof(MapList)) {
// Bad offset. The dex file verifier runs after this method and will reject the file.
return;
}
+ const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
const size_t count = map_list->size_;
size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index cc5d2fd..37a601d 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -570,7 +570,7 @@
};
// Callback for "new locals table entry".
- typedef void (*DexDebugNewLocalCb)(void* context, const LocalInfo& entry);
+ using DexDebugNewLocalCb = void (*)(void* context, const LocalInfo& entry);
const dex::AnnotationsDirectoryItem* GetAnnotationsDirectory(const dex::ClassDef& class_def)
const {
diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h
index ecc0482..bf67187 100644
--- a/libdexfile/dex/dex_file_types.h
+++ b/libdexfile/dex/dex_file_types.h
@@ -17,6 +17,7 @@
#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
#define ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
+#include <functional>
#include <iosfwd>
#include <limits>
#include <utility>
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index f72528f..ec415ac 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -3470,7 +3470,7 @@
return false;
}
- // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+ // Flags allowed on methods, in general. Other lower-16-bit flags are to be ignored.
constexpr uint32_t kMethodAccessFlags = kAccPublic |
kAccPrivate |
kAccProtected |
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index 7e43f75..ff6fcf7 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -22,8 +22,8 @@
#include "base/globals.h"
#include "base/macros.h"
-typedef uint8_t uint4_t;
-typedef int8_t int4_t;
+using uint4_t = uint8_t;
+using int4_t = int8_t;
namespace art {
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 72949b0..a17545c 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -56,6 +56,9 @@
// Used by a class to denote that this class and any objects with this as a
// declaring-class/super-class are to be considered obsolete, meaning they should not be used by.
static constexpr uint32_t kAccObsoleteObject = 0x00200000; // class (runtime)
+// Set during boot image compilation to indicate that the class is
+// not initialized at compile tile and not in the list of preloaded classes.
+static constexpr uint32_t kAccInBootImageAndNotInPreloadedClasses = 0x00400000; // class (runtime)
// This is set by the class linker during LinkInterfaceMethods. It is used by a method
// to represent that it was copied from its declaring class into another class.
// We need copies of the original method because the method may end up in different
diff --git a/libdexfile/dex/utf.cc b/libdexfile/dex/utf.cc
index bfc704d..9692a26 100644
--- a/libdexfile/dex/utf.cc
+++ b/libdexfile/dex/utf.cc
@@ -133,45 +133,11 @@
}
// String contains non-ASCII characters.
- while (char_count--) {
- const uint16_t ch = *utf16_in++;
- if (ch > 0 && ch <= 0x7f) {
- *utf8_out++ = ch;
- } else {
- // Char_count == 0 here implies we've encountered an unpaired
- // surrogate and we have no choice but to encode it as 3-byte UTF
- // sequence. Note that unpaired surrogates can occur as a part of
- // "normal" operation.
- if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
- const uint16_t ch2 = *utf16_in;
-
- // Check if the other half of the pair is within the expected
- // range. If it isn't, we will have to emit both "halves" as
- // separate 3 byte sequences.
- if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
- utf16_in++;
- char_count--;
- const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
- *utf8_out++ = (code_point >> 18) | 0xf0;
- *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
- *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (code_point & 0x3f) | 0x80;
- continue;
- }
- }
-
- if (ch > 0x07ff) {
- // Three byte encoding.
- *utf8_out++ = (ch >> 12) | 0xe0;
- *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- } else /*(ch > 0x7f || ch == 0)*/ {
- // Two byte encoding.
- *utf8_out++ = (ch >> 6) | 0xc0;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- }
- }
- }
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
+ auto append = [&](char c) { *utf8_out++ = c; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(utf16_in, char_count, append);
}
int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length) {
@@ -240,34 +206,13 @@
}
}
-size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+size_t CountModifiedUtf8BytesInUtf16(const uint16_t* chars, size_t char_count) {
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
size_t result = 0;
- const uint16_t *end = chars + char_count;
- while (chars < end) {
- const uint16_t ch = *chars++;
- if (LIKELY(ch != 0 && ch < 0x80)) {
- result++;
- continue;
- }
- if (ch < 0x800) {
- result += 2;
- continue;
- }
- if (ch >= 0xd800 && ch < 0xdc00) {
- if (chars < end) {
- const uint16_t ch2 = *chars;
- // If we find a properly paired surrogate, we emit it as a 4 byte
- // UTF sequence. If we find an unpaired leading or trailing surrogate,
- // we emit it as a 3 byte sequence like would have done earlier.
- if (ch2 >= 0xdc00 && ch2 < 0xe000) {
- chars++;
- result += 4;
- continue;
- }
- }
- }
- result += 3;
- }
+ auto append = [&](char c ATTRIBUTE_UNUSED) { ++result; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(chars, char_count, append);
return result;
}
diff --git a/libdexfile/dex/utf.h b/libdexfile/dex/utf.h
index 35cbf78..d372bff 100644
--- a/libdexfile/dex/utf.h
+++ b/libdexfile/dex/utf.h
@@ -41,12 +41,6 @@
size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count);
/*
- * Returns the number of modified UTF-8 bytes needed to represent the given
- * UTF-16 string.
- */
-size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count);
-
-/*
* Convert from Modified UTF-8 to UTF-16.
*/
void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in);
@@ -85,8 +79,14 @@
void ConvertUtf16ToUtf8(const uint16_t* utf16, size_t char_count, Append&& append);
/*
+ * Returns the number of modified UTF-8 bytes needed to represent the given
+ * UTF-16 string.
+ */
+size_t CountModifiedUtf8BytesInUtf16(const uint16_t* chars, size_t char_count);
+
+/*
* Convert from UTF-16 to Modified UTF-8. Note that the output is _not_
- * NUL-terminated. You probably need to call CountUtf8Bytes before calling
+ * NUL-terminated. You probably need to call CountModifiedUtf8BytesInUtf16 before calling
* this anyway, so if you want a NUL-terminated string, you know where to
* put the NUL byte.
*/
diff --git a/libdexfile/dex/utf_test.cc b/libdexfile/dex/utf_test.cc
index 919259e..85c74d2 100644
--- a/libdexfile/dex/utf_test.cc
+++ b/libdexfile/dex/utf_test.cc
@@ -117,7 +117,7 @@
static void AssertConversion(const std::vector<uint16_t>& input,
const std::vector<uint8_t>& expected) {
- ASSERT_EQ(expected.size(), CountUtf8Bytes(&input[0], input.size()));
+ ASSERT_EQ(expected.size(), CountModifiedUtf8BytesInUtf16(&input[0], input.size()));
std::vector<uint8_t> output(expected.size());
ConvertUtf16ToModifiedUtf8(reinterpret_cast<char*>(&output[0]), expected.size(),
@@ -229,7 +229,7 @@
return len;
}
-static size_t CountUtf8Bytes_reference(const uint16_t* chars, size_t char_count) {
+static size_t CountModifiedUtf8BytesInUtf16_reference(const uint16_t* chars, size_t char_count) {
size_t result = 0;
while (char_count--) {
const uint16_t ch = *chars++;
@@ -320,8 +320,8 @@
int char_count_test, char_count_reference;
// Calculate the number of utf-8 bytes for the utf-16 chars.
- byte_count_reference = CountUtf8Bytes_reference(buf, char_count);
- byte_count_test = CountUtf8Bytes(buf, char_count);
+ byte_count_reference = CountModifiedUtf8BytesInUtf16_reference(buf, char_count);
+ byte_count_test = CountModifiedUtf8BytesInUtf16(buf, char_count);
EXPECT_EQ(byte_count_reference, byte_count_test);
// Convert the utf-16 string to utf-8 bytes.
diff --git a/libdexfile/external/include/art_api/dex_file_external.h b/libdexfile/external/include/art_api/dex_file_external.h
index 360be92..d9db200 100644
--- a/libdexfile/external/include/art_api/dex_file_external.h
+++ b/libdexfile/external/include/art_api/dex_file_external.h
@@ -28,10 +28,10 @@
// may only be added here. C++ users should use dex_file_support.h instead.
struct ADexFile;
-typedef struct ADexFile ADexFile;
+typedef struct ADexFile ADexFile; // NOLINT
struct ADexFile_Method;
-typedef struct ADexFile_Method ADexFile_Method;
+typedef struct ADexFile_Method ADexFile_Method; // NOLINT
enum ADexFile_Error : uint32_t {
ADEXFILE_ERROR_OK = 0,
@@ -39,10 +39,11 @@
ADEXFILE_ERROR_INVALID_HEADER = 2,
ADEXFILE_ERROR_NOT_ENOUGH_DATA = 3,
};
-typedef enum ADexFile_Error ADexFile_Error;
+typedef enum ADexFile_Error ADexFile_Error; // NOLINT
// Callback used to return information about a dex method.
// The method information is valid only during the callback.
+// NOLINTNEXTLINE
typedef void ADexFile_MethodCallback(void* _Nullable callback_data,
const ADexFile_Method* _Nonnull method);
diff --git a/libelffile/dwarf/register.h b/libelffile/dwarf/register.h
index 7742ec4..33c6795 100644
--- a/libelffile/dwarf/register.h
+++ b/libelffile/dwarf/register.h
@@ -40,6 +40,8 @@
static Reg ArmDp(int num) { return Reg(256 + num); } // D0–D31.
static Reg Arm64Core(int num) { return Reg(num); } // X0-X31.
static Reg Arm64Fp(int num) { return Reg(64 + num); } // V0-V31.
+ static Reg Riscv64Core(int num) { return Reg(num); } // X0-X31
+ static Reg Riscv64Fp(int num) { return Reg(32 + num); } // F0-F31
static Reg X86Core(int num) { return Reg(num); }
static Reg X86Fp(int num) { return Reg(21 + num); }
static Reg X86_64Core(int num) {
diff --git a/libelffile/elf/elf_builder.h b/libelffile/elf/elf_builder.h
index 086bd41..6720569 100644
--- a/libelffile/elf/elf_builder.h
+++ b/libelffile/elf/elf_builder.h
@@ -814,6 +814,8 @@
return InstructionSet::kThumb2;
case EM_AARCH64:
return InstructionSet::kArm64;
+ case EM_RISCV:
+ return InstructionSet::kRiscv64;
case EM_386:
return InstructionSet::kX86;
case EM_X86_64:
@@ -839,6 +841,11 @@
elf_header.e_flags = 0;
break;
}
+ case InstructionSet::kRiscv64: {
+ elf_header.e_machine = EM_RISCV;
+ elf_header.e_flags = EF_RISCV_RVC | EF_RISCV_FLOAT_ABI_DOUBLE;
+ break;
+ }
case InstructionSet::kX86: {
elf_header.e_machine = EM_386;
elf_header.e_flags = 0;
diff --git a/libelffile/elf/elf_debug_reader.h b/libelffile/elf/elf_debug_reader.h
index 266c638..6e01989 100644
--- a/libelffile/elf/elf_debug_reader.h
+++ b/libelffile/elf/elf_debug_reader.h
@@ -35,11 +35,11 @@
class ElfDebugReader {
public:
// Note that the input buffer might be misaligned.
- typedef typename ElfTypes::Ehdr ALIGNED(1) Elf_Ehdr;
- typedef typename ElfTypes::Phdr ALIGNED(1) Elf_Phdr;
- typedef typename ElfTypes::Shdr ALIGNED(1) Elf_Shdr;
- typedef typename ElfTypes::Sym ALIGNED(1) Elf_Sym;
- typedef typename ElfTypes::Addr ALIGNED(1) Elf_Addr;
+ using Elf_Ehdr ALIGNED(1) = typename ElfTypes::Ehdr;
+ using Elf_Phdr ALIGNED(1) = typename ElfTypes::Phdr;
+ using Elf_Shdr ALIGNED(1) = typename ElfTypes::Shdr;
+ using Elf_Sym ALIGNED(1) = typename ElfTypes::Sym;
+ using Elf_Addr ALIGNED(1) = typename ElfTypes::Addr;
// Call Frame Information.
struct CFI {
diff --git a/libelffile/elf/elf_utils.h b/libelffile/elf/elf_utils.h
index 9c4f0d81..46b25b0 100644
--- a/libelffile/elf/elf_utils.h
+++ b/libelffile/elf/elf_utils.h
@@ -65,10 +65,21 @@
#define EI_ABIVERSION 8
#define EM_ARM 40
+#if !defined(STV_DEFAULT)
#define STV_DEFAULT 0
+#endif
#define EM_AARCH64 183
+#ifndef EM_RISCV
+#define EM_RISCV 243
+#endif
+
+#ifndef EF_RISCV_RVC
+#define EF_RISCV_RVC 0x1
+#define EF_RISCV_FLOAT_ABI_DOUBLE 0x4
+#endif
+
#define DT_BIND_NOW 24
#define DT_INIT_ARRAY 25
#define DT_FINI_ARRAY 26
diff --git a/libelffile/stream/error_delaying_output_stream.h b/libelffile/stream/error_delaying_output_stream.h
index b37ff4e..7553de6 100644
--- a/libelffile/stream/error_delaying_output_stream.h
+++ b/libelffile/stream/error_delaying_output_stream.h
@@ -71,8 +71,9 @@
PLOG(ERROR) << "Failed to seek in " << GetLocation() << ". Offset=" << offset
<< " whence=" << whence << " new_offset=" << new_offset;
output_good_ = false;
+ } else {
+ DCHECK_EQ(actual_offset, new_offset);
}
- DCHECK_EQ(actual_offset, new_offset);
}
output_offset_ = new_offset;
return new_offset;
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
index daf87f4..71f76a6 100644
--- a/libnativebridge/OWNERS
+++ b/libnativebridge/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 86431
+mast@google.com
dimitry@google.com
eaeltsin@google.com
ngeoffray@google.com
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 2199bab..5904c0f 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -40,7 +40,7 @@
// Function pointer type for sigaction. This is mostly the signature of a signal handler, except
// for the return type. The runtime needs to know whether the signal was handled or should be given
// to the chain.
-typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
+typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*); // NOLINT
// Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
// signals that we do not want to load a native bridge.
diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt
index 8b51ab8..52d06eb 100644
--- a/libnativebridge/libnativebridge.map.txt
+++ b/libnativebridge/libnativebridge.map.txt
@@ -20,12 +20,12 @@
# that defines the exported interface. Please keep in sync with this list.
LIBNATIVEBRIDGE_1 {
global:
- NativeBridgeGetError;
- NativeBridgeInitialized;
- NativeBridgeGetTrampoline;
- PreInitializeNativeBridge;
- NativeBridgeAvailable;
- NeedsNativeBridge;
+ NativeBridgeGetError; # apex
+ NativeBridgeInitialized; # apex
+ NativeBridgeGetTrampoline; # apex
+ PreInitializeNativeBridge; # apex
+ NativeBridgeAvailable; # apex
+ NeedsNativeBridge; # apex
local:
*;
};
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 57d26c0..603f97a 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -180,20 +180,6 @@
],
header_libs: ["libbase_headers"],
- // Support multilib variants (using different suffix per sub-architecture),
- // which is needed on build targets with secondary architectures, as the CTS
- // test suite packaging logic flattens all test artifacts into a single
- // `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
test_config_template: ":art-gtests-target-standalone-cts-template",
test_suites: [
"cts",
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index e14aea2..f1ef32d 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -64,7 +64,7 @@
"libdl_android",
],
static_libs: [
- "PlatformProperties",
+ "libPlatformProperties",
],
},
},
@@ -164,19 +164,6 @@
"libnativeloader",
],
- // Support multilib variants (using different suffix per sub-architecture), which is needed on
- // build targets with secondary architectures, as the CTS test suite packaging logic flattens
- // all test artifacts into a single `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
// Added to CTS for API coverage of libnativeloader which is backed by the
// ART module.
test_config_template: ":art-gtests-target-standalone-cts-template",
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index f735653..87f9142 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1,6 +1,6 @@
-dimitry@google.com
+# Bug component: 86431
+mast@google.com
jiyong@google.com
ngeoffray@google.com
oth@google.com
-mast@google.com
rpl@google.com
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
index c5ace61..919feff 100644
--- a/libnativeloader/README.md
+++ b/libnativeloader/README.md
@@ -10,11 +10,11 @@
The most typical use case of this library is calling `System.loadLibrary(name)`.
When the method is called, the ART runtime delegates the call to this library
-along with the reference to the classloader where the call was made. Then this
-library finds the linker namespace (named `classloader-namespace`) that is
-associated with the given classloader, and tries to load the requested library
-from the namespace. The actual searching, loading, and linking of the library
-is performed by the dynamic linker.
+along with the reference to the classloader where the call was made. Then this
+library finds the linker namespace (typically with the name `clns-` followed by
+a number to make it unique) that is associated with the given classloader, and
+tries to load the requested library from that namespace. The actual searching,
+loading, and linking of the library is performed by the dynamic linker.
The linker namespace is created when an APK is loaded into the process, and is
associated with the classloader that loaded the APK. The linker namespace is
diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt
index 59f457c..8c0fbdd 100644
--- a/libnativeloader/libnativeloader.map.txt
+++ b/libnativeloader/libnativeloader.map.txt
@@ -20,13 +20,13 @@
# that defines the exported interface. Please keep in sync with this list.
LIBNATIVELOADER_1 {
global:
- OpenNativeLibrary;
- CloseNativeLibrary;
- OpenNativeLibraryInNamespace;
- FindNamespaceByClassLoader;
- FindNativeLoaderNamespaceByClassLoader;
- CreateClassLoaderNamespace;
- NativeLoaderFreeErrorMessage;
+ OpenNativeLibrary; # apex
+ CloseNativeLibrary; # apex
+ OpenNativeLibraryInNamespace; # apex
+ FindNamespaceByClassLoader; # apex
+ FindNativeLoaderNamespaceByClassLoader; # apex
+ CreateClassLoaderNamespace; # apex
+ NativeLoaderFreeErrorMessage; # apex
local:
*;
};
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index bcc19aa..9aeebf3 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -30,6 +30,7 @@
#include <android-base/macros.h>
#include <android-base/result.h>
#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
#include <nativehelper/scoped_utf_chars.h>
#include "nativeloader/dlext_namespaces.h"
@@ -55,24 +56,26 @@
// vndk_product namespace for unbundled product apps
constexpr const char* kVndkProductNamespaceName = "vndk_product";
-// classloader-namespace is a linker namespace that is created for the loaded
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// system namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for vendor APKs.
-constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-// If the namespace is shared then add this suffix to form
-// "classloader-namespace-shared" or "vendor-classloader-namespace-shared",
-// respectively. A shared namespace (cf. ANDROID_NAMESPACE_TYPE_SHARED) has
+// clns-XX is a linker namespace that is created for normal apps installed in
+// the data partition. To be specific, it is created for the app classloader.
+// When System.load() is called from a Java class that is loaded from the
+// classloader, the clns namespace associated with that classloader is selected
+// for dlopen. The namespace is configured so that its search path is set to the
+// app-local JNI directory and it is linked to the system namespace with the
+// names of libs listed in the public.libraries.txt and other public libraries.
+// This way an app can only load its own JNI libraries along with the public
+// libs.
+constexpr const char* kClassloaderNamespaceName = "clns";
+// Same thing for unbundled APKs in the vendor partition.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-clns";
+// Same thing for unbundled APKs in the product partition.
+constexpr const char* kProductClassloaderNamespaceName = "product-clns";
+// If the namespace is shared then add this suffix to help identify it in debug
+// messages. A shared namespace (cf. ANDROID_NAMESPACE_TYPE_SHARED) has
// inherited all the libraries of the parent classloader namespace, or the
-// system namespace for the main app classloader. It is used to give full
-// access to the platform libraries for apps bundled in the system image,
-// including their later updates installed in /data.
+// system namespace for the main app classloader. It is used to give full access
+// to the platform libraries for apps bundled in the system image, including
+// their later updates installed in /data.
constexpr const char* kSharedNamespaceSuffix = "-shared";
// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
@@ -89,7 +92,7 @@
const std::regex kVendorDexPathRegex("(^|:)(/system)?/vendor/");
const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
-// Define origin of APK if it is from vendor partition or product partition
+// Define origin partition of APK
using ApkOrigin = enum {
APK_ORIGIN_DEFAULT = 0,
APK_ORIGIN_VENDOR = 1, // Includes both /vendor and /system/vendor
@@ -265,7 +268,7 @@
system_exposed_libraries = system_exposed_libraries + ':' + llndk_libraries_product();
// Different name is useful for debugging
- namespace_name = kVendorClassloaderNamespaceName;
+ namespace_name = kProductClassloaderNamespaceName;
}
}
@@ -275,6 +278,11 @@
namespace_name = namespace_name + kSharedNamespaceSuffix;
}
+ // Append a unique number to the namespace name, to tell them apart when
+ // debugging linker issues, e.g. with debug.ld.all set to "dlopen,dlerror".
+ static int clns_count = 0;
+ namespace_name = android::base::StringPrintf("%s-%d", namespace_name.c_str(), ++clns_count);
+
ALOGD(
"Configuring %s for %s %s. target_sdk_version=%u, uses_libraries=%s, library_path=%s, "
"permitted_path=%s",
@@ -291,8 +299,7 @@
// they are to other apps, including those in system, system_ext, and
// product partitions. The reason is that when GSI is used, the system
// partition may get replaced, and then vendor apps may fail. It's fine for
- // product (and system_ext) apps, because those partitions aren't mounted in
- // GSI tests.
+ // product apps, because that partition isn't mounted in GSI tests.
auto libs =
filter_public_libraries(target_sdk_version, uses_libraries, extended_public_libraries());
if (!libs.empty()) {
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index 080b4d6..6c0c8b1 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -32,8 +32,9 @@
using ::testing::Eq;
using ::testing::NotNull;
+using ::testing::StartsWith;
using ::testing::StrEq;
-using internal::ConfigEntry;
+using internal::ConfigEntry; // NOLINT - ConfigEntry is actually used
using internal::ParseApexLibrariesConfig;
using internal::ParseConfig;
@@ -68,7 +69,7 @@
void SetExpectations() {
std::vector<std::string> default_public_libs =
android::base::Split(preloadable_public_libraries(), ":");
- for (auto l : default_public_libs) {
+ for (const std::string& l : default_public_libs) {
EXPECT_CALL(*mock,
mock_dlopen_ext(false, StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE, NotNull()))
.WillOnce(Return(any_nonnull));
@@ -191,7 +192,7 @@
std::string permitted_path = "/data/app/foo/" LIB_DIR;
// expected output (.. for the default test inputs)
- std::string expected_namespace_name = "classloader-namespace";
+ std::string expected_namespace_prefix = "clns";
uint64_t expected_namespace_flags =
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
std::string expected_library_path = library_path;
@@ -227,7 +228,7 @@
EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(testing::AnyNumber());
EXPECT_CALL(*mock, mock_create_namespace(
- Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
StrEq(expected_library_path), expected_namespace_flags,
StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
.WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
@@ -322,7 +323,7 @@
dex_path = "/system/app/foo/foo.apk";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
@@ -332,7 +333,7 @@
dex_path = "/vendor/app/foo/foo.apk";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
@@ -342,7 +343,7 @@
dex_path = "/vendor/app/foo/foo.apk";
is_shared = false;
- expected_namespace_name = "vendor-classloader-namespace";
+ expected_namespace_prefix = "vendor-clns";
expected_library_path = expected_library_path + ":/vendor/" LIB_DIR;
expected_permitted_path = expected_permitted_path + ":/vendor/" LIB_DIR;
expected_shared_libs_to_platform_ns =
@@ -356,7 +357,7 @@
dex_path = "/product/app/foo/foo.apk";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
@@ -366,7 +367,7 @@
dex_path = "/system/framework/services.jar:/apex/com.android.conscrypt/javalib/service-foo.jar";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
expected_link_with_conscrypt_ns = true;
SetExpectations();
@@ -378,7 +379,7 @@
is_shared = false;
if (is_product_vndk_version_defined()) {
- expected_namespace_name = "vendor-classloader-namespace";
+ expected_namespace_prefix = "product-clns";
expected_library_path = expected_library_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
expected_permitted_path =
expected_permitted_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
@@ -416,7 +417,7 @@
const std::string second_app_permitted_path = "/data/app/bar/" LIB_DIR;
const std::string expected_second_app_permitted_path =
std::string("/data:/mnt/expand:") + second_app_permitted_path;
- const std::string expected_second_app_parent_namespace = "classloader-namespace";
+ const std::string expected_second_app_parent_namespace = "clns";
// no ALSO_USED_AS_ANONYMOUS
const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
@@ -429,7 +430,7 @@
// namespace for the second app is created. Its parent is set to the namespace
// of the first app.
EXPECT_CALL(*mock, mock_create_namespace(
- Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
StrEq(second_app_library_path), expected_second_namespace_flags,
StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
.WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
diff --git a/libnativeloader/native_loader_test.h b/libnativeloader/native_loader_test.h
index 5c51f00..30b2f84 100644
--- a/libnativeloader/native_loader_test.h
+++ b/libnativeloader/native_loader_test.h
@@ -42,7 +42,7 @@
// Instead of having two set of mock APIs for the two, define only one set with an additional
// argument 'bool bridged' to identify the context (i.e., called for libdl_android or
// libnativebridge).
- typedef char* mock_namespace_handle;
+ using mock_namespace_handle = char*;
virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
const char* search_paths) = 0;
virtual mock_namespace_handle mock_create_namespace(
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index ffebe0b..fae4d76 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -46,7 +46,6 @@
using internal::ConfigEntry;
using internal::ParseConfig;
using internal::ParseApexLibrariesConfig;
-using std::literals::string_literals::operator""s;
namespace {
@@ -117,7 +116,7 @@
if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
const std::string company_name(fn);
- const std::string config_file_path = dirname + "/"s + filename;
+ const std::string config_file_path = std::string(dirname) + std::string("/") + filename;
LOG_ALWAYS_FATAL_IF(
company_name.empty(),
"Error extracting company name from public native library list file path \"%s\"",
@@ -129,8 +128,11 @@
android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
return true;
} else {
- return Errorf("Library name \"{}\" does not end with the company name {}.",
- entry.soname, company_name);
+ return Errorf(
+ "Library name \"{}\" does not start with \"lib\" and/or "
+ "does not end with the company name \"{}\".",
+ entry.soname,
+ company_name);
}
});
if (ret.ok()) {
@@ -161,28 +163,36 @@
}
// If this is for preloading libs, don't remove the libs from APEXes.
- if (for_preload) {
- return android::base::Join(*sonames, ':');
+ if (!for_preload) {
+ // Remove the public libs provided by apexes because these libs are available
+ // from apex namespaces.
+ for (const auto& p : apex_public_libraries()) {
+ auto public_libs = base::Split(p.second, ":");
+ sonames->erase(std::remove_if(sonames->begin(),
+ sonames->end(),
+ [&public_libs](const std::string& v) {
+ return std::find(public_libs.begin(), public_libs.end(), v) !=
+ public_libs.end();
+ }),
+ sonames->end());
+ }
}
- // Remove the public libs provided by apexes because these libs are available
- // from apex namespaces.
- for (const auto& p : apex_public_libraries()) {
- auto public_libs = base::Split(p.second, ":");
- sonames->erase(std::remove_if(sonames->begin(), sonames->end(), [&public_libs](const std::string& v) {
- return std::find(public_libs.begin(), public_libs.end(), v) != public_libs.end();
- }), sonames->end());
- }
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitDefaultPublicLibraries for_preload=%d: %s", for_preload, libs.c_str());
+ return libs;
}
static std::string InitVendorPublicLibraries() {
// This file is optional, quietly ignore if the file does not exist.
auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
if (!sonames.ok()) {
+ ALOGI("InitVendorPublicLibraries skipped: %s", sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitVendorPublicLibraries: %s", libs.c_str());
+ return libs;
}
// If ro.product.vndk.version is defined, /product/etc/public.libraries-<companyname>.txt contains
@@ -193,7 +203,9 @@
if (is_product_vndk_version_defined()) {
ReadExtensionLibraries("/product/etc", &sonames);
}
- return android::base::Join(sonames, ':');
+ std::string libs = android::base::Join(sonames, ':');
+ ALOGD("InitProductPublicLibraries: %s", libs.c_str());
+ return libs;
}
// read /system/etc/public.libraries-<companyname>.txt,
@@ -208,7 +220,9 @@
if (!is_product_vndk_version_defined()) {
ReadExtensionLibraries("/product/etc", &sonames);
}
- return android::base::Join(sonames, ':');
+ std::string libs = android::base::Join(sonames, ':');
+ ALOGD("InitExtendedPublicLibraries: %s", libs.c_str());
+ return libs;
}
static std::string InitLlndkLibrariesVendor() {
@@ -219,11 +233,14 @@
LOG_ALWAYS_FATAL("%s: %s", config_file.c_str(), sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitLlndkLibrariesVendor: %s", libs.c_str());
+ return libs;
}
static std::string InitLlndkLibrariesProduct() {
if (!is_product_vndk_version_defined()) {
+ ALOGD("InitLlndkLibrariesProduct: No product VNDK version defined");
return "";
}
std::string config_file = kLlndkLibrariesFile;
@@ -233,7 +250,9 @@
LOG_ALWAYS_FATAL("%s: %s", config_file.c_str(), sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitLlndkLibrariesProduct: %s", libs.c_str());
+ return libs;
}
static std::string InitVndkspLibrariesVendor() {
@@ -244,11 +263,14 @@
LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitVndkspLibrariesVendor: %s", libs.c_str());
+ return libs;
}
static std::string InitVndkspLibrariesProduct() {
if (!is_product_vndk_version_defined()) {
+ ALOGD("InitVndkspLibrariesProduct: No product VNDK version defined");
return "";
}
std::string config_file = kVndkLibrariesFile;
@@ -258,20 +280,33 @@
LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitVndkspLibrariesProduct: %s", libs.c_str());
+ return libs;
}
static std::map<std::string, std::string> InitApexLibraries(const std::string& tag) {
std::string file_content;
if (!base::ReadFileToString(kApexLibrariesConfigFile, &file_content)) {
// config is optional
+ ALOGI("InitApexLibraries skipped: %s", strerror(errno));
return {};
}
- auto config = ParseApexLibrariesConfig(file_content, tag);
+ Result<std::map<std::string, std::string>> config = ParseApexLibrariesConfig(file_content, tag);
if (!config.ok()) {
LOG_ALWAYS_FATAL("%s: %s", kApexLibrariesConfigFile, config.error().message().c_str());
return {};
}
+ ALOGD("InitApexLibraries:\n %s",
+ [&config]() {
+ std::vector<std::string> lib_list;
+ lib_list.reserve(config->size());
+ for (std::pair<std::string, std::string> elem : *config) {
+ lib_list.emplace_back(elem.first + ": " + elem.second);
+ }
+ return android::base::Join(lib_list, "\n ");
+ }()
+ .c_str());
return *config;
}
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index b43a02c..95b9b88 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -23,10 +23,32 @@
default_applicable_licenses: ["art_license"],
}
+// A native library that goes into /system or /system_ext and that depends on
+// a non-public library that is linked from the system namespace.
cc_library {
- name: "libnativeloader_testlib",
- srcs: [],
+ name: "libsystem_testlib",
+ min_sdk_version: "31",
+ stl: "libc++_static",
+ shared_libs: ["liblog"],
+ // It's difficult to add a shared_lib dependency on a non-public library
+ // here, so it dlopens one instead.
+ srcs: ["libsystem_testlib.cc"],
+}
+
+// A native library that goes into /product.
+cc_library {
+ name: "libproduct_testlib",
+ min_sdk_version: "31",
stl: "none",
+ srcs: [],
+}
+
+// A native library that goes into /vendor.
+cc_library {
+ name: "libvendor_testlib",
+ min_sdk_version: "31",
+ stl: "none",
+ srcs: [],
}
// This app is just an intermediate container to be able to include the .so
@@ -34,13 +56,19 @@
android_test_helper_app {
name: "library_container_app",
defaults: ["art_module_source_build_java_defaults"],
+ min_sdk_version: "31",
manifest: "library_container_app_manifest.xml",
compile_multilib: "both",
- jni_libs: ["libnativeloader_testlib"],
+ jni_libs: [
+ "libsystem_testlib",
+ "libproduct_testlib",
+ "libvendor_testlib",
+ ],
}
java_library {
name: "loadlibrarytest_test_utils",
+ sdk_version: "31",
static_libs: [
"androidx.test.ext.junit",
"androidx.test.ext.truth",
@@ -51,19 +79,52 @@
// Test fixture that represents a shared library in /system/framework.
java_library {
name: "libnativeloader_system_shared_lib",
+ sdk_version: "31",
installable: true,
srcs: ["src/android/test/systemsharedlib/SystemSharedLib.java"],
}
+// Test fixture that represents a shared library in /system_ext/framework.
+java_library {
+ name: "libnativeloader_system_ext_shared_lib",
+ sdk_version: "31",
+ installable: true,
+ srcs: ["src/android/test/systemextsharedlib/SystemExtSharedLib.java"],
+}
+
+// Test fixture that represents a shared library in /product/framework.
+java_library {
+ name: "libnativeloader_product_shared_lib",
+ product_specific: true,
+ sdk_version: "31",
+ installable: true,
+ srcs: ["src/android/test/productsharedlib/ProductSharedLib.java"],
+}
+
+// Test fixture that represents a shared library in /vendor/framework.
+java_library {
+ name: "libnativeloader_vendor_shared_lib",
+ vendor: true,
+ sdk_version: "31",
+ installable: true,
+ srcs: ["src/android/test/vendorsharedlib/VendorSharedLib.java"],
+}
+
java_defaults {
name: "loadlibrarytest_app_defaults",
defaults: ["art_module_source_build_java_defaults"],
+ sdk_version: "31",
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
"loadlibrarytest_test_utils",
],
- libs: ["libnativeloader_system_shared_lib"],
+ libs: [
+ "libnativeloader_system_shared_lib",
+ "libnativeloader_system_ext_shared_lib",
+ "libnativeloader_product_shared_lib",
+ "libnativeloader_vendor_shared_lib",
+ ],
}
android_test_helper_app {
@@ -125,6 +186,9 @@
data: [
":library_container_app",
":libnativeloader_system_shared_lib",
+ ":libnativeloader_system_ext_shared_lib",
+ ":libnativeloader_product_shared_lib",
+ ":libnativeloader_vendor_shared_lib",
":loadlibrarytest_system_priv_app",
":loadlibrarytest_system_app",
":loadlibrarytest_system_ext_app",
diff --git a/libnativeloader/test/libnativeloader_e2e_tests.xml b/libnativeloader/test/libnativeloader_e2e_tests.xml
index b1333b0..d4f6348 100644
--- a/libnativeloader/test/libnativeloader_e2e_tests.xml
+++ b/libnativeloader/test/libnativeloader_e2e_tests.xml
@@ -16,9 +16,28 @@
<configuration description="Config for libnativeloader e2e test cases">
<option name="test-suite-tag" value="libnativeloader_e2e_tests" />
<option name="test-suite-tag" value="apct" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <!-- We push all files in LibnativeloaderTest.java, but use this
+ preparer to make partitions writable. That since remounting may
+ require a device reboot, and then it's important that the
+ `setenforce 0` from DisableSELinuxTargetPreparer occurs after that. -->
+ <option name="remount-system" value="true" />
+ <option name="remount-vendor" value="true" />
+ </target_preparer>
+
+ <!-- Vendor native libraries aren't accessible by any apps by sepolicy
+ rules. For that they need to be labelled same_process_hal_file in a
+ vendor specific file_contexts file (see
+ https://source.android.com/docs/core/permissions/namespaces_libraries#adding-additional-native-libraries).
+ To avoid setting that up to test loading libvendor_private*.so, disable
+ sepolicy checks while running the tests. It's libnativeloader logic we
+ want to test here. -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer"/>
+
<test class="com.android.tradefed.testtype.HostTest" >
<option name="jar" value="libnativeloader_e2e_tests.jar" />
</test>
diff --git a/libnativeloader/test/libsystem_testlib.cc b/libnativeloader/test/libsystem_testlib.cc
new file mode 100644
index 0000000..97d3223
--- /dev/null
+++ b/libnativeloader/test/libsystem_testlib.cc
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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 <dlfcn.h>
+
+#include "log/log.h"
+
+static void __attribute__((constructor)) ctor() {
+ // Load a library that should be available to system libraries through a
+ // linked namespace (i.e. is not directly in /system/${LIB}), and that is not
+ // in public.libraries.txt. We use a real one to avoid having to set up an
+ // APEX test fixture and rerun linkerconfig.
+ void* h = dlopen("libandroidicu.so", RTLD_NOW);
+ if (h == nullptr) {
+ LOG_ALWAYS_FATAL("Failed to load dependency: %s", dlerror());
+ }
+ dlclose(h);
+}
diff --git a/libnativeloader/test/loadlibrarytest_data_app_manifest.xml b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
index 9b663e6..4be78f8 100644
--- a/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
@@ -20,13 +20,19 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.test.app.data" />
<application>
- <uses-library android:name="android.test.systemsharedlib" />
- <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
- <uses-native-library android:required="false" android:name="libbar.oem1.so" />
- <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
- <!--libbar.oem2.so is left out -->
- <uses-native-library android:required="false" android:name="libfoo.product1.so" />
- <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem2.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub1.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub2.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub3.oem1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
</application>
</manifest>
diff --git a/libnativeloader/test/loadlibrarytest_product_app_manifest.xml b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
index c1d997a..9061c39 100644
--- a/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
@@ -20,13 +20,19 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.test.app.product" />
<application>
- <uses-library android:name="android.test.systemsharedlib" />
- <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
- <uses-native-library android:required="false" android:name="libbar.oem1.so" />
- <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
- <!--libbar.oem2.so is left out -->
- <uses-native-library android:required="false" android:name="libfoo.product1.so" />
- <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem2.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub1.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub2.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub3.oem1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
</application>
</manifest>
diff --git a/libnativeloader/test/loadlibrarytest_system_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
index 5c6af09..7ee7532 100644
--- a/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
@@ -20,11 +20,16 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.test.app.system" />
<application>
- <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
<!-- System apps get a shared classloader namespace, so they don't need
- uses-native-library entries for anything in /system. -->
- <uses-native-library android:required="false" android:name="libfoo.product1.so" />
- <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ uses-native-library entries for anything in /system or /system_ext. -->
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
</application>
</manifest>
diff --git a/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
index 961f9ba..a2c1a2c 100644
--- a/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
@@ -20,11 +20,16 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.test.app.system_ext" />
<application>
- <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
<!-- System apps get a shared classloader namespace, so they don't need
- uses-native-library entries for anything in /system. -->
- <uses-native-library android:required="false" android:name="libfoo.product1.so" />
- <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ uses-native-library entries for anything in /system or /system_ext. -->
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
</application>
</manifest>
diff --git a/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
index f4bf3c0..87a30ce 100644
--- a/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
@@ -20,11 +20,16 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.test.app.system_priv" />
<application>
- <uses-library android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
<!-- System apps get a shared classloader namespace, so they don't need
- uses-native-library entries for anything in /system. -->
- <uses-native-library android:required="false" android:name="libfoo.product1.so" />
- <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ uses-native-library entries for anything in /system or /system_ext. -->
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
</application>
</manifest>
diff --git a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
index 1a8cbcc..b3434fb 100644
--- a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
+++ b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
@@ -20,13 +20,19 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.test.app.vendor" />
<application>
- <uses-library android:name="android.test.systemsharedlib" />
- <uses-native-library android:required="false" android:name="libfoo.oem1.so" />
- <uses-native-library android:required="false" android:name="libbar.oem1.so" />
- <uses-native-library android:required="false" android:name="libfoo.oem2.so" />
- <!--libbar.oem2.so is left out -->
- <uses-native-library android:required="false" android:name="libfoo.product1.so" />
- <uses-native-library android:required="false" android:name="libbar.product1.so" />
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem2.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub1.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub2.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub3.oem1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
</application>
</manifest>
diff --git a/libnativeloader/test/src/android/test/app/DataAppTest.java b/libnativeloader/test/src/android/test/app/DataAppTest.java
index db97e8d..9403494 100644
--- a/libnativeloader/test/src/android/test/app/DataAppTest.java
+++ b/libnativeloader/test/src/android/test/app/DataAppTest.java
@@ -17,39 +17,96 @@
package android.test.app;
import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
import android.test.systemsharedlib.SystemSharedLib;
-import androidx.test.filters.SmallTest;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-@SmallTest
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class DataAppTest {
@Test
public void testLoadExtendedPublicLibraries() {
- System.loadLibrary("foo.oem1");
- System.loadLibrary("bar.oem1");
- System.loadLibrary("foo.oem2");
- TestUtils.assertLinkerNamespaceError(
- () -> System.loadLibrary("bar.oem2")); // Missing <uses-native-library>.
- System.loadLibrary("foo.product1");
- System.loadLibrary("bar.product1");
+ System.loadLibrary("system_extpub.oem1");
+ System.loadLibrary("system_extpub.oem2");
+ System.loadLibrary("system_extpub1.oem1");
+ TestUtils.assertLinkerNamespaceError( // Missing <uses-native-library>.
+ () -> System.loadLibrary("system_extpub_nouses.oem2"));
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
}
@Test
public void testLoadPrivateLibraries() {
TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
}
@Test
- public void testLoadPrivateLibrariesViaSystemSharedLib() {
- // TODO(b/237577392): Fix this use case.
- TestUtils.assertLinkerNamespaceError(() -> SystemSharedLib.loadLibrary("system_private2"));
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
+ }
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemSharedLib.loadLibrary("system_private2");
+ // SystemSharedLib.loadLibrary("systemext_private2");
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
}
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemExtSharedLib.loadLibrary("system_private3");
+ // SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> ProductSharedLib.loadLibrary("system_private4"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> ProductSharedLib.loadLibrary("systemext_private4"));
+ ProductSharedLib.loadLibrary("product_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> VendorSharedLib.loadLibrary("system_private5"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> VendorSharedLib.loadLibrary("systemext_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ VendorSharedLib.loadLibrary("vendor_private5");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_extpub3.oem1"));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system_ext", "systemext_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/product", "product_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/vendor", "vendor_private6")));
+ }
}
diff --git a/libnativeloader/test/src/android/test/app/ProductAppTest.java b/libnativeloader/test/src/android/test/app/ProductAppTest.java
index a9b8697..7ec817b 100644
--- a/libnativeloader/test/src/android/test/app/ProductAppTest.java
+++ b/libnativeloader/test/src/android/test/app/ProductAppTest.java
@@ -17,39 +17,95 @@
package android.test.app;
import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
import android.test.systemsharedlib.SystemSharedLib;
-import androidx.test.filters.SmallTest;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-@SmallTest
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class ProductAppTest {
@Test
public void testLoadExtendedPublicLibraries() {
- System.loadLibrary("foo.oem1");
- System.loadLibrary("bar.oem1");
- System.loadLibrary("foo.oem2");
- TestUtils.assertLinkerNamespaceError(
- () -> System.loadLibrary("bar.oem2")); // Missing <uses-native-library>.
- System.loadLibrary("foo.product1");
- System.loadLibrary("bar.product1");
+ System.loadLibrary("system_extpub.oem1");
+ System.loadLibrary("system_extpub.oem2");
+ System.loadLibrary("system_extpub1.oem1");
+ TestUtils.assertLinkerNamespaceError( // Missing <uses-native-library>.
+ () -> System.loadLibrary("system_extpub_nouses.oem2"));
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
}
@Test
public void testLoadPrivateLibraries() {
TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
System.loadLibrary("product_private1");
TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
}
@Test
- public void testLoadPrivateLibrariesViaSystemSharedLib() {
- // TODO(b/237577392): Fix this use case.
- TestUtils.assertLinkerNamespaceError(() -> SystemSharedLib.loadLibrary("system_private2"));
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
+ }
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemSharedLib.loadLibrary("system_private2");
+ // SystemSharedLib.loadLibrary("systemext_private2");
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
}
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemExtSharedLib.loadLibrary("system_private3");
+ // SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> ProductSharedLib.loadLibrary("system_private4"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> ProductSharedLib.loadLibrary("systemext_private4"));
+ ProductSharedLib.loadLibrary("product_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> VendorSharedLib.loadLibrary("system_private5"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> VendorSharedLib.loadLibrary("systemext_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ VendorSharedLib.loadLibrary("vendor_private5");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_extpub3.oem1"));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system_ext", "systemext_private6")));
+ System.load(TestUtils.libPath("/product", "product_private6"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/vendor", "vendor_private6")));
+ }
}
diff --git a/libnativeloader/test/src/android/test/app/SystemAppTest.java b/libnativeloader/test/src/android/test/app/SystemAppTest.java
index 6644478..cabdfb7 100644
--- a/libnativeloader/test/src/android/test/app/SystemAppTest.java
+++ b/libnativeloader/test/src/android/test/app/SystemAppTest.java
@@ -17,37 +17,90 @@
package android.test.app;
import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
import android.test.systemsharedlib.SystemSharedLib;
-import androidx.test.filters.SmallTest;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
// These tests are run from /system/app, /system/priv-app, and /system_ext/app.
-@SmallTest
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class SystemAppTest {
@Test
public void testLoadExtendedPublicLibraries() {
- System.loadLibrary("foo.oem1");
- System.loadLibrary("bar.oem1");
- System.loadLibrary("foo.oem2");
- System.loadLibrary("bar.oem2");
- System.loadLibrary("foo.product1");
- System.loadLibrary("bar.product1");
+ System.loadLibrary("system_extpub.oem1");
+ System.loadLibrary("system_extpub.oem2");
+ System.loadLibrary("system_extpub1.oem1");
+ // Missing <uses-native-library> not relevant for system apps, which have shared classloader
+ // namespaces.
+ System.loadLibrary("system_extpub_nouses.oem2");
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
}
@Test
public void testLoadPrivateLibraries() {
System.loadLibrary("system_private1");
+ System.loadLibrary("systemext_private1");
TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
}
@Test
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
+ }
+
+ @Test
public void testLoadPrivateLibrariesViaSystemSharedLib() {
SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
}
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ ProductSharedLib.loadLibrary("system_private4");
+ ProductSharedLib.loadLibrary("systemext_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("product_private4"));
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ VendorSharedLib.loadLibrary("system_private5");
+ VendorSharedLib.loadLibrary("systemext_private5");
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("vendor_private5"));
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_extpub3.oem1"));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_private6"));
+ System.load(TestUtils.libPath("/system_ext", "systemext_private6"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/product", "product_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/vendor", "vendor_private6")));
+ }
}
diff --git a/libnativeloader/test/src/android/test/app/VendorAppTest.java b/libnativeloader/test/src/android/test/app/VendorAppTest.java
index 5187ac8..10d0ea0 100644
--- a/libnativeloader/test/src/android/test/app/VendorAppTest.java
+++ b/libnativeloader/test/src/android/test/app/VendorAppTest.java
@@ -17,42 +17,95 @@
package android.test.app;
import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
import android.test.systemsharedlib.SystemSharedLib;
-import androidx.test.filters.SmallTest;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-@SmallTest
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class VendorAppTest {
@Test
public void testLoadExtendedPublicLibraries() {
- TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("foo.oem1"));
- TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("bar.oem1"));
- TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("foo.oem2"));
- TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("bar.oem2"));
- System.loadLibrary("foo.product1");
- System.loadLibrary("bar.product1");
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub.oem1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub.oem2"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub1.oem1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub_nouses.oem2"));
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
}
@Test
public void testLoadPrivateLibraries() {
TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
- // TODO(mast): The vendor app fails to load a private vendor library because it gets
- // classified as untrusted_app in SELinux, which doesn't have access to vendor_file. Even an
- // app in /vendor/priv-app, which gets classified as priv_app, still doesn't have access to
- // vendor_file. Check that the test setup is correct and if this is WAI.
- TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ System.loadLibrary("vendor_private1");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
}
@Test
public void testLoadPrivateLibrariesViaSystemSharedLib() {
- // TODO(b/237577392): Fix this use case.
- TestUtils.assertLinkerNamespaceError(() -> SystemSharedLib.loadLibrary("system_private2"));
-
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemSharedLib.loadLibrary("system_private2");
+ // SystemSharedLib.loadLibrary("systemext_private2");
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
}
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemExtSharedLib.loadLibrary("system_private3");
+ // SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> ProductSharedLib.loadLibrary("system_private4"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> ProductSharedLib.loadLibrary("systemext_private4"));
+ ProductSharedLib.loadLibrary("product_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> VendorSharedLib.loadLibrary("system_private5"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> VendorSharedLib.loadLibrary("systemext_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ VendorSharedLib.loadLibrary("vendor_private5");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_extpub3.oem1")));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system_ext", "systemext_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/product", "product_private6")));
+ System.load(TestUtils.libPath("/vendor", "vendor_private6"));
+ }
}
diff --git a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
index c908a49..55a6dd2 100644
--- a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
+++ b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
@@ -30,8 +30,14 @@
import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import com.android.tradefed.util.CommandResult;
+
import com.google.common.io.ByteStreams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -39,10 +45,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import org.junit.Test;
-import org.junit.runner.RunWith;
/**
* Test libnativeloader behavior for apps and libs in various partitions by overlaying them over
@@ -56,20 +61,24 @@
@BeforeClassWithInfo
public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
- DeviceContext ctx = new DeviceContext(
- testInfo.getContext(), testInfo.getDevice(), testInfo.getBuildInfo());
+ DeviceContext ctx = new DeviceContext(testInfo);
// A soft reboot is slow, so do setup for all tests and reboot once.
- ctx.mDevice.remountSystemWritable();
-
File libContainerApk = ctx.mBuildHelper.getTestFile("library_container_app.apk");
try (ZipFile libApk = new ZipFile(libContainerApk)) {
ctx.pushExtendedPublicSystemOemLibs(libApk);
ctx.pushExtendedPublicProductLibs(libApk);
ctx.pushPrivateLibs(libApk);
}
- ctx.pushSystemSharedLib();
+ ctx.pushSharedLib(
+ "/system", "android.test.systemsharedlib", "libnativeloader_system_shared_lib.jar");
+ ctx.pushSharedLib("/system_ext", "android.test.systemextsharedlib",
+ "libnativeloader_system_ext_shared_lib.jar");
+ ctx.pushSharedLib("/product", "android.test.productsharedlib",
+ "libnativeloader_product_shared_lib.jar");
+ ctx.pushSharedLib(
+ "/vendor", "android.test.vendorsharedlib", "libnativeloader_vendor_shared_lib.jar");
// "Install" apps in various partitions through plain adb push followed by a soft reboot. We
// need them in these locations to test library loading restrictions, so for all except
@@ -101,13 +110,13 @@
@AfterClassWithInfo
public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
- ITestDevice device = testInfo.getDevice();
+ DeviceContext ctx = new DeviceContext(testInfo);
// Uninstall loadlibrarytest_data_app.
- device.uninstallPackage("android.test.app.data");
+ ctx.mDevice.uninstallPackage("android.test.app.data");
String cleanupPathList = testInfo.properties().get(CLEANUP_PATHS_KEY);
- CleanupPaths cleanup = new CleanupPaths(device, cleanupPathList);
+ CleanupPaths cleanup = new CleanupPaths(ctx.mDevice, cleanupPathList);
cleanup.cleanup();
}
@@ -115,33 +124,41 @@
public void testSystemPrivApp() throws Exception {
// There's currently no difference in the tests between /system/priv-app and /system/app, so
// let's reuse the same one.
- runDeviceTests("android.test.app.system_priv", "android.test.app.SystemAppTest");
+ runTests("android.test.app.system_priv", "android.test.app.SystemAppTest");
}
@Test
public void testSystemApp() throws Exception {
- runDeviceTests("android.test.app.system", "android.test.app.SystemAppTest");
+ runTests("android.test.app.system", "android.test.app.SystemAppTest");
}
@Test
public void testSystemExtApp() throws Exception {
// /system_ext should behave the same as /system, so run the same test class there.
- runDeviceTests("android.test.app.system_ext", "android.test.app.SystemAppTest");
+ runTests("android.test.app.system_ext", "android.test.app.SystemAppTest");
}
@Test
public void testProductApp() throws Exception {
- runDeviceTests("android.test.app.product", "android.test.app.ProductAppTest");
+ runTests("android.test.app.product", "android.test.app.ProductAppTest");
}
@Test
public void testVendorApp() throws Exception {
- runDeviceTests("android.test.app.vendor", "android.test.app.VendorAppTest");
+ runTests("android.test.app.vendor", "android.test.app.VendorAppTest");
}
@Test
public void testDataApp() throws Exception {
- runDeviceTests("android.test.app.data", "android.test.app.DataAppTest");
+ runTests("android.test.app.data", "android.test.app.DataAppTest");
+ }
+
+ private void runTests(String pkgName, String testClassName) throws Exception {
+ DeviceContext ctx = new DeviceContext(getTestInformation());
+ var options = new DeviceTestRunOptions(pkgName)
+ .setTestClassName(testClassName)
+ .addInstrumentationArg("libDirName", ctx.libDirName());
+ runDeviceTests(options);
}
// Utility class that keeps track of a set of paths the need to be deleted after testing.
@@ -187,7 +204,7 @@
}
}
- // Class for code that needs an ITestDevice. It is instantiated both in tests and in
+ // Class for code that needs an ITestDevice. It may be instantiated both in tests and in
// (Before|After)ClassWithInfo.
private static class DeviceContext implements AutoCloseable {
IInvocationContext mContext;
@@ -196,55 +213,95 @@
CleanupPaths mCleanup;
private String mTestArch;
- DeviceContext(IInvocationContext context, ITestDevice device, IBuildInfo buildInfo) {
- mContext = context;
- mDevice = device;
- mBuildHelper = new CompatibilityBuildHelper(buildInfo);
+ DeviceContext(TestInformation testInfo) {
+ mContext = testInfo.getContext();
+ mDevice = testInfo.getDevice();
+ mBuildHelper = new CompatibilityBuildHelper(testInfo.getBuildInfo());
mCleanup = new CleanupPaths(mDevice);
}
public void close() throws DeviceNotAvailableException { mCleanup.cleanup(); }
- void pushExtendedPublicSystemOemLibs(ZipFile libApk) throws Exception {
- pushNativeTestLib(libApk, "/system/${LIB}/libfoo.oem1.so");
- pushNativeTestLib(libApk, "/system/${LIB}/libbar.oem1.so");
- pushString("libfoo.oem1.so\n"
- + "libbar.oem1.so\n",
- "/system/etc/public.libraries-oem1.txt");
+ // Helper class to both push a library to device and record it in a public.libraries-xxx.txt
+ // file.
+ class PublicLibs {
+ private ZipFile mLibApk;
+ private List<String> mPublicLibs = new ArrayList<String>();
- pushNativeTestLib(libApk, "/system/${LIB}/libfoo.oem2.so");
- pushNativeTestLib(libApk, "/system/${LIB}/libbar.oem2.so");
- pushString("libfoo.oem2.so\n"
- + "libbar.oem2.so\n",
- "/system/etc/public.libraries-oem2.txt");
+ PublicLibs(ZipFile libApk) {
+ mLibApk = libApk;
+ }
+
+ void addLib(String libName, String destDir, String destName) throws Exception {
+ pushNativeTestLib(mLibApk, libName, destDir + "/" + destName);
+ mPublicLibs.add(destName);
+ }
+
+ void pushPublicLibrariesFile(String path) throws DeviceNotAvailableException {
+ pushString(mPublicLibs.stream().collect(Collectors.joining("\n")) + "\n", path);
+ }
+ }
+
+ void pushExtendedPublicSystemOemLibs(ZipFile libApk) throws Exception {
+ var oem1Libs = new PublicLibs(libApk);
+ // Push libsystem_extpub<n>.oem1.so for each test. Since we cannot unload them, we need
+ // a fresh never-before-loaded library in each loadLibrary call.
+ for (int i = 1; i <= 3; ++i) {
+ oem1Libs.addLib("libsystem_testlib.so", "/system/${LIB}",
+ "libsystem_extpub" + i + ".oem1.so");
+ }
+ oem1Libs.addLib("libsystem_testlib.so", "/system/${LIB}", "libsystem_extpub.oem1.so");
+ oem1Libs.pushPublicLibrariesFile("/system/etc/public.libraries-oem1.txt");
+
+ var oem2Libs = new PublicLibs(libApk);
+ oem2Libs.addLib("libsystem_testlib.so", "/system/${LIB}", "libsystem_extpub.oem2.so");
+ // libextpub_nouses.oem2.so is a library that the test apps don't have
+ // <uses-native-library> dependencies for.
+ oem2Libs.addLib(
+ "libsystem_testlib.so", "/system/${LIB}", "libsystem_extpub_nouses.oem2.so");
+ oem2Libs.pushPublicLibrariesFile("/system/etc/public.libraries-oem2.txt");
}
void pushExtendedPublicProductLibs(ZipFile libApk) throws Exception {
- pushNativeTestLib(libApk, "/product/${LIB}/libfoo.product1.so");
- pushNativeTestLib(libApk, "/product/${LIB}/libbar.product1.so");
- pushString("libfoo.product1.so\n"
- + "libbar.product1.so\n",
- "/product/etc/public.libraries-product1.txt");
+ var product1Libs = new PublicLibs(libApk);
+ // Push libproduct_extpub<n>.product1.so for each test. Since we cannot unload them, we
+ // need a fresh never-before-loaded library in each loadLibrary call.
+ for (int i = 1; i <= 3; ++i) {
+ product1Libs.addLib("libproduct_testlib.so", "/product/${LIB}",
+ "libproduct_extpub" + i + ".product1.so");
+ }
+ product1Libs.addLib(
+ "libproduct_testlib.so", "/product/${LIB}", "libproduct_extpub.product1.so");
+ product1Libs.pushPublicLibrariesFile("/product/etc/public.libraries-product1.txt");
}
void pushPrivateLibs(ZipFile libApk) throws Exception {
// Push the libraries once for each test. Since we cannot unload them, we need a fresh
// never-before-loaded library in each loadLibrary call.
- for (int i = 1; i <= 2; ++i) {
- pushNativeTestLib(libApk, "/system/${LIB}/libsystem_private" + i + ".so");
- pushNativeTestLib(libApk, "/product/${LIB}/libproduct_private" + i + ".so");
- pushNativeTestLib(libApk, "/vendor/${LIB}/libvendor_private" + i + ".so");
+ for (int i = 1; i <= 6; ++i) {
+ pushNativeTestLib(libApk, "libsystem_testlib.so",
+ "/system/${LIB}/libsystem_private" + i + ".so");
+ pushNativeTestLib(libApk, "libsystem_testlib.so",
+ "/system_ext/${LIB}/libsystemext_private" + i + ".so");
+ pushNativeTestLib(libApk, "libproduct_testlib.so",
+ "/product/${LIB}/libproduct_private" + i + ".so");
+ pushNativeTestLib(libApk, "libvendor_testlib.so",
+ "/vendor/${LIB}/libvendor_private" + i + ".so");
}
}
- void pushSystemSharedLib() throws Exception {
- String packageName = "android.test.systemsharedlib";
- String path = "/system/framework/" + packageName + ".jar";
- pushFile("libnativeloader_system_shared_lib.jar", path);
+ void pushSharedLib(String partitionDir, String packageName, String buildJarName)
+ throws Exception {
+ String path = partitionDir + "/framework/" + packageName + ".jar";
+ pushFile(buildJarName, path);
+ // This permissions xml file is necessary to make it possible to depend on the shared
+ // library from the test app, even if it's in the same partition. It makes the library
+ // public to apps in other partitions as well, which is more than we need, but that
+ // being the case we test all shared libraries from all apps.
pushString("<permissions>\n"
+ "<library name=\"" + packageName + "\" file=\"" + path + "\" />\n"
+ "</permissions>\n",
- "system/etc/permissions/" + packageName + ".xml");
+ partitionDir + "/etc/permissions/" + packageName + ".xml");
}
void softReboot() throws DeviceNotAvailableException {
@@ -258,11 +315,15 @@
if (mTestArch == null) {
IAbi abi = mContext.getConfigurationDescriptor().getAbi();
mTestArch = abi != null ? abi.getName()
- : assertCommandSucceeds("getprop ro.bionic.arch");
+ : assertCommandSucceeds("getprop ro.product.cpu.abi");
}
return mTestArch;
}
+ String libDirName() throws DeviceNotAvailableException {
+ return getTestArch().contains("64") ? "lib64" : "lib";
+ }
+
// Pushes the given file contents to the device at the given destination path. destPath is
// assumed to have no risk of overlapping with existing files, and is deleted in tearDown(),
// along with any directory levels that had to be created.
@@ -284,8 +345,8 @@
// Like pushString, but extracts libnativeloader_testlib.so from the library_container_app
// APK and pushes it to destPath. "${LIB}" is replaced with "lib" or "lib64" as appropriate.
- void pushNativeTestLib(ZipFile libApk, String destPath) throws Exception {
- String libApkPath = "lib/" + getTestArch() + "/libnativeloader_testlib.so";
+ void pushNativeTestLib(ZipFile libApk, String libName, String destPath) throws Exception {
+ String libApkPath = "lib/" + getTestArch() + "/" + libName;
ZipEntry entry = libApk.getEntry(libApkPath);
assertWithMessage("Failed to find " + libApkPath + " in library_container_app.apk")
.that(entry)
@@ -293,11 +354,10 @@
File libraryTempFile;
try (InputStream inStream = libApk.getInputStream(entry)) {
- libraryTempFile = writeStreamToTempFile("libnativeloader_testlib.so", inStream);
+ libraryTempFile = writeStreamToTempFile(libName, inStream);
}
- String libDir = getTestArch().contains("64") ? "lib64" : "lib";
- destPath = destPath.replace("${LIB}", libDir);
+ destPath = destPath.replace("${LIB}", libDirName());
mCleanup.addPath(destPath);
assertThat(mDevice.pushFile(libraryTempFile, destPath)).isTrue();
diff --git a/libnativeloader/test/src/android/test/lib/TestUtils.java b/libnativeloader/test/src/android/test/lib/TestUtils.java
index eda9993..1dd917f 100644
--- a/libnativeloader/test/src/android/test/lib/TestUtils.java
+++ b/libnativeloader/test/src/android/test/lib/TestUtils.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.function.ThrowingRunnable;
public final class TestUtils {
@@ -32,4 +33,9 @@
assertThat(t.getMessage())
.containsMatch("dlopen failed: .* is not accessible for the namespace");
}
+
+ public static String libPath(String dir, String libName) {
+ String libDirName = InstrumentationRegistry.getArguments().getString("libDirName");
+ return dir + "/" + libDirName + "/lib" + libName + ".so";
+ }
}
diff --git a/libnativeloader/test/src/android/test/productsharedlib/ProductSharedLib.java b/libnativeloader/test/src/android/test/productsharedlib/ProductSharedLib.java
new file mode 100644
index 0000000..a500d2a
--- /dev/null
+++ b/libnativeloader/test/src/android/test/productsharedlib/ProductSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 android.test.productsharedlib;
+
+public final class ProductSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
new file mode 100644
index 0000000..1240e12
--- /dev/null
+++ b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 android.test.systemextsharedlib;
+
+public final class SystemExtSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libnativeloader/test/src/android/test/vendorsharedlib/VendorSharedLib.java b/libnativeloader/test/src/android/test/vendorsharedlib/VendorSharedLib.java
new file mode 100644
index 0000000..8859b63
--- /dev/null
+++ b/libnativeloader/test/src/android/test/vendorsharedlib/VendorSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 android.test.vendorsharedlib;
+
+public final class VendorSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libprofile/Android.bp b/libprofile/Android.bp
index ecbcd0b..55bdcd1 100644
--- a/libprofile/Android.bp
+++ b/libprofile/Android.bp
@@ -106,11 +106,6 @@
"libprofile_defaults",
"libart_nativeunwind_defaults",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- export_shared_lib_headers: ["libbase"],
target: {
android: {
shared_libs: [
@@ -147,10 +142,6 @@
"art_debug_defaults",
"libprofile_defaults",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
target: {
android: {
shared_libs: [
@@ -171,7 +162,6 @@
],
},
},
- export_shared_lib_headers: ["libbase"],
apex_available: [
"com.android.art.debug",
// TODO(b/183882457): This lib doesn't go into com.android.art, but
@@ -195,9 +185,12 @@
"profile/profile_boot_info_test.cc",
"profile/profile_compilation_info_test.cc",
],
- shared_libs: [
+ static_libs: [
"libziparchive",
],
+ shared_libs: [
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
+ ],
}
// Version of ART gtest `art_libprofile_tests` bundled with the ART APEX on target.
diff --git a/libprofile/art_standalone_libprofile_tests.xml b/libprofile/art_standalone_libprofile_tests.xml
index 9ecd9a5..65264f4 100644
--- a/libprofile/art_standalone_libprofile_tests.xml
+++ b/libprofile/art_standalone_libprofile_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_libprofile_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_libprofile_tests->/data/local/tmp/art_standalone_libprofile_tests/art_standalone_libprofile_tests" />
diff --git a/libprofile/profile/profile_boot_info_test.cc b/libprofile/profile/profile_boot_info_test.cc
index 9939f0a..3c5829c 100644
--- a/libprofile/profile/profile_boot_info_test.cc
+++ b/libprofile/profile/profile_boot_info_test.cc
@@ -66,6 +66,7 @@
ScratchFile profile;
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
std::vector<const DexFile*> dex_files2;
+ dex_files2.reserve(dex_files.size());
for (const std::unique_ptr<const DexFile>& file : dex_files) {
dex_files2.push_back(file.get());
}
@@ -104,6 +105,7 @@
ProfileBootInfo loaded_info;
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
std::vector<const DexFile*> dex_files2;
+ dex_files2.reserve(dex_files.size());
for (const std::unique_ptr<const DexFile>& file : dex_files) {
dex_files2.push_back(file.get());
}
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index f135805..83da564 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -1710,14 +1710,14 @@
if (memcmp(header.GetVersion(), version_, kProfileVersionSize) != 0) {
*error = IsForBootImage() ? "Expected boot profile, got app profile."
: "Expected app profile, got boot profile.";
- return ProfileLoadStatus::kMergeError;
+ return ProfileLoadStatus::kVersionMismatch;
}
// Check if there are too many section infos.
uint32_t section_count = header.GetFileSectionCount();
uint32_t uncompressed_data_size = sizeof(FileHeader) + section_count * sizeof(FileSectionInfo);
if (uncompressed_data_size > GetSizeErrorThresholdBytes()) {
- LOG(ERROR) << "Profile data size exceeds " << GetSizeErrorThresholdBytes()
+ LOG(WARNING) << "Profile data size exceeds " << GetSizeErrorThresholdBytes()
<< " bytes. It has " << uncompressed_data_size << " bytes.";
return ProfileLoadStatus::kBadData;
}
@@ -1743,7 +1743,7 @@
// Allow large profiles for non target builds for the case where we are merging many profiles
// to generate a boot image profile.
if (uncompressed_data_size > GetSizeErrorThresholdBytes()) {
- LOG(ERROR) << "Profile data size exceeds "
+ LOG(WARNING) << "Profile data size exceeds "
<< GetSizeErrorThresholdBytes()
<< " bytes. It has " << uncompressed_data_size << " bytes.";
return ProfileLoadStatus::kBadData;
@@ -2209,7 +2209,8 @@
return vec;
};
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- const std::string& profile_key = dex_file->GetLocation();
+ const std::string& dex_location = dex_file->GetLocation();
+ std::string profile_key = info.GetProfileDexFileBaseKey(dex_location);
uint32_t checksum = dex_file->GetLocationChecksum();
uint32_t number_of_classes = dex_file->NumClassDefs();
@@ -2387,7 +2388,8 @@
}
bool ProfileCompilationInfo::UpdateProfileKeys(
- const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+ const std::vector<std::unique_ptr<const DexFile>>& dex_files, /*out*/ bool* matched) {
+ *matched = false;
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
if (dex_data->checksum == dex_file->GetLocationChecksum() &&
@@ -2408,6 +2410,7 @@
dex_data->profile_key = MigrateAnnotationInfo(new_profile_key, dex_data->profile_key);
profile_key_map_.Put(dex_data->profile_key, dex_data->profile_index);
}
+ *matched = true;
}
}
}
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 4366078..27902ad 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -645,7 +645,10 @@
//
// If the new profile key would collide with an existing key (for a different dex)
// the method returns false. Otherwise it returns true.
- bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+ //
+ // `matched` is set to true if any profile has matched any input dex file.
+ bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ /*out*/ bool* matched);
// Checks if the profile is empty.
bool IsEmpty() const;
diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc
index 8c9d0df..6d10467 100644
--- a/libprofile/profile/profile_compilation_info_test.cc
+++ b/libprofile/profile/profile_compilation_info_test.cc
@@ -40,17 +40,23 @@
CommonArtTest::SetUp();
allocator_.reset(new ArenaAllocator(&pool_));
- dex1 = BuildDex("location1", /*checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 101);
- dex2 = BuildDex("location2", /*checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 102);
- dex3 = BuildDex("location3", /*checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 103);
- dex4 = BuildDex("location4", /*checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 104);
+ dex1 = BuildDex("location1", /*location_checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 101);
+ dex2 = BuildDex("location2", /*location_checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 102);
+ dex3 = BuildDex("location3", /*location_checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 103);
+ dex4 = BuildDex("location4", /*location_checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 104);
- dex1_checksum_missmatch =
- BuildDex("location1", /*checksum=*/ 12, "LUnique1;", /*num_method_ids=*/ 101);
- dex1_renamed =
- BuildDex("location1-renamed", /*checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 101);
- dex2_renamed =
- BuildDex("location2-renamed", /*checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 102);
+ dex1_checksum_missmatch = BuildDex("location1",
+ /*location_checksum=*/ 12,
+ "LUnique1;",
+ /*num_method_ids=*/ 101);
+ dex1_renamed = BuildDex("location1-renamed",
+ /*location_checksum=*/ 1,
+ "LUnique1;",
+ /*num_method_ids=*/ 101);
+ dex2_renamed = BuildDex("location2-renamed",
+ /*location_checksum=*/ 2,
+ "LUnique2;",
+ /*num_method_ids=*/ 102);
}
protected:
@@ -350,10 +356,16 @@
TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
ScratchFile profile;
- const DexFile* dex_max1 = BuildDex(
- "location-max1", /*checksum=*/ 5, "LUniqueMax1;", kMaxMethodIds, kMaxClassIds);
- const DexFile* dex_max2 = BuildDex(
- "location-max2", /*checksum=*/ 6, "LUniqueMax2;", kMaxMethodIds, kMaxClassIds);
+ const DexFile* dex_max1 = BuildDex("location-max1",
+ /*location_checksum=*/ 5,
+ "LUniqueMax1;",
+ kMaxMethodIds,
+ kMaxClassIds);
+ const DexFile* dex_max2 = BuildDex("location-max2",
+ /*location_checksum=*/ 6,
+ "LUniqueMax2;",
+ kMaxMethodIds,
+ kMaxClassIds);
ProfileCompilationInfo saved_info;
@@ -733,11 +745,11 @@
// Save a few methods.
for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexType>::max(); i++) {
std::string location = std::to_string(i);
- const DexFile* dex = BuildDex(location, /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex(location, /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_TRUE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
// Add an extra dex file.
- const DexFile* dex = BuildDex("-1", /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex("-1", /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_FALSE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
@@ -746,11 +758,11 @@
// Save a few methods.
for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexType>::max(); i++) {
std::string location = std::to_string(i);
- const DexFile* dex = BuildDex(location, /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex(location, /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_TRUE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
// Add an extra dex file.
- const DexFile* dex = BuildDex("-1", /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex("-1", /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_FALSE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
@@ -944,7 +956,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0);
// Update the profile keys based on the original dex files
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool matched = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
+ ASSERT_TRUE(matched);
// Verify that we find the methods when searched with the original dex files.
for (const std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -970,7 +984,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0, Hotness::kFlagHot, annotation);
// Update the profile keys based on the original dex files
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool matched = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
+ ASSERT_TRUE(matched);
// Verify that we find the methods when searched with the original dex files.
for (const std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -985,7 +1001,33 @@
}
}
-TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoUpdate) {
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkMatchedButNoUpdate) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files.push_back(std::unique_ptr<const DexFile>(dex1));
+
+ // Both the checksum and the location match the original dex file.
+ ProfileCompilationInfo info;
+ AddMethod(&info, dex1, /*method_idx=*/0);
+
+ // No update should happen, but this should be considered as a happy case.
+ bool matched = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
+ ASSERT_TRUE(matched);
+
+ // Verify that we find the methods when searched with the original dex files.
+ for (const std::unique_ptr<const DexFile>& dex : dex_files) {
+ ProfileCompilationInfo::MethodHotness loaded_hotness =
+ GetMethod(info, dex.get(), /*method_idx=*/ 0);
+ ASSERT_TRUE(loaded_hotness.IsHot());
+ }
+
+ // Release the ownership as this is held by the test class;
+ for (std::unique_ptr<const DexFile>& dex : dex_files) {
+ UNUSED(dex.release());
+ }
+}
+
+TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoMatch) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files.push_back(std::unique_ptr<const DexFile>(dex1));
@@ -993,7 +1035,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0);
// Update the profile keys based on the original dex files.
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool matched = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
+ ASSERT_FALSE(matched);
// Verify that we did not perform any update and that we cannot find anything with the new
// location.
@@ -1025,7 +1069,9 @@
// This will cause the rename to fail because an existing entry would already have that name.
AddMethod(&info, dex1_renamed, /*method_idx=*/ 0);
- ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
+ bool matched = false;
+ ASSERT_FALSE(info.UpdateProfileKeys(dex_files, &matched));
+ ASSERT_FALSE(matched);
// Release the ownership as this is held by the test class;
for (std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -1167,12 +1213,12 @@
ScratchFile profile;
const DexFile* dex1_1000 = BuildDex("location1_1000",
- /*checksum=*/ 7,
+ /*location_checksum=*/ 7,
"LC1_1000;",
/*num_method_ids=*/ 1u,
/*num_class_ids=*/ 1000u);
const DexFile* dex2_1000 = BuildDex("location2_1000",
- /*checksum=*/ 8,
+ /*location_checksum=*/ 8,
"LC2_1000;",
/*num_method_ids=*/ 1u,
/*num_class_ids=*/ 1000u);
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 5724280..ba7ceb5 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -70,6 +70,9 @@
"libdexfile",
"libprofile",
],
+ static_libs: [
+ "libelffile",
+ ],
},
host: {
// Make the host binary static, except for system libraries.
@@ -124,6 +127,9 @@
"libdexfiled",
"libprofiled",
],
+ static_libs: [
+ "libelffiled",
+ ],
},
host: {
// Make the host binary static, except for system libraries.
diff --git a/oatdump/art_standalone_oatdump_tests.xml b/oatdump/art_standalone_oatdump_tests.xml
index bcd94ed..ab11b11 100644
--- a/oatdump/art_standalone_oatdump_tests.xml
+++ b/oatdump/art_standalone_oatdump_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_oatdump_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_oatdump_tests->/data/local/tmp/art_standalone_oatdump_tests/art_standalone_oatdump_tests" />
@@ -54,7 +56,8 @@
<!-- ART Mainline Module (external (AOSP) version). -->
<option name="mainline-module-package-name" value="com.android.art" />
</object>
-
+ <!-- Skip on HWASan. TODO(b/230394041): Re-enable -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" />
<!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
</configuration>
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index e69b32a..0d163bd 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -45,7 +45,6 @@
#include "class_linker-inl.h"
#include "class_linker.h"
#include "class_root-inl.h"
-#include "compiled_method.h"
#include "debug/debug_info.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
@@ -183,17 +182,17 @@
builder_->WriteDynamicSection();
const OatHeader& oat_header = oat_file_->GetOatHeader();
- #define DO_TRAMPOLINE(fn_name) \
- if (oat_header.Get ## fn_name ## Offset() != 0) { \
- debug::MethodDebugInfo info = {}; \
- info.custom_name = #fn_name; \
- info.isa = oat_header.GetInstructionSet(); \
- info.is_code_address_text_relative = true; \
- size_t code_offset = oat_header.Get ## fn_name ## Offset(); \
- code_offset -= CompiledCode::CodeDelta(oat_header.GetInstructionSet()); \
- info.code_address = code_offset - oat_header.GetExecutableOffset(); \
- info.code_size = 0; /* The symbol lasts until the next symbol. */ \
- method_debug_infos_.push_back(std::move(info)); \
+ #define DO_TRAMPOLINE(fn_name) \
+ if (oat_header.Get ## fn_name ## Offset() != 0) { \
+ debug::MethodDebugInfo info = {}; \
+ info.custom_name = #fn_name; \
+ info.isa = oat_header.GetInstructionSet(); \
+ info.is_code_address_text_relative = true; \
+ size_t code_offset = oat_header.Get ## fn_name ## Offset(); \
+ code_offset -= GetInstructionSetEntryPointAdjustment(oat_header.GetInstructionSet()); \
+ info.code_address = code_offset - oat_header.GetExecutableOffset(); \
+ info.code_size = 0; /* The symbol lasts until the next symbol. */ \
+ method_debug_infos_.push_back(std::move(info)); \
}
DO_TRAMPOLINE(JniDlsymLookupTrampoline);
DO_TRAMPOLINE(JniDlsymLookupCriticalTrampoline);
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 3ec5b94..708befe 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -328,7 +328,9 @@
auto post_fork_fn = []() {
setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager.
- return true; // Ignore setpgid failures.
+ // Ignore setpgid failures.
+ return setenv("ANDROID_LOG_TAGS", "*:e", 1) == 0; // We're only interested in errors and
+ // fatal logs.
};
ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, line_buf_fn);
diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp
index 9958e3a..9b08940 100644
--- a/odrefresh/Android.bp
+++ b/odrefresh/Android.bp
@@ -47,6 +47,7 @@
],
static_libs: [
"libc++fs",
+ "libmodules-utils-build",
],
tidy: true,
tidy_disabled_srcs: [":art-apex-cache-info"],
@@ -122,6 +123,7 @@
local_include_dirs: ["include"],
header_libs: ["libbase_headers"],
srcs: ["odrefresh_broken.cc"],
+ installable: false,
apex_available: ["test_jitzygote_com.android.art"],
}
diff --git a/odrefresh/odr_compilation_log.cc b/odrefresh/odr_compilation_log.cc
index 9c50817..0c8dda8 100644
--- a/odrefresh/odr_compilation_log.cc
+++ b/odrefresh/odr_compilation_log.cc
@@ -185,6 +185,12 @@
return true;
}
+ // The backoff time is for avoiding too many failed attempts. It should not be applied if the last
+ // compilation was successful.
+ if (entries_.back().exit_code == ExitCode::kCompilationSuccess) {
+ return true;
+ }
+
if (trigger == OdrMetrics::Trigger::kApexVersionMismatch ||
trigger == OdrMetrics::Trigger::kDexFilesChanged) {
// Things have changed since the last run.
diff --git a/odrefresh/odr_compilation_log_test.cc b/odrefresh/odr_compilation_log_test.cc
index 46cea79..f28d849 100644
--- a/odrefresh/odr_compilation_log_test.cc
+++ b/odrefresh/odr_compilation_log_test.cc
@@ -109,7 +109,7 @@
/*apex_version=*/1,
/*last_update_millis=*/762,
OdrMetrics::Trigger::kApexVersionMismatch,
- ExitCode::kCompilationSuccess);
+ ExitCode::kCompilationFailed);
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kApexVersionMismatch));
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kDexFilesChanged));
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown));
@@ -180,8 +180,8 @@
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationSuccess);
- ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
- ASSERT_FALSE(
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
+ ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 4));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2));
@@ -382,7 +382,7 @@
kLastUpdateMillis,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
- ExitCode::kCompilationSuccess);
+ ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
}
}
diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h
index 5c3f4ee..76375ac 100644
--- a/odrefresh/odr_config.h
+++ b/odrefresh/odr_config.h
@@ -41,6 +41,11 @@
// everything if any property matching a prefix changes.
constexpr const char* kCheckedSystemPropertyPrefixes[]{"dalvik.vm.", "ro.dalvik.vm."};
+// System property for the phenotype flag to override the device or default-configured
+// system server compiler filter setting.
+static constexpr char kSystemPropertySystemServerCompilerFilterOverride[] =
+ "persist.device_config.runtime_native_boot.systemservercompilerfilter_override";
+
// The list of system properties that odrefresh ignores. They don't affect compilation results.
const std::unordered_set<std::string> kIgnoredSystemProperties{
"dalvik.vm.dex2oat-cpu-set",
@@ -68,7 +73,9 @@
const android::base::NoDestructor<std::vector<SystemPropertyConfig>> kSystemProperties{
{SystemPropertyConfig{.name = "persist.device_config.runtime_native_boot.enable_uffd_gc",
.default_value = "false"},
- SystemPropertyConfig{.name = kPhDisableCompactDex, .default_value = "false"}}};
+ SystemPropertyConfig{.name = kPhDisableCompactDex, .default_value = "false"},
+ SystemPropertyConfig{.name = kSystemPropertySystemServerCompilerFilterOverride,
+ .default_value = ""}}};
// An enumeration of the possible zygote configurations on Android.
enum class ZygoteKind : uint8_t {
@@ -96,6 +103,7 @@
InstructionSet isa_;
std::string program_name_;
std::string system_server_classpath_;
+ std::string boot_image_compiler_filter_;
std::string system_server_compiler_filter_;
ZygoteKind zygote_kind_;
std::string boot_classpath_;
@@ -181,6 +189,9 @@
const std::string& GetSystemServerClasspath() const {
return system_server_classpath_;
}
+ const std::string& GetBootImageCompilerFilter() const {
+ return boot_image_compiler_filter_;
+ }
const std::string& GetSystemServerCompilerFilter() const {
return system_server_compiler_filter_;
}
@@ -217,6 +228,9 @@
system_server_classpath_ = classpath;
}
+ void SetBootImageCompilerFilter(const std::string& filter) {
+ boot_image_compiler_filter_ = filter;
+ }
void SetSystemServerCompilerFilter(const std::string& filter) {
system_server_compiler_filter_ = filter;
}
@@ -261,6 +275,7 @@
case art::InstructionSet::kX86:
case art::InstructionSet::kX86_64:
return std::make_pair(art::InstructionSet::kX86, art::InstructionSet::kX86_64);
+ case art::InstructionSet::kRiscv64:
case art::InstructionSet::kThumb2:
case art::InstructionSet::kNone:
LOG(FATAL) << "Invalid instruction set " << isa_;
diff --git a/odrefresh/odr_fs_utils.cc b/odrefresh/odr_fs_utils.cc
index 22cc1b6..3ed8021 100644
--- a/odrefresh/odr_fs_utils.cc
+++ b/odrefresh/odr_fs_utils.cc
@@ -68,9 +68,12 @@
}
if (rmdir(dir_path.c_str()) != 0) {
- LOG(ERROR) << "Failed to delete '" << dir_path << "'";
- return false;
+ // It's possible that we are not able to remove the directory itself. For example, when
+ // odrefresh is running in CompOS, the staging dir is prepared beforehand passed to the VM as an
+ // FD. In this case, just log and ignore the error. It's okay to keep the directory.
+ LOG(WARNING) << "Failed to delete '" << dir_path << "'";
}
+
return true;
}
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index 4c8b876..c65c986 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -56,12 +56,14 @@
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
+#include "android-base/parsebool.h"
#include "android-base/parseint.h"
#include "android-base/properties.h"
#include "android-base/result.h"
#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "android-modules-utils/sdk_level.h"
#include "android/log.h"
#include "arch/instruction_set.h"
#include "base/file_utils.h"
@@ -76,6 +78,7 @@
#include "dex/art_dex_file_loader.h"
#include "dexoptanalyzer.h"
#include "exec_utils.h"
+#include "fmt/format.h"
#include "log/log.h"
#include "odr_artifacts.h"
#include "odr_common.h"
@@ -86,25 +89,31 @@
#include "odrefresh/odrefresh.h"
#include "palette/palette.h"
#include "palette/palette_types.h"
+#include "read_barrier_config.h"
namespace art {
namespace odrefresh {
+namespace {
+
namespace apex = com::android::apex;
namespace art_apex = com::android::art;
-using android::base::Result;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::Result;
+using ::android::modules::sdklevel::IsAtLeastU;
-namespace {
+using ::fmt::literals::operator""_format; // NOLINT
// Name of cache info file in the ART Apex artifact cache.
constexpr const char* kCacheInfoFile = "cache-info.xml";
// Maximum execution time for odrefresh from start to end.
-constexpr time_t kMaximumExecutionSeconds = 300;
+constexpr time_t kMaximumExecutionSeconds = 480;
// Maximum execution time for any child process spawned.
-constexpr time_t kMaxChildProcessSeconds = 90;
+constexpr time_t kMaxChildProcessSeconds = 120;
constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
@@ -204,10 +213,12 @@
return module_info_list;
}
-// Returns a rewritten path based on ANDROID_ROOT if the path starts with "/system/".
-std::string AndroidRootRewrite(const std::string& path) {
+// Returns a rewritten path based on environment variables for interesting paths.
+std::string RewriteParentDirectoryIfNeeded(const std::string& path) {
if (StartsWith(path, "/system/")) {
return Concatenate({GetAndroidRoot(), path.substr(7)});
+ } else if (StartsWith(path, "/system_ext/")) {
+ return Concatenate({GetSystemExtRoot(), path.substr(11)});
} else {
return path;
}
@@ -281,7 +292,7 @@
ArtDexFileLoader loader;
for (const std::string& path : jars) {
- std::string actual_path = AndroidRootRewrite(path);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(path);
struct stat sb;
if (stat(actual_path.c_str(), &sb) == -1) {
PLOG(ERROR) << "Failed to stat component: " << QuotePath(actual_path);
@@ -407,7 +418,8 @@
args.emplace_back(Concatenate({"--instruction-set=", isa_str}));
}
-void AddDex2OatProfileAndCompilerFilter(
+// Returns true if any profile has been added.
+bool AddDex2OatProfile(
/*inout*/ std::vector<std::string>& args,
/*inout*/ std::vector<std::unique_ptr<File>>& output_files,
const std::vector<std::string>& profile_paths) {
@@ -420,12 +432,7 @@
has_any_profile = true;
}
}
-
- if (has_any_profile) {
- args.emplace_back("--compiler-filter=speed-profile");
- } else {
- args.emplace_back("--compiler-filter=speed");
- }
+ return has_any_profile;
}
bool AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
@@ -439,7 +446,7 @@
if (StartsWith(jar, "/apex/")) {
bcp_fds.emplace_back("-1");
} else {
- std::string actual_path = AndroidRootRewrite(jar);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(jar);
std::unique_ptr<File> jar_file(OS::OpenFileForReading(actual_path.c_str()));
if (!jar_file || !jar_file->IsValid()) {
LOG(ERROR) << "Failed to open a BCP jar " << actual_path;
@@ -555,6 +562,13 @@
std::string GetSystemBootImageDir() { return GetAndroidRoot() + "/framework"; }
+bool HasVettedDeviceSystemServerProfiles() {
+ // While system_server profiles were bundled on the device prior to U+, they were not used by
+ // default or rigorously tested, so we cannot vouch for their efficacy.
+ static const bool kDeviceIsAtLeastU = IsAtLeastU();
+ return kDeviceIsAtLeastU;
+}
+
} // namespace
OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config)
@@ -781,7 +795,7 @@
const std::string image_name = ReplaceFileExtension(jar_name, "art");
const char* isa_str = GetInstructionSetString(config_.GetSystemServerIsa());
// Typically "/system/framework/oat/<isa>/services.art".
- return Concatenate({GetAndroidRoot(), "/framework/oat/", isa_str, "/", image_name});
+ return Concatenate({android::base::Dirname(jar_path), "/oat/", isa_str, "/", image_name});
} else {
// Typically
// "/data/misc/apexdata/.../dalvik-cache/<isa>/system@framework@services.jar@classes.art".
@@ -907,17 +921,35 @@
return true;
}
+WARN_UNUSED bool OnDeviceRefresh::CheckBuildUserfaultFdGc() const {
+ auto it = config_.GetSystemProperties().find("ro.dalvik.vm.enable_uffd_gc");
+ bool build_enable_uffd_gc = it != config_.GetSystemProperties().end() ?
+ ParseBool(it->second) == ParseBoolResult::kTrue :
+ false;
+ if (build_enable_uffd_gc != gUseUserfaultfd) {
+ // Normally, this should not happen. If this happens, the system image was probably built with a
+ // wrong PRODUCT_ENABLE_UFFD_GC flag.
+ LOG(WARNING) << "Userfaultfd GC check failed (build-time: {}, runtime: {})."_format(
+ build_enable_uffd_gc, gUseUserfaultfd);
+ return false;
+ }
+ return true;
+}
+
WARN_UNUSED bool OnDeviceRefresh::BootClasspathArtifactsOnSystemUsable(
const apex::ApexInfo& art_apex_info) const {
if (!art_apex_info.getIsFactory()) {
+ LOG(INFO) << "Updated ART APEX mounted";
return false;
}
- LOG(INFO) << "Factory ART APEX mounted.";
if (!CheckSystemPropertiesAreDefault()) {
return false;
}
- LOG(INFO) << "System properties are set to default values.";
+
+ if (!CheckBuildUserfaultFdGc()) {
+ return false;
+ }
return true;
}
@@ -927,14 +959,17 @@
if (std::any_of(apex_info_list.begin(),
apex_info_list.end(),
[](const apex::ApexInfo& apex_info) { return !apex_info.getIsFactory(); })) {
+ LOG(INFO) << "Updated APEXes mounted";
return false;
}
- LOG(INFO) << "Factory APEXes mounted.";
if (!CheckSystemPropertiesAreDefault()) {
return false;
}
- LOG(INFO) << "System properties are set to default values.";
+
+ if (!CheckBuildUserfaultFdGc()) {
+ return false;
+ }
return true;
}
@@ -1452,8 +1487,18 @@
std::vector<std::unique_ptr<File>> readonly_files_raii;
const std::string art_boot_profile_file = GetArtRoot() + "/etc/boot-image.prof";
const std::string framework_boot_profile_file = GetAndroidRoot() + "/etc/boot-image.prof";
- AddDex2OatProfileAndCompilerFilter(args, readonly_files_raii,
- {art_boot_profile_file, framework_boot_profile_file});
+ bool has_any_profile = AddDex2OatProfile(
+ args, readonly_files_raii, {art_boot_profile_file, framework_boot_profile_file});
+ if (!has_any_profile) {
+ *error_msg = "Missing boot image profile";
+ return false;
+ }
+ const std::string& compiler_filter = config_.GetBootImageCompilerFilter();
+ if (!compiler_filter.empty()) {
+ args.emplace_back("--compiler-filter=" + compiler_filter);
+ } else {
+ args.emplace_back("--compiler-filter=speed-profile");
+ }
// Compile as a single image for fewer files and slightly less memory overhead.
args.emplace_back("--single-image");
@@ -1489,7 +1534,7 @@
}
for (const std::string& component : jars_to_compile) {
- std::string actual_path = AndroidRootRewrite(component);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(component);
args.emplace_back("--dex-file=" + component);
std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
args.emplace_back(android::base::StringPrintf("--dex-fd=%d", file->Fd()));
@@ -1597,7 +1642,7 @@
args.emplace_back(dex2oat);
args.emplace_back("--dex-file=" + jar);
- std::string actual_jar_path = AndroidRootRewrite(jar);
+ std::string actual_jar_path = RewriteParentDirectoryIfNeeded(jar);
std::unique_ptr<File> dex_file(OS::OpenFileForReading(actual_jar_path.c_str()));
args.emplace_back(android::base::StringPrintf("--dex-fd=%d", dex_file->Fd()));
readonly_files_raii.push_back(std::move(dex_file));
@@ -1611,11 +1656,17 @@
const std::string jar_name(android::base::Basename(jar));
const std::string profile = Concatenate({GetAndroidRoot(), "/framework/", jar_name, ".prof"});
- std::string compiler_filter = config_.GetSystemServerCompilerFilter();
- if (compiler_filter == "speed-profile") {
- AddDex2OatProfileAndCompilerFilter(args, readonly_files_raii, {profile});
- } else {
+ const std::string& compiler_filter = config_.GetSystemServerCompilerFilter();
+ const bool maybe_add_profile =
+ !compiler_filter.empty() || HasVettedDeviceSystemServerProfiles();
+ const bool has_added_profile =
+ maybe_add_profile && AddDex2OatProfile(args, readonly_files_raii, {profile});
+ if (!compiler_filter.empty()) {
args.emplace_back("--compiler-filter=" + compiler_filter);
+ } else if (has_added_profile) {
+ args.emplace_back("--compiler-filter=speed-profile");
+ } else {
+ args.emplace_back("--compiler-filter=speed");
}
const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar);
@@ -1683,7 +1734,7 @@
if (!classloader_context.empty()) {
std::vector<int> fds;
for (const std::string& path : classloader_context) {
- std::string actual_path = AndroidRootRewrite(path);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(path);
std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
if (!file->IsValid()) {
PLOG(ERROR) << "Failed to open classloader context " << actual_path;
diff --git a/odrefresh/odrefresh.h b/odrefresh/odrefresh.h
index b14aa41..ff3d660 100644
--- a/odrefresh/odrefresh.h
+++ b/odrefresh/odrefresh.h
@@ -150,6 +150,9 @@
WARN_UNUSED bool CheckSystemPropertiesHaveNotChanged(
const com::android::art::CacheInfo& cache_info) const;
+ // Returns true if the system image is built with the right userfaultfd GC flag.
+ WARN_UNUSED bool CheckBuildUserfaultFdGc() const;
+
// Returns true if boot classpath artifacts on /system are usable if they exist. Note that this
// function does not check file existence.
WARN_UNUSED bool BootClasspathArtifactsOnSystemUsable(
diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc
index a3761ef..98a75f3 100644
--- a/odrefresh/odrefresh_main.cc
+++ b/odrefresh/odrefresh_main.cc
@@ -43,6 +43,7 @@
using ::art::odrefresh::kCheckedSystemPropertyPrefixes;
using ::art::odrefresh::kIgnoredSystemProperties;
using ::art::odrefresh::kSystemProperties;
+using ::art::odrefresh::kSystemPropertySystemServerCompilerFilterOverride;
using ::art::odrefresh::OdrCompilationLog;
using ::art::odrefresh::OdrConfig;
using ::art::odrefresh::OdrMetrics;
@@ -146,6 +147,8 @@
config->SetArtifactDirectory(GetApexDataDalvikCacheDirectory(art::InstructionSet::kNone));
} else if (ArgumentMatches(arg, "--zygote-arch=", &value)) {
zygote = value;
+ } else if (ArgumentMatches(arg, "--boot-image-compiler-filter=", &value)) {
+ config->SetBootImageCompilerFilter(value);
} else if (ArgumentMatches(arg, "--system-server-compiler-filter=", &value)) {
config->SetSystemServerCompilerFilter(value);
} else if (ArgumentMatches(arg, "--staging-dir=", &value)) {
@@ -174,7 +177,8 @@
config->SetZygoteKind(zygote_kind);
if (config->GetSystemServerCompilerFilter().empty()) {
- std::string filter = GetProperty("dalvik.vm.systemservercompilerfilter", "speed");
+ std::string filter = GetProperty("dalvik.vm.systemservercompilerfilter", "");
+ filter = GetProperty(kSystemPropertySystemServerCompilerFilterOverride, filter);
config->SetSystemServerCompilerFilter(filter);
}
@@ -234,6 +238,9 @@
UsageMsg("--staging-dir=<DIR> Write temporary artifacts to <DIR> rather than");
UsageMsg(" .../staging");
UsageMsg("--zygote-arch=<STRING> Zygote kind that overrides ro.zygote");
+ UsageMsg("--boot-image-compiler-filter=<STRING>");
+ UsageMsg(" Compiler filter for the boot image. Default: ");
+ UsageMsg(" speed-profile");
UsageMsg("--system-server-compiler-filter=<STRING>");
UsageMsg(" Compiler filter that overrides");
UsageMsg(" dalvik.vm.systemservercompilerfilter");
@@ -250,16 +257,21 @@
// by others and prevents system_server from loading generated artifacts.
umask(S_IWGRP | S_IWOTH);
- // Explicitly initialize logging (b/201042799).
- android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
-
OdrConfig config(argv[0]);
int n = InitializeConfig(argc, argv, &config);
+
+ // Explicitly initialize logging (b/201042799).
+ // But not in CompOS mode - logd doesn't exist in Microdroid (b/265153235).
+ if (!config.GetCompilationOsMode()) {
+ android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+ }
+
argv += n;
argc -= n;
if (argc != 1) {
ArgumentError("Expected 1 argument, but have %d.", argc);
}
+
GetSystemProperties(config.MutableSystemProperties());
OdrMetrics metrics(config.GetArtifactDirectory());
diff --git a/odrefresh/odrefresh_test.cc b/odrefresh/odrefresh_test.cc
index 4dd6f3a..31e03a2 100644
--- a/odrefresh/odrefresh_test.cc
+++ b/odrefresh/odrefresh_test.cc
@@ -29,6 +29,7 @@
#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "android-modules-utils/sdk_level.h"
#include "arch/instruction_set.h"
#include "base/common_art_test.h"
#include "base/file_utils.h"
@@ -201,7 +202,7 @@
config_.SetStandaloneSystemServerJars(Concatenate({services_foo_jar_, ":", services_bar_jar_}));
config_.SetIsa(InstructionSet::kX86_64);
config_.SetZygoteKind(ZygoteKind::kZygote64_32);
- config_.SetSystemServerCompilerFilter("speed"); // specify a default
+ config_.SetSystemServerCompilerFilter("");
config_.SetArtifactDirectory(dalvik_cache_dir_);
std::string staging_dir = dalvik_cache_dir_ + "/staging";
@@ -254,11 +255,41 @@
Contains(Concatenate({"--dex-file=", framework_jar_})),
Contains(FlagContains("--dex-fd=", FdOf(core_oj_jar_))),
Contains(FlagContains("--dex-fd=", FdOf(framework_jar_))),
- Contains(FlagContains("--profile-file-fd=", FdOf(art_profile_))),
- Contains(FlagContains("--profile-file-fd=", FdOf(framework_profile_))),
Contains(Concatenate({"--oat-location=", dalvik_cache_dir_, "/x86_64/boot.oat"})),
- Contains(HasSubstr("--base=")),
- Contains("--compiler-filter=speed-profile"))))
+ Contains(HasSubstr("--base=")))))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(odrefresh_->Compile(*metrics_,
+ CompilationOptions{
+ .compile_boot_classpath_for_isas = {InstructionSet::kX86_64},
+ }),
+ ExitCode::kCompilationSuccess);
+}
+
+TEST_F(OdRefreshTest, BootClasspathJarsWithExplicitCompilerFilter) {
+ config_.SetBootImageCompilerFilter("speed");
+
+ // Profiles should still be passed.
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(Contains(FlagContains("--profile-file-fd=", FdOf(art_profile_))),
+ Contains(FlagContains("--profile-file-fd=", FdOf(framework_profile_))),
+ Contains("--compiler-filter=speed"))))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(odrefresh_->Compile(*metrics_,
+ CompilationOptions{
+ .compile_boot_classpath_for_isas = {InstructionSet::kX86_64},
+ }),
+ ExitCode::kCompilationSuccess);
+}
+
+TEST_F(OdRefreshTest, BootClasspathJarsWithDefaultCompilerFilter) {
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(Contains(FlagContains("--profile-file-fd=", FdOf(art_profile_))),
+ Contains(FlagContains("--profile-file-fd=", FdOf(framework_profile_))),
+ Contains("--compiler-filter=speed-profile"))))
.WillOnce(Return(0));
EXPECT_EQ(odrefresh_->Compile(*metrics_,
@@ -361,9 +392,11 @@
ExitCode::kCompilationSuccess);
}
-TEST_F(OdRefreshTest, CompileSetsCompilerFilterToSpeed) {
- // Test setup: use "speed" compiler filter.
- config_.SetSystemServerCompilerFilter("speed");
+// Test setup: The compiler filter is explicitly set to "speed-profile". Use it regardless of
+// whether the profile exists or not. Dex2oat will fall back to "verify" if the profile doesn't
+// exist.
+TEST_F(OdRefreshTest, CompileSetsCompilerFilterWithExplicitValue) {
+ config_.SetSystemServerCompilerFilter("speed-profile");
// Uninteresting calls.
EXPECT_CALL(
@@ -375,13 +408,12 @@
*mock_exec_utils_,
DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", location_provider_jar_})),
Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=speed"))))
+ Contains("--compiler-filter=speed-profile"))))
.WillOnce(Return(0));
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=speed"))))
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
+ Contains(HasSubstr("--profile-file-fd=")),
+ Contains("--compiler-filter=speed-profile"))))
.WillOnce(Return(0));
EXPECT_EQ(
odrefresh_->Compile(*metrics_,
@@ -391,11 +423,9 @@
ExitCode::kCompilationSuccess);
}
-TEST_F(OdRefreshTest, CompileSetsCompilerFilterToSpeedProfile) {
- // Test setup: with "speed-profile" compiler filter in the request, only apply if there is a
- // profile, otherwise fallback to speed.
- config_.SetSystemServerCompilerFilter("speed-profile");
-
+// Test setup: The compiler filter is not explicitly set. Use "speed-profile" if there is a vetted
+// profile (on U+), otherwise fall back to "speed".
+TEST_F(OdRefreshTest, CompileSetsCompilerFilterWithDefaultValue) {
// Uninteresting calls.
EXPECT_CALL(
*mock_exec_utils_, DoExecAndReturnCode(_))
@@ -409,12 +439,20 @@
Not(Contains(HasSubstr("--profile-file-fd="))),
Contains("--compiler-filter=speed"))))
.WillOnce(Return(0));
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Contains(HasSubstr("--profile-file-fd=")),
- Contains("--compiler-filter=speed-profile"))))
- .WillOnce(Return(0));
+ // Only on U+ should we use the profile by default if available.
+ if (android::modules::sdklevel::IsAtLeastU()) {
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
+ Contains(HasSubstr("--profile-file-fd=")),
+ Contains("--compiler-filter=speed-profile"))))
+ .WillOnce(Return(0));
+ } else {
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
+ Not(Contains(HasSubstr("--profile-file-fd="))),
+ Contains("--compiler-filter=speed"))))
+ .WillOnce(Return(0));
+ }
EXPECT_EQ(
odrefresh_->Compile(*metrics_,
CompilationOptions{
@@ -423,36 +461,6 @@
ExitCode::kCompilationSuccess);
}
-TEST_F(OdRefreshTest, CompileSetsCompilerFilterToVerify) {
- // Test setup: use "speed" compiler filter.
- config_.SetSystemServerCompilerFilter("verify");
-
- // Uninteresting calls.
- EXPECT_CALL(
- *mock_exec_utils_, DoExecAndReturnCode(_))
- .Times(odrefresh_->AllSystemServerJars().size() - 2)
- .WillRepeatedly(Return(0));
-
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", location_provider_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=verify"))))
- .WillOnce(Return(0));
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=verify"))))
- .WillOnce(Return(0));
- EXPECT_EQ(
- odrefresh_->Compile(*metrics_,
- CompilationOptions{
- .system_server_jars_to_compile = odrefresh_->AllSystemServerJars(),
- }),
- ExitCode::kCompilationSuccess);
-}
-
TEST_F(OdRefreshTest, OutputFilesAndIsa) {
EXPECT_CALL(
*mock_exec_utils_,
diff --git a/openjdkjvm/Android.bp b/openjdkjvm/Android.bp
index 2757d9d..eb379d8 100644
--- a/openjdkjvm/Android.bp
+++ b/openjdkjvm/Android.bp
@@ -35,10 +35,10 @@
name: "art_openjdkjvm_license",
visibility: [":__subpackages__"],
license_kinds: [
- "SPDX-license-identifier-GPL-with-classpath-exception",
+ "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
],
license_text: [
- "NOTICE",
+ "LICENSE",
],
}
diff --git a/openjdkjvm/NOTICE b/openjdkjvm/LICENSE
similarity index 100%
rename from openjdkjvm/NOTICE
rename to openjdkjvm/LICENSE
diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
index 8c1f773..da6fc85 100644
--- a/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
@@ -32,6 +32,8 @@
/*
* Services that OpenJDK expects the VM to provide.
*/
+
+#include <assert.h>
#include <dlfcn.h>
#include <limits.h>
#include <stdio.h>
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index d01357f..d2ac9b6 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -36,10 +36,10 @@
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-GPL-with-classpath-exception",
+ "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
],
license_text: [
- "NOTICE",
+ "LICENSE",
],
}
@@ -111,7 +111,6 @@
shared_libs: [
"libart",
"libart-compiler",
- "libart-dexlayout",
"libdexfile",
"libartbase",
],
@@ -130,7 +129,6 @@
shared_libs: [
"libartd",
"libartd-compiler",
- "libartd-dexlayout",
"libdexfiled",
"libartbased",
],
diff --git a/openjdkjvmti/NOTICE b/openjdkjvmti/LICENSE
similarity index 100%
rename from openjdkjvmti/NOTICE
rename to openjdkjvmti/LICENSE
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 09900e1..276b3a8 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -89,12 +89,6 @@
} \
} while (false)
-// Returns whether we are able to use all jvmti features.
-static bool IsFullJvmtiAvailable() {
- art::Runtime* runtime = art::Runtime::Current();
- return runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable();
-}
-
class JvmtiFunctions {
private:
static jvmtiError getEnvironmentError(jvmtiEnv* env) {
@@ -1474,19 +1468,21 @@
FieldUtil::Register(gEventHandler);
BreakpointUtil::Register(gEventHandler);
Transformer::Register(gEventHandler);
-
- {
- // Make sure we can deopt anything we need to.
- art::ScopedSuspendAll ssa(__FUNCTION__);
- gDeoptManager->FinishSetup();
- }
-
+ gDeoptManager->FinishSetup();
runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
return true;
}
extern "C" bool ArtPlugin_Deinitialize() {
+ // When runtime is shutting down, it is not necessary to unregister callbacks or update
+ // instrumentation levels. Removing callbacks require a GC critical section in some cases and
+ // when runtime is shutting down we already stop GC and hence it is not safe to request to
+ // enter a GC critical section.
+ if (art::Runtime::Current()->IsShuttingDown(art::Thread::Current())) {
+ return true;
+ }
+
gEventHandler->Shutdown();
gDeoptManager->Shutdown();
PhaseUtil::Unregister();
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 083ba6d..bc965a2 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -47,9 +47,11 @@
#include "base/strlcpy.h"
#include "base/mutex.h"
#include "events.h"
+#include "instrumentation.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_env_ext.h"
#include "jvmti.h"
+#include "runtime.h"
#include "ti_breakpoint.h"
namespace art {
@@ -69,6 +71,13 @@
// This is the value 0x70010200.
static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+// Returns whether we are able to use all jvmti features.
+static inline bool IsFullJvmtiAvailable() {
+ art::Runtime* runtime = art::Runtime::Current();
+ return runtime->GetInstrumentation()->IsForcedInterpretOnly() ||
+ runtime->IsJavaDebuggableAtInit();
+}
+
// A structure that is a jvmtiEnv with additional information for the runtime.
struct ArtJvmTiEnv : public jvmtiEnv {
art::JavaVMExt* art_vm;
@@ -141,7 +150,7 @@
explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
JvmtiDeleter(JvmtiDeleter&) = default;
- JvmtiDeleter(JvmtiDeleter&&) = default;
+ JvmtiDeleter(JvmtiDeleter&&) noexcept = default;
JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
void operator()(T* ptr) const {
@@ -161,7 +170,7 @@
explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
JvmtiDeleter(JvmtiDeleter&) = default;
- JvmtiDeleter(JvmtiDeleter&&) = default;
+ JvmtiDeleter(JvmtiDeleter&&) noexcept = default;
JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
template <typename U>
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 312a797..978843c 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -47,10 +47,12 @@
#include "gc/scoped_gc_critical_section.h"
#include "instrumentation.h"
#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "jni/jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/object_array-inl.h"
#include "nativehelper/scoped_local_ref.h"
+#include "oat_file_manager.h"
#include "read_barrier_config.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
@@ -61,16 +63,15 @@
namespace openjdkjvmti {
-// TODO We should make this much more selective in the future so we only return true when we
+static constexpr const char* kInstrumentationKey = "JVMTI_DeoptRequester";
+
+// We could make this much more selective in the future so we only return true when we
// actually care about the method at this time (ie active frames had locals changed). For now we
-// just assume that if anything has changed any frame's locals we care about all methods. If nothing
-// has we only care about methods with active breakpoints on them. In the future we should probably
-// rewrite all of this to instead do this at the ShadowFrame or thread granularity.
-bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) {
- // In non-java-debuggable runtimes the breakpoint check would miss if we have breakpoints on
- // methods that are inlined. Since these features are best effort in non-java-debuggable
- // runtimes it is OK to be less precise. For debuggable runtimes, inlining is disabled.
- return manager_->HaveLocalsChanged() || manager_->MethodHasBreakpoints(method);
+// just assume that if anything has changed any frame's locals we care about all methods. This only
+// impacts whether we are able to OSR or not so maybe not really important to maintain frame
+// specific information.
+bool JvmtiMethodInspectionCallback::HaveLocalsChanged() {
+ return manager_->HaveLocalsChanged();
}
DeoptManager::DeoptManager()
@@ -94,14 +95,6 @@
callbacks->AddMethodInspectionCallback(&inspection_callback_);
}
-void DeoptManager::Shutdown() {
- art::ScopedThreadStateChange stsc(art::Thread::Current(),
- art::ThreadState::kWaitingForDebuggerToAttach);
- art::ScopedSuspendAll ssa("remove method Inspection Callback");
- art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
- callbacks->RemoveMethodInspectionCallback(&inspection_callback_);
-}
-
void DeoptManager::DumpDeoptInfo(art::Thread* self, std::ostream& stream) {
art::ScopedObjectAccess soa(self);
art::MutexLock mutll(self, *art::Locks::thread_list_lock_);
@@ -151,48 +144,59 @@
void DeoptManager::FinishSetup() {
art::Thread* self = art::Thread::Current();
- art::MutexLock mu(self, deoptimization_status_lock_);
-
art::Runtime* runtime = art::Runtime::Current();
- // See if we need to do anything.
- if (!runtime->IsJavaDebuggable()) {
- // See if we can enable all JVMTI functions. If this is false, only kArtTiVersion agents can be
- // retrieved and they will all be best-effort.
- if (PhaseUtil::GetPhaseUnchecked() == JVMTI_PHASE_ONLOAD) {
- // We are still early enough to change the compiler options and get full JVMTI support.
- LOG(INFO) << "Openjdkjvmti plugin loaded on a non-debuggable runtime. Changing runtime to "
- << "debuggable state. Please pass '--debuggable' to dex2oat and "
- << "'-Xcompiler-option --debuggable' to dalvikvm in the future.";
- DCHECK(runtime->GetJit() == nullptr) << "Jit should not be running yet!";
- runtime->AddCompilerOption("--debuggable");
- runtime->SetJavaDebuggable(true);
- } else {
- LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was "
- << "loaded too late to change runtime state to DEBUGGABLE. Only kArtTiVersion "
- << "(0x" << std::hex << kArtTiVersion << ") environments are available. Some "
- << "functionality might not work properly.";
- if (runtime->GetJit() == nullptr &&
- runtime->GetJITOptions()->UseJitCompilation() &&
- !runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
- // If we don't have a jit we should try to start the jit for performance reasons. We only
- // need to do this for late attach on non-debuggable processes because for debuggable
- // processes we already rely on jit and we cannot force this jit to start if we are still in
- // OnLoad since the runtime hasn't started up sufficiently. This is only expected to happen
- // on userdebug/eng builds.
- LOG(INFO) << "Attempting to start jit for openjdkjvmti plugin.";
- // Note: use rwx allowed = true, because if this is the system server, we will not be
- // allowed to allocate any JIT code cache, anyways.
- runtime->CreateJitCodeCache(/*rwx_memory_allowed=*/true);
- runtime->CreateJit();
- if (runtime->GetJit() == nullptr) {
- LOG(WARNING) << "Could not start jit for openjdkjvmti plugin. This process might be "
- << "quite slow as it is running entirely in the interpreter. Try running "
- << "'setenforce 0' and restarting this process.";
- }
- }
- }
- runtime->DeoptimizeBootImage();
+ if (runtime->IsJavaDebuggable()) {
+ return;
}
+
+ // See if we can enable all JVMTI functions.
+ if (PhaseUtil::GetPhaseUnchecked() == JVMTI_PHASE_ONLOAD) {
+ // We are still early enough to change the compiler options and get full JVMTI support.
+ LOG(INFO) << "Openjdkjvmti plugin loaded on a non-debuggable runtime. Changing runtime to "
+ << "debuggable state. Please pass '--debuggable' to dex2oat and "
+ << "'-Xcompiler-option --debuggable' to dalvikvm in the future.";
+ DCHECK(runtime->GetJit() == nullptr) << "Jit should not be running yet!";
+ art::ScopedSuspendAll ssa(__FUNCTION__);
+ // TODO check if we need to hold deoptimization_status_lock_ here.
+ art::MutexLock mu(self, deoptimization_status_lock_);
+ runtime->AddCompilerOption("--debuggable");
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
+ runtime->DeoptimizeBootImage();
+ return;
+ }
+
+ // Runtime has already started in non-debuggable mode. Only kArtTiVersion agents can be
+ // retrieved and they will all be best-effort.
+ LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was "
+ << "loaded too late to change runtime state to support all capabilities. Only "
+ << "kArtTiVersion (0x" << std::hex << kArtTiVersion << ") environments are "
+ << "available. Some functionality might not work properly.";
+
+ // Transition the runtime to debuggable:
+ // 1. Wait for any background verification tasks to finish. We don't support
+ // background verification after moving to debuggable state.
+ runtime->GetOatFileManager().WaitForBackgroundVerificationTasksToFinish();
+
+ // Do the transition in ScopedJITSuspend, so we don't start any JIT compilations
+ // before the transition to debuggable is finished.
+ art::jit::ScopedJitSuspend suspend_jit;
+ art::ScopedSuspendAll ssa(__FUNCTION__);
+
+ // 2. Discard any JITed code that was generated before, since they would be
+ // compiled without debug support.
+ art::jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr) {
+ jit->GetCodeCache()->InvalidateAllCompiledCode();
+ jit->GetCodeCache()->TransitionToDebuggable();
+ jit->GetJitCompiler()->SetDebuggableCompilerOption(true);
+ }
+
+ // 3. Change the state to JavaDebuggable, so that debug features can be
+ // enabled from now on.
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggable);
+
+ // 4. Update all entrypoints to avoid using any AOT code.
+ runtime->GetInstrumentation()->UpdateEntrypointsForDebuggable();
}
bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) {
@@ -365,6 +369,30 @@
}
}
+void DeoptManager::Shutdown() {
+ art::Thread* self = art::Thread::Current();
+ art::Runtime* runtime = art::Runtime::Current();
+
+ // Do the transition in ScopedJITSuspend, so we don't start any JIT compilations
+ // before the transition to debuggable is finished.
+ art::jit::ScopedJitSuspend suspend_jit;
+
+ art::ScopedThreadStateChange sts(self, art::ThreadState::kSuspended);
+ deoptimization_status_lock_.ExclusiveLock(self);
+ ScopedDeoptimizationContext sdc(self, this);
+
+ art::RuntimeCallbacks* callbacks = runtime->GetRuntimeCallbacks();
+ callbacks->RemoveMethodInspectionCallback(&inspection_callback_);
+
+ if (runtime->IsShuttingDown(self)) {
+ return;
+ }
+
+ runtime->GetInstrumentation()->DisableDeoptimization(kInstrumentationKey);
+ runtime->GetInstrumentation()->DisableDeoptimization(kDeoptManagerInstrumentationKey);
+ runtime->GetInstrumentation()->MaybeSwitchRuntimeDebugState(self);
+}
+
void DeoptManager::RemoveDeoptimizeAllMethodsLocked(art::Thread* self) {
DCHECK_GT(global_deopt_count_, 0u) << "Request to remove non-existent global deoptimization!";
global_deopt_count_--;
@@ -438,7 +466,6 @@
return OK;
}
-static constexpr const char* kInstrumentationKey = "JVMTI_DeoptRequester";
void DeoptManager::RemoveDeoptimizationRequester() {
art::Thread* self = art::Thread::Current();
@@ -460,6 +487,15 @@
art::ScopedThreadStateChange stsc(self, art::ThreadState::kSuspended);
deoptimization_status_lock_.ExclusiveLock(self);
deopter_count_++;
+ if (deopter_count_ == 1) {
+ // When we add a deoptimization requester, we should enable entry / exit hooks. We only call
+ // this in debuggable runtimes and hence it won't be necessary to update entrypoints but we
+ // still need to inform instrumentation that we need to actually run entry / exit hooks. Though
+ // entrypoints are capable of running entry / exit hooks they won't run them unless enabled.
+ ScopedDeoptimizationContext sdc(self, this);
+ art::Runtime::Current()->GetInstrumentation()->EnableEntryExitHooks(kInstrumentationKey);
+ return;
+ }
deoptimization_status_lock_.ExclusiveUnlock(self);
}
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
index c0a788b..e9b91de 100644
--- a/openjdkjvmti/deopt_manager.h
+++ b/openjdkjvmti/deopt_manager.h
@@ -57,8 +57,7 @@
public:
explicit JvmtiMethodInspectionCallback(DeoptManager* manager) : manager_(manager) {}
- bool IsMethodBeingInspected(art::ArtMethod* method)
- override REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool HaveLocalsChanged() override REQUIRES_SHARED(art::Locks::mutator_lock_);
private:
DeoptManager* manager_;
@@ -111,9 +110,7 @@
REQUIRES_SHARED(art::Locks::mutator_lock_);
void DeoptimizeAllThreads() REQUIRES_SHARED(art::Locks::mutator_lock_);
- void FinishSetup()
- REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
- REQUIRES(art::Locks::mutator_lock_);
+ void FinishSetup() REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_);
static DeoptManager* Get();
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index a6425af..8f54003 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -447,9 +447,8 @@
if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWait)) {
art::Thread* self = art::Thread::Current();
art::JNIEnvExt* jnienv = self->GetJniEnv();
- art::ArtField* parkBlockerField = art::jni::DecodeArtField(
- art::WellKnownClasses::java_lang_Thread_parkBlocker);
- art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ art::ObjPtr<art::mirror::Object> blocker_obj =
+ art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer());
if (blocker_obj.IsNull()) {
blocker_obj = self->GetPeer();
}
@@ -505,9 +504,8 @@
if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWaited)) {
art::Thread* self = art::Thread::Current();
art::JNIEnvExt* jnienv = self->GetJniEnv();
- art::ArtField* parkBlockerField = art::jni::DecodeArtField(
- art::WellKnownClasses::java_lang_Thread_parkBlocker);
- art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ art::ObjPtr<art::mirror::Object> blocker_obj =
+ art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer());
if (blocker_obj.IsNull()) {
blocker_obj = self->GetPeer();
}
@@ -741,7 +739,6 @@
// Call-back for when a method is popped due to an exception throw. A method will either cause a
// MethodExited call-back or a MethodUnwind call-back when its activation is removed.
void MethodUnwind(art::Thread* self,
- art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
art::ArtMethod* method,
uint32_t dex_pc ATTRIBUTE_UNUSED)
REQUIRES_SHARED(art::Locks::mutator_lock_) override {
@@ -1133,13 +1130,11 @@
switch (event) {
case ArtJvmtiEvent::kBreakpoint:
case ArtJvmtiEvent::kException:
- return DeoptRequirement::kLimited;
- // TODO MethodEntry is needed due to inconsistencies between the interpreter and the trampoline
- // in how to handle exceptions.
case ArtJvmtiEvent::kMethodEntry:
+ case ArtJvmtiEvent::kMethodExit:
+ return DeoptRequirement::kLimited;
case ArtJvmtiEvent::kExceptionCatch:
return DeoptRequirement::kFull;
- case ArtJvmtiEvent::kMethodExit:
case ArtJvmtiEvent::kFieldModification:
case ArtJvmtiEvent::kFieldAccess:
case ArtJvmtiEvent::kSingleStep:
@@ -1256,10 +1251,10 @@
}
for (auto& m : klass->GetMethods(art::kRuntimePointerSize)) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
- if (m.IsNative() || m.IsProxyMethod()) {
+ if (m.IsNative() || m.IsProxyMethod() || !m.IsInvokable()) {
continue;
} else if (!runtime_->GetClassLinker()->IsQuickToInterpreterBridge(code) &&
- !runtime_->IsAsyncDeoptimizeable(reinterpret_cast<uintptr_t>(code))) {
+ !runtime_->IsAsyncDeoptimizeable(&m, reinterpret_cast<uintptr_t>(code))) {
runtime_->GetInstrumentation()->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
}
}
diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h
index bd4c85b..4adf769 100644
--- a/openjdkjvmti/jvmti_allocator.h
+++ b/openjdkjvmti/jvmti_allocator.h
@@ -46,13 +46,13 @@
template <>
class JvmtiAllocator<void> {
public:
- typedef void value_type;
- typedef void* pointer;
- typedef const void* const_pointer;
+ using value_type = void;
+ using pointer = void*;
+ using const_pointer = const void*;
template <typename U>
struct rebind {
- typedef JvmtiAllocator<U> other;
+ using other = JvmtiAllocator<U>;
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
@@ -79,17 +79,17 @@
template <typename T>
class JvmtiAllocator {
public:
- typedef T value_type;
- typedef T* pointer;
- typedef T& reference;
- typedef const T* const_pointer;
- typedef const T& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
+ using value_type = T;
+ using pointer = T*;
+ using reference = T&;
+ using const_pointer = const T*;
+ using const_reference = const T&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
template <typename U>
struct rebind {
- typedef JvmtiAllocator<U> other;
+ using other = JvmtiAllocator<U>;
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 9752fb1..37ceb37 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -80,7 +80,7 @@
#include "ti_phase.h"
#include "ti_redefine.h"
#include "transform.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -927,40 +927,42 @@
} else if (count_ptr == nullptr || classes == nullptr) {
return ERR(NULL_POINTER);
}
- art::JNIEnvExt* jnienv = self->GetJniEnv();
- if (loader == nullptr ||
- jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_BootClassLoader)) {
+ std::vector<const art::DexFile*> dex_files_storage;
+ const std::vector<const art::DexFile*>* dex_files = nullptr;
+ if (loader == nullptr) {
// We can just get the dex files directly for the boot class path.
- return CopyClassDescriptors(env,
- art::Runtime::Current()->GetClassLinker()->GetBootClassPath(),
- count_ptr,
- classes);
+ dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath();
+ } else {
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<1> hs(self);
+ art::Handle<art::mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
+ if (class_loader->InstanceOf(art::WellKnownClasses::java_lang_BootClassLoader.Get())) {
+ // We can just get the dex files directly for the boot class path.
+ dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath();
+ } else if (!class_loader->InstanceOf(art::WellKnownClasses::java_lang_ClassLoader.Get())) {
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (!class_loader->InstanceOf(
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
+ JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for "
+ << "BootClassPath and dalvik.system.BaseDexClassLoader class loaders";
+ // TODO Possibly return OK With no classes would be better since these ones cannot have any
+ // real classes associated with them.
+ return ERR(NOT_IMPLEMENTED);
+ } else {
+ art::VisitClassLoaderDexFiles(
+ self,
+ class_loader,
+ [&](const art::DexFile* dex_file) {
+ dex_files_storage.push_back(dex_file);
+ return true; // Continue with other dex files.
+ });
+ dex_files = &dex_files_storage;
+ }
}
- if (!jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_ClassLoader)) {
- return ERR(ILLEGAL_ARGUMENT);
- } else if (!jnienv->IsInstanceOf(loader,
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
- JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for "
- << "BootClassPath and dalvik.system.BaseDexClassLoader class loaders";
- // TODO Possibly return OK With no classes would be better since these ones cannot have any
- // real classes associated with them.
- return ERR(NOT_IMPLEMENTED);
- }
-
- art::ScopedObjectAccess soa(self);
- art::StackHandleScope<1> hs(self);
- art::Handle<art::mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
- std::vector<const art::DexFile*> dex_files;
- art::VisitClassLoaderDexFiles(
- soa,
- class_loader,
- [&](const art::DexFile* dex_file) {
- dex_files.push_back(dex_file);
- return true; // Continue with other dex files.
- });
// We hold the loader so the dex files won't go away until after this call at worst.
- return CopyClassDescriptors(env, dex_files, count_ptr, classes);
+ DCHECK(dex_files != nullptr);
+ return CopyClassDescriptors(env, *dex_files, count_ptr, classes);
}
jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env,
@@ -973,19 +975,17 @@
return ERR(NULL_POINTER);
}
art::Thread* self = art::Thread::Current();
- if (!self->GetJniEnv()->IsInstanceOf(initiating_loader,
- art::WellKnownClasses::java_lang_ClassLoader)) {
- return ERR(ILLEGAL_ARGUMENT);
- }
- if (self->GetJniEnv()->IsInstanceOf(initiating_loader,
- art::WellKnownClasses::java_lang_BootClassLoader)) {
- // Need to use null for the BootClassLoader.
- initiating_loader = nullptr;
- }
-
art::ScopedObjectAccess soa(self);
art::ObjPtr<art::mirror::ClassLoader> class_loader =
soa.Decode<art::mirror::ClassLoader>(initiating_loader);
+ if (class_loader == nullptr) {
+ // Keep null, meaning the boot class loader.
+ } else if (!class_loader->InstanceOf(art::WellKnownClasses::java_lang_ClassLoader.Get())) {
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (class_loader->InstanceOf(art::WellKnownClasses::java_lang_BootClassLoader.Get())) {
+ // Need to use null for the BootClassLoader.
+ class_loader = nullptr;
+ }
art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h
index 29ea684..f6b0126 100644
--- a/openjdkjvmti/ti_class_loader-inl.h
+++ b/openjdkjvmti/ti_class_loader-inl.h
@@ -48,8 +48,8 @@
art::Handle<art::mirror::ClassLoader> loader,
const Visitor& visitor) {
art::StackHandleScope<1> hs(self);
- art::ArtField* element_dex_file_field = art::jni::DecodeArtField(
- art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ art::ArtField* element_dex_file_field =
+ art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(
hs.NewHandle(GetDexElementList(self, loader)));
diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc
index d0a6634..c117937 100644
--- a/openjdkjvmti/ti_class_loader.cc
+++ b/openjdkjvmti/ti_class_loader.cc
@@ -57,16 +57,17 @@
#include "object_lock.h"
#include "runtime.h"
#include "transform.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
bool ClassLoaderHelper::AddToClassLoader(art::Thread* self,
art::Handle<art::mirror::ClassLoader> loader,
const art::DexFile* dex_file) {
- art::ScopedObjectAccessUnchecked soa(self);
art::StackHandleScope<3> hs(self);
- if (art::ClassLinker::IsBootClassLoader(soa, loader.Get())) {
- art::Runtime::Current()->GetClassLinker()->AppendToBootClassPath(self, dex_file);
+ if (art::ClassLinker::IsBootClassLoader(loader.Get())) {
+ art::Runtime::Current()->AppendToBootClassPath(
+ dex_file->GetLocation(), dex_file->GetLocation(), {dex_file});
return true;
}
art::Handle<art::mirror::Object> java_dex_file_obj(
@@ -139,15 +140,14 @@
art::Handle<art::mirror::ClassLoader> loader) {
art::StackHandleScope<4> hs(self);
- art::Handle<art::mirror::Class>
- base_dex_loader_class(hs.NewHandle(self->DecodeJObject(
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader)->AsClass()));
+ art::Handle<art::mirror::Class> base_dex_loader_class =
+ hs.NewHandle(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get());
// Get all the ArtFields so we can look in the BaseDexClassLoader
- art::ArtField* path_list_field = art::jni::DecodeArtField(
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
+ art::ArtField* path_list_field =
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
art::ArtField* dex_path_list_element_field =
- art::jni::DecodeArtField(art::WellKnownClasses::dalvik_system_DexPathList_dexElements);
+ art::WellKnownClasses::dalvik_system_DexPathList_dexElements;
// Check if loader is a BaseDexClassLoader
art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index 10ea43a..02dc9f1 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -398,8 +398,7 @@
// These require index-ids and debuggable to function
art::Runtime* runtime = art::Runtime::Current();
- if (runtime->GetJniIdType() == art::JniIdType::kIndices &&
- (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) {
+ if (runtime->GetJniIdType() == art::JniIdType::kIndices && IsFullJvmtiAvailable()) {
// IsStructurallyModifiableClass
error = add_extension(
reinterpret_cast<jvmtiExtensionFunction>(Redefiner::IsStructurallyModifiableClass),
@@ -703,8 +702,7 @@
return error;
}
art::Runtime* runtime = art::Runtime::Current();
- if (runtime->GetJniIdType() == art::JniIdType::kIndices &&
- (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) {
+ if (runtime->GetJniIdType() == art::JniIdType::kIndices && IsFullJvmtiAvailable()) {
error = add_extension(
ArtJvmtiEvent::kStructuralDexFileLoadHook,
"com.android.art.class.structural_dex_file_load_hook",
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 08e824b..d40964f 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -130,7 +130,7 @@
#include "transform.h"
#include "verifier/class_verifier.h"
#include "verifier/verifier_enums.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
#include "write_barrier.h"
namespace openjdkjvmti {
@@ -285,8 +285,7 @@
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- ObsoleteMap* obsolete_maps)
- REQUIRES(art::Locks::mutator_lock_) {
+ ObsoleteMap* obsolete_maps) REQUIRES(art::Locks::mutator_lock_) {
ObsoleteMethodStackVisitor visitor(thread,
allocator,
obsoleted_methods,
@@ -456,8 +455,7 @@
}
// Check Thread specifically since it's not a root but too many things reach into it with Unsafe
// too allow structural redefinition.
- if (klass->IsAssignableFrom(
- self->DecodeJObject(art::WellKnownClasses::java_lang_Thread)->AsClass())) {
+ if (klass->IsAssignableFrom(art::WellKnownClasses::java_lang_Thread.Get())) {
*error_msg =
"java.lang.Thread has fields accessed using sun.misc.unsafe directly. It is not "
"safe to structurally redefine it.";
@@ -514,8 +512,15 @@
art::MemMap Redefiner::MoveDataToMemMap(const std::string& original_location,
art::ArrayRef<const unsigned char> data,
std::string* error_msg) {
+ std::string modified_location = StringPrintf("%s-transformed", original_location.c_str());
+ // A dangling multi-dex location appended to bootclasspath can cause inaccuracy in oat file
+ // validation. For simplicity, just convert it to a normal location.
+ size_t pos = modified_location.find(art::DexFileLoader::kMultiDexSeparator);
+ if (pos != std::string::npos) {
+ modified_location[pos] = '-';
+ }
art::MemMap map = art::MemMap::MapAnonymous(
- StringPrintf("%s-transformed", original_location.c_str()).c_str(),
+ modified_location.c_str(),
data.size(),
PROT_READ|PROT_WRITE,
/*low_4gb=*/ false,
@@ -547,6 +552,13 @@
if (driver_ != nullptr && lock_acquired_) {
GetMirrorClass()->MonitorExit(driver_->self_);
}
+ if (art::kIsDebugBuild) {
+ if (dex_file_ != nullptr) {
+ art::Thread* self = art::Thread::Current();
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ CHECK(!cl->IsDexFileRegistered(self, *dex_file_));
+ }
+ }
}
template<RedefinitionType kType>
@@ -1219,6 +1231,8 @@
actually_structural_(redefinitions_->size(), false),
initial_structural_(redefinitions_->size(), false) {}
+ ~RedefinitionDataHolder() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
bool IsNull() const REQUIRES_SHARED(art::Locks::mutator_lock_) {
return arr_.IsNull();
}
@@ -1425,8 +1439,9 @@
RedefinitionDataIter(const RedefinitionDataIter&) = default;
RedefinitionDataIter(RedefinitionDataIter&&) = default;
- RedefinitionDataIter& operator=(const RedefinitionDataIter&) = default;
- RedefinitionDataIter& operator=(RedefinitionDataIter&&) = default;
+ // Assignments are deleted because holder_ is a reference.
+ RedefinitionDataIter& operator=(const RedefinitionDataIter&) = delete;
+ RedefinitionDataIter& operator=(RedefinitionDataIter&&) = delete;
bool operator==(const RedefinitionDataIter& other) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -1615,6 +1630,24 @@
return RedefinitionDataIter(Length(), *this);
}
+RedefinitionDataHolder::~RedefinitionDataHolder() {
+ art::Thread* self = art::Thread::Current();
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ for (RedefinitionDataIter data = begin(); data != end(); ++data) {
+ art::ObjPtr<art::mirror::DexCache> dex_cache = data.GetNewDexCache();
+ // When redefinition fails, the dex file will be deleted in the
+ // `ClassRedefinition` destructor. To avoid having a heap `DexCache` pointing
+ // to a dangling pointer, we clear the entries of those dex caches that are
+ // not registered in the runtime.
+ if (dex_cache != nullptr &&
+ dex_cache->GetDexFile() != nullptr &&
+ !cl->IsDexFileRegistered(self, *dex_cache->GetDexFile())) {
+ dex_cache->ResetNativeArrays();
+ dex_cache->SetDexFile(nullptr);
+ }
+ }
+}
+
bool Redefiner::ClassRedefinition::CheckVerification(const RedefinitionDataIter& iter) {
DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
art::StackHandleScope<3> hs(driver_->self_);
@@ -1653,10 +1686,12 @@
art::MutableHandle<art::mirror::LongArray> old_cookie(
hs.NewHandle<art::mirror::LongArray>(nullptr));
bool has_older_cookie = false;
- // See if we already have a cookie that a previous redefinition got from the same classloader.
+ // See if we already have a cookie that a previous redefinition got from the same classloader
+ // and the same JavaDex file.
for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) {
- if (old_data.GetSourceClassLoader() == source_class_loader.Get()) {
- // Since every instance of this classloader should have the same cookie associated with it we
+ if (old_data.GetSourceClassLoader() == source_class_loader.Get() &&
+ old_data.GetJavaDexFile() == dex_file_obj.Get()) {
+ // Since every instance of this JavaDex file should have the same cookie associated with it we
// can stop looking here.
has_older_cookie = true;
old_cookie.Assign(old_data.GetNewDexFileCookie());
@@ -1681,12 +1716,13 @@
// Save the cookie.
cur_data->SetNewDexFileCookie(new_cookie.Get());
- // If there are other copies of this same classloader we need to make sure that we all have the
- // same cookie.
+ // If there are other copies of the same classloader and the same JavaDex file we need to
+ // make sure that we all have the same cookie.
if (has_older_cookie) {
for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) {
// We will let the GC take care of the cookie we allocated for this one.
- if (old_data.GetSourceClassLoader() == source_class_loader.Get()) {
+ if (old_data.GetSourceClassLoader() == source_class_loader.Get() &&
+ old_data.GetJavaDexFile() == dex_file_obj.Get()) {
old_data.SetNewDexFileCookie(new_cookie.Get());
}
}
@@ -1804,13 +1840,12 @@
bool Redefiner::ClassRedefinition::FinishRemainingCommonAllocations(
/*out*/RedefinitionDataIter* cur_data) {
- art::ScopedObjectAccessUnchecked soa(driver_->self_);
art::StackHandleScope<2> hs(driver_->self_);
cur_data->SetMirrorClass(GetMirrorClass());
// This shouldn't allocate
art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader()));
// The bootclasspath is handled specially so it doesn't have a j.l.DexFile.
- if (!art::ClassLinker::IsBootClassLoader(soa, loader.Get())) {
+ if (!art::ClassLinker::IsBootClassLoader(loader.Get())) {
cur_data->SetSourceClassLoader(loader.Get());
art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(
ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader)));
@@ -2220,6 +2255,11 @@
}
void Redefiner::ClassRedefinition::ReleaseDexFile() {
+ if (art::kIsDebugBuild) {
+ art::Thread* self = art::Thread::Current();
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ CHECK(cl->IsDexFileRegistered(self, *dex_file_));
+ }
dex_file_.release(); // NOLINT b/117926937
}
@@ -2479,7 +2519,9 @@
art::ClassLinker* cl = runtime_->GetClassLinker();
if (data.GetSourceClassLoader() == nullptr) {
// AppendToBootClassPath includes dex file registration.
- cl->AppendToBootClassPath(&data.GetRedefinition().GetDexFile(), data.GetNewDexCache());
+ const art::DexFile& dex_file = data.GetRedefinition().GetDexFile();
+ runtime_->AppendToBootClassPath(
+ dex_file.GetLocation(), dex_file.GetLocation(), {{&dex_file, data.GetNewDexCache()}});
} else {
cl->RegisterExistingDexCache(data.GetNewDexCache(), data.GetSourceClassLoader());
}
@@ -2912,6 +2954,27 @@
// be undone. This replaces the mirror::Class in 'holder' as well. It's magic!
HeapExtensions::ReplaceReferences(driver_->self_, map);
+ // Undo the replacement of old_class with new_class for the methods / fields on the old_class.
+ // It is hard to ensure that we don't replace the declaring class of the old class field / methods
+ // isn't impacted by ReplaceReferences. It is just simpler to undo the replacement here.
+ std::for_each(
+ old_classes_vec.cbegin(),
+ old_classes_vec.cend(),
+ [](art::ObjPtr<art::mirror::Class> orig) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ orig->VisitMethods(
+ [&](art::ArtMethod* method) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (method->IsCopied()) {
+ // Copied methods have interfaces as their declaring class.
+ return;
+ }
+ method->SetDeclaringClass(orig);
+ },
+ art::kRuntimePointerSize);
+ orig->VisitFields([&](art::ArtField* field) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ field->SetDeclaringClass(orig);
+ });
+ });
+
// Save the old class so that the JIT gc doesn't get confused by it being collected before the
// jit code. This is also needed to keep the dex-caches of any obsolete methods live.
for (auto [new_class, old_class] :
@@ -3083,10 +3146,14 @@
// First save the old values of the 2 arrays that make up the obsolete methods maps. Then
// allocate the 2 arrays that make up the obsolete methods map. Since the contents of the arrays
// are only modified when all threads (other than the modifying one) are suspended we don't need
- // to worry about missing the unsyncronized writes to the array. We do synchronize when setting
+ // to worry about missing the unsynchronized writes to the array. We do synchronize when setting
// it however, since that can happen at any time.
cur_data->SetOldObsoleteMethods(ext->GetObsoleteMethods());
cur_data->SetOldDexCaches(ext->GetObsoleteDexCaches());
+ // FIXME: The `ClassExt::ExtendObsoleteArrays()` is non-atomic and does not ensure proper
+ // memory visibility, so it can race with `ArtMethod::GetObsoleteDexCache()`.
+ // We should allocate the new arrays here but record it in the redefinition data and set the
+ // new arrays in `ClassExt` later with all other threads suspended.
if (!art::mirror::ClassExt::ExtendObsoleteArrays(
ext, driver_->self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) {
// OOM. Clear exception and return error.
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index 85b1070..cdd6627 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -126,7 +126,7 @@
~ClassRedefinition() NO_THREAD_SAFETY_ANALYSIS;
// Move assignment so we can sort these in a vector.
- ClassRedefinition& operator=(ClassRedefinition&& other) {
+ ClassRedefinition& operator=(ClassRedefinition&& other) noexcept {
driver_ = other.driver_;
klass_ = other.klass_;
dex_file_ = std::move(other.dex_file_);
@@ -138,7 +138,7 @@
}
// Move constructor so we can put these into a vector.
- ClassRedefinition(ClassRedefinition&& other)
+ ClassRedefinition(ClassRedefinition&& other) noexcept
: driver_(other.driver_),
klass_(other.klass_),
dex_file_(std::move(other.dex_file_)),
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index 526836e..4d96732 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -60,7 +60,7 @@
#include "thread_list.h"
#include "ti_logging.h"
#include "ti_phase.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -247,12 +247,8 @@
return ERR(ILLEGAL_ARGUMENT);
}
- art::ScopedObjectAccess soa(art::Thread::Current());
- for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
- current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), dex_file.release());
- }
-
- return ERR(NONE);
+ current->AddExtraBootDexFiles(segment, segment, std::move(dex_files));
+ return OK;
}
jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env,
@@ -343,33 +339,33 @@
// exceptions are swallowed.
art::Thread* self = art::Thread::Current();
- JNIEnv* env = self->GetJniEnv();
- if (!env->IsInstanceOf(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<2u> hs(self);
+ art::Handle<art::mirror::ClassLoader> class_loader =
+ hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(classloader));
+ if (!class_loader->InstanceOf(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!";
return ERR(CLASS_LOADER_UNSUPPORTED);
}
- jmethodID add_dex_path_id = env->GetMethodID(
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
- "addDexPath",
- "(Ljava/lang/String;)V");
+ art::ArtMethod* add_dex_path_id =
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader->FindClassMethod(
+ "addDexPath", "(Ljava/lang/String;)V", art::kRuntimePointerSize);
if (add_dex_path_id == nullptr) {
return ERR(INTERNAL);
}
- ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
- if (dex_path.get() == nullptr) {
+ art::Handle<art::mirror::String> dex_path =
+ hs.NewHandle(art::mirror::String::AllocFromModifiedUtf8(self, segment));
+ if (dex_path == nullptr) {
return ERR(INTERNAL);
}
- env->CallVoidMethod(classloader, add_dex_path_id, dex_path.get());
- if (env->ExceptionCheck()) {
- {
- art::ScopedObjectAccess soa(self);
- JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
- << self->GetException()->Dump();
- }
- env->ExceptionClear();
+ add_dex_path_id->InvokeVirtual<'V', 'L'>(self, class_loader.Get(), dex_path.Get());
+ if (self->IsExceptionPending()) {
+ JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
+ << self->GetException()->Dump();
+ self->ClearException();
return ERR(ILLEGAL_ARGUMENT);
}
return OK;
@@ -396,10 +392,13 @@
return ERR(INTERNAL);
}
- art::Thread* self = art::Thread::Current();
- JNIEnv* env = self->GetJniEnv();
- if (!env->IsInstanceOf(loader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
- return ERR(INTERNAL);
+ {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::ClassLoader> class_loader =
+ soa.Decode<art::mirror::ClassLoader>(loader);
+ if (!class_loader->InstanceOf(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
+ return ERR(INTERNAL);
+ }
}
return AddToDexClassLoader(jvmti_env, loader, segment);
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 38257f1..c3aab4c 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -82,7 +82,7 @@
#include "thread_list.h"
#include "thread_pool.h"
#include "ti_thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -578,10 +578,11 @@
if (thread_list[i] == nullptr) {
return ERR(INVALID_THREAD);
}
- if (!soa.Env()->IsInstanceOf(thread_list[i], art::WellKnownClasses::java_lang_Thread)) {
+ art::ObjPtr<art::mirror::Object> thread = soa.Decode<art::mirror::Object>(thread_list[i]);
+ if (!thread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) {
return ERR(INVALID_THREAD);
}
- data.handles.push_back(hs.NewHandle(soa.Decode<art::mirror::Object>(thread_list[i])));
+ data.handles.push_back(hs.NewHandle(thread));
}
RunCheckpointAndWait(&data, static_cast<size_t>(max_frame_count));
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index 7fb789d..b5bc35e 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -59,7 +59,7 @@
#include "thread-current-inl.h"
#include "thread_list.h"
#include "ti_phase.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -174,12 +174,7 @@
static void WaitForSystemDaemonStart(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
- {
- art::ScopedThreadStateChange strc(self, art::ThreadState::kNative);
- JNIEnv* jni = self->GetJniEnv();
- jni->CallStaticVoidMethod(art::WellKnownClasses::java_lang_Daemons,
- art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart);
- }
+ art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart->InvokeStatic<'V'>(self);
if (self->IsExceptionPending()) {
LOG(WARNING) << "Exception occurred when waiting for system daemons to start: "
<< self->GetException()->Dump();
@@ -192,8 +187,7 @@
gThreadCallback.started = true;
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
- art::ObjPtr<art::mirror::Class> thread_class =
- soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
+ art::ObjPtr<art::mirror::Class> thread_class = art::WellKnownClasses::java_lang_Thread.Get();
CHECK(thread_class != nullptr);
context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader",
"Ljava/lang/ClassLoader;");
@@ -236,7 +230,9 @@
if (thread == nullptr) {
*thr = art::Thread::Current();
return true;
- } else if (!soa.Env()->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) {
+ }
+ art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread);
+ if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) {
*err = ERR(INVALID_THREAD);
return false;
} else {
@@ -297,7 +293,7 @@
// ThreadGroup.
if (peer != nullptr) {
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group;
CHECK(f != nullptr);
art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
info_ptr->thread_group = group == nullptr
@@ -322,7 +318,7 @@
// Name.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_name);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_name;
CHECK(f != nullptr);
art::ObjPtr<art::mirror::Object> name = f->GetObject(peer);
std::string name_cpp;
@@ -343,21 +339,21 @@
// Priority.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_priority);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_priority;
CHECK(f != nullptr);
info_ptr->priority = static_cast<jint>(f->GetInt(peer));
}
// Daemon.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_daemon);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_daemon;
CHECK(f != nullptr);
info_ptr->is_daemon = f->GetBoolean(peer) == 0 ? JNI_FALSE : JNI_TRUE;
}
// ThreadGroup.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group;
CHECK(f != nullptr);
art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
info_ptr->thread_group = group == nullptr
@@ -617,8 +613,7 @@
// Need to read the Java "started" field to know whether this is starting or terminated.
art::Handle<art::mirror::Object> peer(hs.NewHandle(soa.Decode<art::mirror::Object>(thread)));
- art::ObjPtr<art::mirror::Class> thread_klass =
- soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
+ art::ObjPtr<art::mirror::Class> thread_klass = art::WellKnownClasses::java_lang_Thread.Get();
if (!thread_klass->IsAssignableFrom(peer->GetClass())) {
return ERR(INVALID_THREAD);
}
@@ -815,46 +810,52 @@
if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) {
return ERR(INVALID_PRIORITY);
}
- JNIEnv* env = art::Thread::Current()->GetJniEnv();
- if (thread == nullptr || !env->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) {
+ if (thread == nullptr) {
return ERR(INVALID_THREAD);
}
- if (proc == nullptr) {
- return ERR(NULL_POINTER);
- }
-
+ art::Runtime* runtime = art::Runtime::Current();
+ art::Thread* self = art::Thread::Current();
+ std::unique_ptr<AgentData> data;
{
- art::Runtime* runtime = art::Runtime::Current();
- art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
- if (runtime->IsShuttingDownLocked()) {
- // The runtime is shutting down so we cannot create new threads.
- // TODO It's not fully clear from the spec what we should do here. We aren't yet in
- // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now
- // impossible. Existing agents don't seem to generally do anything with this return value so
- // it doesn't matter too much. We could do something like sending a fake ThreadStart event
- // even though code is never actually run.
- return ERR(INTERNAL);
+ art::ScopedObjectAccess soa(self);
+ art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread);
+ if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) {
+ return ERR(INVALID_THREAD);
}
- runtime->StartThreadBirth();
- }
+ if (proc == nullptr) {
+ return ERR(NULL_POINTER);
+ }
- std::unique_ptr<AgentData> data(new AgentData);
- data->arg = arg;
- data->proc = proc;
- // We need a global ref for Java objects, as local refs will be invalid.
- data->thread = env->NewGlobalRef(thread);
- data->java_vm = art::Runtime::Current()->GetJavaVM();
- data->jvmti_env = jvmti_env;
- data->priority = priority;
- ScopedLocalRef<jstring> s(
- env,
- reinterpret_cast<jstring>(
- env->GetObjectField(thread, art::WellKnownClasses::java_lang_Thread_name)));
- if (s == nullptr) {
- data->name = "JVMTI Agent Thread";
- } else {
- ScopedUtfChars name(env, s.get());
- data->name = name.c_str();
+ {
+ art::MutexLock mu(soa.Self(), *art::Locks::runtime_shutdown_lock_);
+ if (runtime->IsShuttingDownLocked()) {
+ // The runtime is shutting down so we cannot create new threads.
+ // TODO It's not fully clear from the spec what we should do here. We aren't yet in
+ // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now
+ // impossible. Existing agents don't seem to generally do anything with this return value so
+ // it doesn't matter too much. We could do something like sending a fake ThreadStart event
+ // even though code is never actually run.
+ return ERR(INTERNAL);
+ }
+ runtime->StartThreadBirth();
+ }
+
+ data.reset(new AgentData);
+ data->arg = arg;
+ data->proc = proc;
+ // We need a global ref for Java objects, as local refs will be invalid.
+ data->thread = runtime->GetJavaVM()->AddGlobalRef(soa.Self(), othread);
+ data->java_vm = runtime->GetJavaVM();
+ data->jvmti_env = jvmti_env;
+ data->priority = priority;
+ art::ObjPtr<art::mirror::Object> name =
+ art::WellKnownClasses::java_lang_Thread_name->GetObject(
+ soa.Decode<art::mirror::Object>(thread));
+ if (name == nullptr) {
+ data->name = "JVMTI Agent Thread";
+ } else {
+ data->name = name->AsString()->ToModifiedUtf8();
+ }
}
pthread_t pthread;
@@ -864,8 +865,7 @@
reinterpret_cast<void*>(data.get()));
if (pthread_create_result != 0) {
// If the create succeeded the other thread will call EndThreadBirth.
- art::Runtime* runtime = art::Runtime::Current();
- art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
+ art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
return ERR(INTERNAL);
}
diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc
index bc912cf..120024e 100644
--- a/openjdkjvmti/ti_threadgroup.cc
+++ b/openjdkjvmti/ti_threadgroup.cc
@@ -47,7 +47,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -95,22 +95,21 @@
}
art::ScopedObjectAccess soa(art::Thread::Current());
- if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) {
+ art::StackHandleScope<2> hs(soa.Self());
+ art::Handle<art::mirror::Class> tg_class =
+ hs.NewHandle(art::WellKnownClasses::java_lang_ThreadGroup.Get());
+ art::Handle<art::mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<art::mirror::Object>(group));
+ if (!thread_group->InstanceOf(tg_class.Get())) {
return ERR(INVALID_THREAD_GROUP);
}
- art::StackHandleScope<2> hs(soa.Self());
- art::Handle<art::mirror::Class> tg_class(
- hs.NewHandle(soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_ThreadGroup)));
- art::Handle<art::mirror::Object> obj(hs.NewHandle(soa.Decode<art::mirror::Object>(group)));
-
// Do the name first. It's the only thing that can fail.
{
- art::ArtField* name_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name);
+ art::ArtField* name_field = art::WellKnownClasses::java_lang_ThreadGroup_name;
CHECK(name_field != nullptr);
art::ObjPtr<art::mirror::String> name_obj =
- art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj.Get()));
+ art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(thread_group.Get()));
std::string tmp_str;
const char* tmp_cstr;
if (name_obj == nullptr) {
@@ -129,10 +128,9 @@
// Parent.
{
- art::ArtField* parent_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent);
+ art::ArtField* parent_field = art::WellKnownClasses::java_lang_ThreadGroup_parent;
CHECK(parent_field != nullptr);
- art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj.Get());
+ art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(thread_group.Get());
info_ptr->parent = parent_group == nullptr
? nullptr
: soa.AddLocalReference<jthreadGroup>(parent_group);
@@ -142,14 +140,14 @@
{
art::ArtField* prio_field = tg_class->FindDeclaredInstanceField("maxPriority", "I");
CHECK(prio_field != nullptr);
- info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj.Get()));
+ info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(thread_group.Get()));
}
// Daemon.
{
art::ArtField* daemon_field = tg_class->FindDeclaredInstanceField("daemon", "Z");
CHECK(daemon_field != nullptr);
- info_ptr->is_daemon = daemon_field->GetBoolean(obj.Get()) == 0 ? JNI_FALSE : JNI_TRUE;
+ info_ptr->is_daemon = daemon_field->GetBoolean(thread_group.Get()) == 0 ? JNI_FALSE : JNI_TRUE;
}
return ERR(NONE);
@@ -161,8 +159,7 @@
REQUIRES_SHARED(art::Locks::mutator_lock_) {
CHECK(desired_thread_group != nullptr);
- art::ArtField* thread_group_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+ art::ArtField* thread_group_field = art::WellKnownClasses::java_lang_Thread_group;
DCHECK(thread_group_field != nullptr);
art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer);
return (group == desired_thread_group.Get());
@@ -194,8 +191,7 @@
CHECK(thread_group != nullptr);
// Get the ThreadGroup[] "groups" out of this thread group...
- art::ArtField* groups_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups);
+ art::ArtField* groups_field = art::WellKnownClasses::java_lang_ThreadGroup_groups;
art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get());
if (groups_array == nullptr) {
@@ -225,14 +221,13 @@
}
art::ScopedObjectAccess soa(art::Thread::Current());
-
- if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) {
+ art::StackHandleScope<1> hs(soa.Self());
+ art::Handle<art::mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<art::mirror::Object>(group));
+ if (!thread_group->InstanceOf(art::WellKnownClasses::java_lang_ThreadGroup.Get())) {
return ERR(INVALID_THREAD_GROUP);
}
- art::StackHandleScope<1> hs(soa.Self());
- art::Handle<art::mirror::Object> thread_group = hs.NewHandle(
- soa.Decode<art::mirror::Object>(group));
art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group);
diff --git a/perfetto_hprof/Android.bp b/perfetto_hprof/Android.bp
index a81a4fa..f5ae042 100644
--- a/perfetto_hprof/Android.bp
+++ b/perfetto_hprof/Android.bp
@@ -50,6 +50,7 @@
compile_multilib: "both",
shared_libs: [
+ "libartpalette",
"libbase",
"liblog",
],
@@ -68,6 +69,12 @@
header_libs: [
"libnativehelper_header_only",
],
+ // FIXME: Workaround LTO build breakage
+ // http://b/241700157
+ lto: {
+ never: true,
+ },
+
}
art_cc_library {
diff --git a/perfetto_hprof/perfetto_hprof.cc b/perfetto_hprof/perfetto_hprof.cc
index 669fb0c..7731aaa 100644
--- a/perfetto_hprof/perfetto_hprof.cc
+++ b/perfetto_hprof/perfetto_hprof.cc
@@ -18,9 +18,8 @@
#include "perfetto_hprof.h"
-#include <android-base/logging.h>
-#include <base/fast_exit.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <inttypes.h>
#include <sched.h>
#include <signal.h>
@@ -36,6 +35,11 @@
#include <optional>
#include <type_traits>
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/properties.h"
+#include "base/fast_exit.h"
+#include "base/systrace.h"
#include "gc/heap-visit-objects-inl.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
@@ -86,6 +90,7 @@
static int requested_tracing_session_id = 0;
static State g_state = State::kUninitialized;
+static bool g_oome_triggered = false;
// Pipe to signal from the signal handler into a worker thread that handles the
// dump requests.
@@ -151,19 +156,33 @@
return false;
}
+uint64_t GetCurrentBootClockNs() {
+ struct timespec ts = {};
+ if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) {
+ LOG(FATAL) << "Failed to get boottime.";
+ }
+ return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+}
+
class JavaHprofDataSource : public perfetto::DataSource<JavaHprofDataSource> {
public:
constexpr static perfetto::BufferExhaustedPolicy kBufferExhaustedPolicy =
perfetto::BufferExhaustedPolicy::kStall;
+
+ explicit JavaHprofDataSource(bool dispatched_from_heapprofd)
+ : dispatched_from_heapprofd_(dispatched_from_heapprofd) {}
+
void OnSetup(const SetupArgs& args) override {
- uint64_t normalized_cfg_tracing_session_id =
- args.config->tracing_session_id() % std::numeric_limits<int32_t>::max();
- if (requested_tracing_session_id < 0) {
- LOG(ERROR) << "invalid requested tracing session id " << requested_tracing_session_id;
- return;
- }
- if (static_cast<uint64_t>(requested_tracing_session_id) != normalized_cfg_tracing_session_id) {
- return;
+ if (dispatched_from_heapprofd_) {
+ uint64_t normalized_tracing_session_id =
+ args.config->tracing_session_id() % std::numeric_limits<int32_t>::max();
+ if (requested_tracing_session_id < 0) {
+ LOG(ERROR) << "invalid requested tracing session id " << requested_tracing_session_id;
+ return;
+ }
+ if (static_cast<uint64_t>(requested_tracing_session_id) != normalized_tracing_session_id) {
+ return;
+ }
}
// This is on the heap as it triggers -Wframe-larger-than.
@@ -178,7 +197,7 @@
}
// This tracing session ID matches the requesting tracing session ID, so we know heapprofd
// has verified it targets this process.
- enabled_ = true;
+ enabled_ = dispatched_from_heapprofd_ || IsDumpEnabled(*cfg.get());
}
bool dump_smaps() { return dump_smaps_; }
@@ -232,10 +251,26 @@
}
private:
+ static bool IsDumpEnabled(const perfetto::protos::pbzero::JavaHprofConfig::Decoder& cfg) {
+ std::string cmdline;
+ if (!android::base::ReadFileToString("/proc/self/cmdline", &cmdline)) {
+ return false;
+ }
+ const char* argv0 = cmdline.c_str();
+
+ for (auto it = cfg.process_cmdline(); it; ++it) {
+ std::string pattern = (*it).ToStdString();
+ if (fnmatch(pattern.c_str(), argv0, FNM_NOESCAPE) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool dispatched_from_heapprofd_ = false;
bool enabled_ = false;
bool dump_smaps_ = false;
std::vector<std::string> ignored_types_;
- static art::Thread* self_;
art::Mutex finish_mutex_{"perfetto_hprof_ds_mutex", art::LockLevel::kGenericBottomLock};
bool is_finished_ = false;
@@ -243,27 +278,40 @@
std::function<void()> async_stop_;
};
-art::Thread* JavaHprofDataSource::self_ = nullptr;
-
-
-void WaitForDataSource(art::Thread* self) {
+void SetupDataSource(const std::string& ds_name, bool dispatched_from_heapprofd) {
perfetto::TracingInitArgs args;
args.backends = perfetto::BackendType::kSystemBackend;
perfetto::Tracing::Initialize(args);
perfetto::DataSourceDescriptor dsd;
- dsd.set_name("android.java_hprof");
+ dsd.set_name(ds_name);
dsd.set_will_notify_on_stop(true);
- JavaHprofDataSource::Register(dsd);
+ JavaHprofDataSource::Register(dsd, dispatched_from_heapprofd);
+ LOG(INFO) << "registered data source " << ds_name;
+}
- LOG(INFO) << "waiting for data source";
-
+// Waits for the data source OnStart
+void WaitForDataSource(art::Thread* self) {
art::MutexLock lk(self, GetStateMutex());
while (g_state != State::kStart) {
GetStateCV().Wait(self);
}
}
+// Waits for the data source OnStart with a timeout. Returns false on timeout.
+bool TimedWaitForDataSource(art::Thread* self, int64_t timeout_ms) {
+ const uint64_t cutoff_ns = GetCurrentBootClockNs() + timeout_ms * 1000000;
+ art::MutexLock lk(self, GetStateMutex());
+ while (g_state != State::kStart) {
+ const uint64_t current_ns = GetCurrentBootClockNs();
+ if (current_ns >= cutoff_ns) {
+ return false;
+ }
+ GetStateCV().TimedWait(self, (cutoff_ns - current_ns) / 1000000, 0);
+ }
+ return true;
+}
+
// Helper class to write Java heap dumps to `ctx`. The whole heap dump can be
// split into more perfetto.protos.HeapGraph messages, to avoid making each
// message too big.
@@ -831,10 +879,45 @@
uint64_t prev_object_id_ = 0;
};
-void DumpPerfetto(art::Thread* self) {
+// waitpid with a timeout implemented by ~busy-waiting
+// See b/181031512 for rationale.
+void BusyWaitpid(pid_t pid, uint32_t timeout_ms) {
+ for (size_t i = 0;; ++i) {
+ if (i == timeout_ms) {
+ // The child hasn't exited.
+ // Give up and SIGKILL it. The next waitpid should succeed.
+ LOG(ERROR) << "perfetto_hprof child timed out. Sending SIGKILL.";
+ kill(pid, SIGKILL);
+ }
+ int stat_loc;
+ pid_t wait_result = waitpid(pid, &stat_loc, WNOHANG);
+ if (wait_result == -1 && errno != EINTR) {
+ if (errno != ECHILD) {
+ // This hopefully never happens (should only be EINVAL).
+ PLOG(FATAL_WITHOUT_ABORT) << "waitpid";
+ }
+ // If we get ECHILD, the parent process was handling SIGCHLD, or did a wildcard wait.
+ // The child is no longer here either way, so that's good enough for us.
+ break;
+ } else if (wait_result > 0) {
+ break;
+ } else { // wait_result == 0 || errno == EINTR.
+ usleep(1000);
+ }
+ }
+}
+
+enum class ResumeParentPolicy {
+ IMMEDIATELY,
+ DEFERRED
+};
+
+void ForkAndRun(art::Thread* self,
+ ResumeParentPolicy resume_parent_policy,
+ const std::function<void(pid_t child)>& parent_runnable,
+ const std::function<void(pid_t parent, uint64_t timestamp)>& child_runnable) {
pid_t parent_pid = getpid();
LOG(INFO) << "preparing to dump heap for " << parent_pid;
-
// Need to take a heap dump while GC isn't running. See the comment in
// Heap::VisitObjects(). Also we need the critical section to avoid visiting
// the same object twice. See b/34967844.
@@ -859,41 +942,20 @@
}
if (pid != 0) {
// Parent
- // Stop the thread suspension as soon as possible to allow the rest of the application to
- // continue while we waitpid here.
- ssa.reset();
- gcs.reset();
- for (size_t i = 0;; ++i) {
- if (i == 1000) {
- // The child hasn't exited for 1 second (and all it was supposed to do was fork itself).
- // Give up and SIGKILL it. The next waitpid should succeed.
- LOG(ERROR) << "perfetto_hprof child timed out. Sending SIGKILL.";
- kill(pid, SIGKILL);
- }
- // Busy waiting here will introduce some extra latency, but that is okay because we have
- // already unsuspended all other threads. This runs on the perfetto_hprof_listener, which
- // is not needed for progress of the app itself.
- int stat_loc;
- pid_t wait_result = waitpid(pid, &stat_loc, WNOHANG);
- if (wait_result == -1 && errno != EINTR) {
- if (errno != ECHILD) {
- // This hopefully never happens (should only be EINVAL).
- PLOG(FATAL_WITHOUT_ABORT) << "waitpid";
- }
- // If we get ECHILD, the parent process was handling SIGCHLD, or did a wildcard wait.
- // The child is no longer here either way, so that's good enough for us.
- break;
- } else if (wait_result > 0) {
- break;
- } else { // wait_result == 0 || errno == EINTR.
- usleep(1000);
- }
+ if (resume_parent_policy == ResumeParentPolicy::IMMEDIATELY) {
+ // Stop the thread suspension as soon as possible to allow the rest of the application to
+ // continue while we waitpid here.
+ ssa.reset();
+ gcs.reset();
+ }
+ parent_runnable(pid);
+ if (resume_parent_policy != ResumeParentPolicy::IMMEDIATELY) {
+ ssa.reset();
+ gcs.reset();
}
return;
}
-
// The following code is only executed by the child of the original process.
-
// Uninstall signal handler, so we don't trigger a profile on it.
if (sigaction(kJavaHeapprofdSignal, &g_orig_act, nullptr) != 0) {
close(g_signal_pipe_fds[0]);
@@ -902,25 +964,14 @@
return;
}
- // Daemon creates a new process that is the grand-child of the original process, and exits.
- if (daemon(0, 0) == -1) {
- PLOG(FATAL) << "daemon";
- }
+ uint64_t ts = GetCurrentBootClockNs();
+ child_runnable(parent_pid, ts);
+ // Prevent the `atexit` handlers from running. We do not want to call cleanup
+ // functions the parent process has registered.
+ art::FastExit(0);
+}
- // The following code is only executed by the grand-child of the original process.
-
- // Make sure that this is the first thing we do after forking, so if anything
- // below hangs, the fork will go away from the watchdog.
- ArmWatchdogOrDie();
-
- struct timespec ts = {};
- if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) {
- LOG(FATAL) << "Failed to get boottime.";
- }
- uint64_t timestamp = ts.tv_sec * 1000000000LL + ts.tv_nsec;
-
- WaitForDataSource(self);
-
+void WriteHeapPackets(pid_t parent_pid, uint64_t timestamp) {
JavaHprofDataSource::Trace(
[parent_pid, timestamp](JavaHprofDataSource::TraceContext ctx)
NO_THREAD_SAFETY_ANALYSIS {
@@ -968,11 +1019,98 @@
}
}
});
+}
- LOG(INFO) << "finished dumping heap for " << parent_pid;
- // Prevent the `atexit` handlers from running. We do not want to call cleanup
- // functions the parent process has registered.
- art::FastExit(0);
+void DumpPerfetto(art::Thread* self) {
+ ForkAndRun(
+ self,
+ ResumeParentPolicy::IMMEDIATELY,
+ // parent thread
+ [](pid_t child) {
+ // Busy waiting here will introduce some extra latency, but that is okay because we have
+ // already unsuspended all other threads. This runs on the perfetto_hprof_listener, which
+ // is not needed for progress of the app itself.
+ // We daemonize the child process, so effectively we only need to wait
+ // for it to fork and exit.
+ BusyWaitpid(child, 1000);
+ },
+ // child thread
+ [self](pid_t dumped_pid, uint64_t timestamp) {
+ // Daemon creates a new process that is the grand-child of the original process, and exits.
+ if (daemon(0, 0) == -1) {
+ PLOG(FATAL) << "daemon";
+ }
+ // The following code is only executed by the grand-child of the original process.
+
+ // Make sure that this is the first thing we do after forking, so if anything
+ // below hangs, the fork will go away from the watchdog.
+ ArmWatchdogOrDie();
+ SetupDataSource("android.java_hprof", true);
+ WaitForDataSource(self);
+ WriteHeapPackets(dumped_pid, timestamp);
+ LOG(INFO) << "finished dumping heap for " << dumped_pid;
+ });
+}
+
+void DumpPerfettoOutOfMemory() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::Thread* self = art::Thread::Current();
+ if (!self) {
+ LOG(FATAL_WITHOUT_ABORT) << "no thread in DumpPerfettoOutOfMemory";
+ return;
+ }
+ {
+ // OutOfMemoryErrors are reentrant, make sure we do not fork and process
+ // more than once.
+ art::MutexLock lk(self, GetStateMutex());
+ if (g_oome_triggered) {
+ return;
+ }
+ g_oome_triggered = true;
+ }
+
+ art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended);
+ // If we fork & resume the original process execution it will most likely exit
+ // ~immediately due to the OOME error thrown. When the system detects that
+ // that, it will cleanup by killing all processes in the cgroup (including
+ // the process we just forked).
+ // We need to avoid the race between the heap dump and the process group
+ // cleanup, and the only way to do this is to avoid resuming the original
+ // process until the heap dump is complete.
+ // Given we are already about to crash anyway, the diagnostic data we get
+ // outweighs the cost of introducing some latency.
+ ForkAndRun(
+ self,
+ ResumeParentPolicy::DEFERRED,
+ // parent process
+ [](pid_t child) {
+ // waitpid to reap the zombie
+ // we are explicitly waiting for the child to exit
+ // The reason for the timeout on top of the watchdog is that it is
+ // possible (albeit unlikely) that even the watchdog will fail to be
+ // activated in the case of an atfork handler.
+ BusyWaitpid(child, kWatchdogTimeoutSec * 1000);
+ },
+ // child process
+ [self](pid_t dumped_pid, uint64_t timestamp) {
+ ArmWatchdogOrDie();
+ art::ScopedTrace trace("perfetto_hprof oome");
+ SetupDataSource("android.java_hprof.oom", false);
+ perfetto::Tracing::ActivateTriggers({"com.android.telemetry.art-outofmemory"}, 500);
+
+ // A pre-armed tracing session might not exist, so we should wait for a
+ // limited amount of time before we decide to let the execution continue.
+ if (!TimedWaitForDataSource(self, 500)) {
+ LOG(INFO) << "timeout waiting for data source start (no active session?)";
+ return;
+ }
+ WriteHeapPackets(dumped_pid, timestamp);
+ LOG(INFO) << "finished dumping heap for OOME " << dumped_pid;
+ });
+}
+
+bool CanProfile() {
+ std::string build_type = android::base::GetProperty("ro.build.type", "");
+ return !build_type.empty() && build_type != "user";
}
// The plugin initialization function.
@@ -1062,10 +1200,19 @@
});
th.detach();
+ // Register the OOM error handler.
+ if (CanProfile()) {
+ art::Runtime::Current()->SetOutOfMemoryErrorHook(perfetto_hprof::DumpPerfettoOutOfMemory);
+ }
+
return true;
}
extern "C" bool ArtPlugin_Deinitialize() {
+ if (CanProfile()) {
+ art::Runtime::Current()->SetOutOfMemoryErrorHook(nullptr);
+ }
+
if (sigaction(kJavaHeapprofdSignal, &g_orig_act, nullptr) != 0) {
PLOG(ERROR) << "failed to reset signal handler";
// We cannot close the pipe if the signal handler wasn't unregistered,
diff --git a/profman/Android.bp b/profman/Android.bp
index b231499..8e76bc2 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -32,6 +32,7 @@
"profman.cc",
"profile_assistant.cc",
],
+ header_libs: ["profman_headers"],
target: {
android: {
@@ -160,11 +161,31 @@
},
}
+cc_library_headers {
+ name: "profman_headers",
+ defaults: ["art_defaults"],
+ export_include_dirs: ["include"],
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
+
art_cc_defaults {
name: "art_profman_tests_defaults",
data: [
":art-gtest-jars-ProfileTestMultiDex",
],
+ header_libs: ["profman_headers"],
tidy_timeout_srcs: ["profile_assistant_test.cc"],
srcs: ["profile_assistant_test.cc"],
}
diff --git a/profman/include/profman/profman_result.h b/profman/include/profman/profman_result.h
new file mode 100644
index 0000000..9c9aca9
--- /dev/null
+++ b/profman/include/profman/profman_result.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
+#define ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
+
+namespace art {
+
+class ProfmanResult {
+ public:
+ static constexpr int kErrorUsage = 100;
+
+ // The return codes of processing profiles (running profman in normal mode).
+ //
+ // On a successful run:
+ // - If `--force-merge` is specified, the return code can only be `kSuccess`.
+ // - If no `--profile-file(-fd)` is specified, the return code can only be
+ // `kSkipCompilationSmallDelta` or `kSkipCompilationEmptyProfiles`.
+ // - Otherwise, the return code can only be `kCompile`, `kSkipCompilationSmallDelta`, or
+ // `kSkipCompilationEmptyProfiles`.
+ //
+ // Note that installd consumes the returns codes with its own copy of these values
+ // (frameworks/native/cmds/installd/dexopt.cpp).
+ enum ProcessingResult {
+ // The success code for `--force-merge`.
+ // This is also the generic success code for non-analysis runs.
+ kSuccess = 0,
+ // A merge has been performed, meaning the reference profile has been changed.
+ kCompile = 1,
+ // `--profile-file(-fd)` is not specified, or the specified profiles are outdated (i.e., APK
+ // filename or checksum mismatch), empty, or don't contain enough number of new classes and
+ // methods that meets the threshold to trigger a merge.
+ kSkipCompilationSmallDelta = 2,
+ // All the input profiles (including the reference profile) are either outdated (i.e., APK
+ // filename or checksum mismatch) or empty.
+ kSkipCompilationEmptyProfiles = 7,
+ // Errors.
+ kErrorBadProfiles = 3,
+ kErrorIO = 4,
+ kErrorCannotLock = 5,
+ kErrorDifferentVersions = 6,
+ };
+
+ // The return codes of running profman with `--copy-and-update-profile-key`.
+ enum CopyAndUpdateResult {
+ kCopyAndUpdateSuccess = 0,
+ kCopyAndUpdateNoMatch = 21,
+ kCopyAndUpdateErrorFailedToUpdateProfile = 22,
+ kCopyAndUpdateErrorFailedToSaveProfile = 23,
+ kCopyAndUpdateErrorFailedToLoadProfile = 24,
+ };
+};
+
+} // namespace art
+
+#endif // ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index d098738..abbde2d 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -18,6 +18,7 @@
#include "base/os.h"
#include "base/unix_file/fd_file.h"
+#include "profman/profman_result.h"
namespace art {
@@ -26,25 +27,22 @@
static constexpr const uint32_t kMinNewMethodsForCompilation = 100;
static constexpr const uint32_t kMinNewClassesForCompilation = 50;
-
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
- const std::vector<ScopedFlock>& profile_files,
- const ScopedFlock& reference_profile_file,
- const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
- const Options& options) {
- DCHECK(!profile_files.empty());
-
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
+ const std::vector<ScopedFlock>& profile_files,
+ const ScopedFlock& reference_profile_file,
+ const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
+ const Options& options) {
ProfileCompilationInfo info(options.IsBootImageMerge());
// Load the reference profile.
if (!info.Load(reference_profile_file->Fd(), /*merge_classes=*/ true, filter_fn)) {
LOG(WARNING) << "Could not load reference profile file";
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
if (options.IsBootImageMerge() && !info.IsForBootImage()) {
LOG(WARNING) << "Requested merge for boot image profile but the reference profile is regular.";
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
// Store the current state of the reference profile before merging with the current profiles.
@@ -65,21 +63,21 @@
// TODO: Do we really need to use a different error code for version mismatch?
ProfileCompilationInfo wrong_info(!options.IsBootImageMerge());
if (wrong_info.Load(profile_files[i]->Fd(), /*merge_classes=*/ true, filter_fn)) {
- return kErrorDifferentVersions;
+ return ProfmanResult::kErrorDifferentVersions;
}
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
if (!info.MergeWith(cur_info)) {
LOG(WARNING) << "Could not merge profile file at index " << i;
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
}
// If we perform a forced merge do not analyze the difference between profiles.
if (!options.IsForceMerge()) {
if (info.IsEmpty()) {
- return kSkipCompilationEmptyProfiles;
+ return ProfmanResult::kSkipCompilationEmptyProfiles;
}
uint32_t min_change_in_methods_for_compilation = std::max(
(options.GetMinNewMethodsPercentChangeForCompilation() * number_of_methods) / 100,
@@ -91,21 +89,21 @@
if (((info.GetNumberOfMethods() - number_of_methods) < min_change_in_methods_for_compilation) &&
((info.GetNumberOfResolvedClasses() - number_of_classes)
< min_change_in_classes_for_compilation)) {
- return kSkipCompilationSmallDelta;
+ return ProfmanResult::kSkipCompilationSmallDelta;
}
}
// We were successful in merging all profile information. Update the reference profile.
if (!reference_profile_file->ClearContent()) {
PLOG(WARNING) << "Could not clear reference profile file";
- return kErrorIO;
+ return ProfmanResult::kErrorIO;
}
if (!info.Save(reference_profile_file->Fd())) {
LOG(WARNING) << "Could not save reference profile file";
- return kErrorIO;
+ return ProfmanResult::kErrorIO;
}
- return options.IsForceMerge() ? kSuccess : kCompile;
+ return options.IsForceMerge() ? ProfmanResult::kSuccess : ProfmanResult::kCompile;
}
class ScopedFlockList {
@@ -144,7 +142,7 @@
std::vector<ScopedFlock> flocks_;
};
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfiles(
const std::vector<int>& profile_files_fd,
int reference_profile_file_fd,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
@@ -155,7 +153,7 @@
ScopedFlockList profile_files(profile_files_fd.size());
if (!profile_files.Init(profile_files_fd, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
// The reference_profile_file is opened in read/write mode because it's
@@ -166,7 +164,7 @@
&error);
if (reference_profile_file.get() == nullptr) {
LOG(WARNING) << "Could not lock reference profiled files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files.Get(),
@@ -175,7 +173,7 @@
options);
}
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfiles(
const std::vector<std::string>& profile_files,
const std::string& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
@@ -185,14 +183,14 @@
ScopedFlockList profile_files_list(profile_files.size());
if (!profile_files_list.Init(profile_files, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
ScopedFlock locked_reference_profile_file = LockedFile::Open(
reference_profile_file.c_str(), O_RDWR, /* block= */ true, &error);
if (locked_reference_profile_file.get() == nullptr) {
LOG(WARNING) << "Could not lock reference profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files_list.Get(),
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
index 0ef4f88..6b7a7a6 100644
--- a/profman/profile_assistant.h
+++ b/profman/profile_assistant.h
@@ -22,24 +22,12 @@
#include "base/scoped_flock.h"
#include "profile/profile_compilation_info.h"
+#include "profman/profman_result.h"
namespace art {
class ProfileAssistant {
public:
- // These also serve as return codes of profman and are processed by installd
- // (frameworks/native/cmds/installd/commands.cpp)
- enum ProcessingResult {
- kSuccess = 0, // Generic success code for non-analysis runs.
- kCompile = 1,
- kSkipCompilationSmallDelta = 2,
- kErrorBadProfiles = 3,
- kErrorIO = 4,
- kErrorCannotLock = 5,
- kErrorDifferentVersions = 6,
- kSkipCompilationEmptyProfiles = 7,
- };
-
class Options {
public:
static constexpr bool kForceMergeDefault = false;
@@ -101,14 +89,14 @@
// this case no file will be updated. A variation of this code is
// kSkipCompilationEmptyProfiles which indicates that all the profiles are empty.
// This allow the caller to make fine grain decisions on the compilation strategy.
- static ProcessingResult ProcessProfiles(
+ static ProfmanResult::ProcessingResult ProcessProfiles(
const std::vector<std::string>& profile_files,
const std::string& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn
= ProfileCompilationInfo::ProfileFilterFnAcceptAll,
const Options& options = Options());
- static ProcessingResult ProcessProfiles(
+ static ProfmanResult::ProcessingResult ProcessProfiles(
const std::vector<int>& profile_files_fd_,
int reference_profile_file_fd,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn
@@ -116,7 +104,7 @@
const Options& options = Options());
private:
- static ProcessingResult ProcessProfilesInternal(
+ static ProfmanResult::ProcessingResult ProcessProfilesInternal(
const std::vector<ScopedFlock>& profile_files,
const ScopedFlock& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index f446c09..f7c4255 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#include "profile_assistant.h"
+
#include <sstream>
#include <string>
@@ -31,12 +32,13 @@
#include "dex/dex_instruction_iterator.h"
#include "dex/type_reference.h"
#include "exec_utils.h"
+#include "gtest/gtest.h"
#include "linear_alloc.h"
#include "mirror/class-inl.h"
#include "obj_ptr-inl.h"
#include "profile/profile_compilation_info.h"
#include "profile/profile_test_helper.h"
-#include "profile_assistant.h"
+#include "profman/profman_result.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
@@ -50,13 +52,13 @@
void PostRuntimeCreate() override {
allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
- dex1 = BuildDex("location1", /*checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 10001);
- dex2 = BuildDex("location2", /*checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 10002);
- dex3 = BuildDex("location3", /*checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 10003);
- dex4 = BuildDex("location4", /*checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 10004);
+ dex1 = BuildDex("location1", /*location_checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 10001);
+ dex2 = BuildDex("location2", /*location_checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 10002);
+ dex3 = BuildDex("location3", /*location_checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 10003);
+ dex4 = BuildDex("location4", /*location_checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 10004);
dex1_checksum_missmatch =
- BuildDex("location1", /*checksum=*/ 12, "LUnique1;", /*num_method_ids=*/ 10001);
+ BuildDex("location1", /*location_checksum=*/ 12, "LUnique1;", /*num_method_ids=*/ 10001);
}
protected:
@@ -267,7 +269,7 @@
bool CreateAndDump(const std::string& input_file_contents,
std::string* output_file_contents,
- std::optional<const std::string> target = std::nullopt) {
+ const std::optional<const std::string>& target = std::nullopt) {
ScratchFile profile_file;
EXPECT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
@@ -442,10 +444,16 @@
const std::vector<const std::string>& extra_args =
std::vector<const std::string>()) {
uint16_t max_classes = std::max(classes_in_cur_profile, classes_in_ref_profile);
- const DexFile* dex1_x = BuildDex(
- "location1_x", /*checksum=*/ 0x101, "LUnique1_x;", /*num_method_ids=*/ 0, max_classes);
- const DexFile* dex2_x = BuildDex(
- "location2_x", /*checksum=*/ 0x102, "LUnique2_x;", /*num_method_ids=*/ 0, max_classes);
+ const DexFile* dex1_x = BuildDex("location1_x",
+ /*location_checksum=*/ 0x101,
+ "LUnique1_x;",
+ /*num_method_ids=*/ 0,
+ max_classes);
+ const DexFile* dex2_x = BuildDex("location2_x",
+ /*location_checksum=*/ 0x102,
+ "LUnique2_x;",
+ /*num_method_ids=*/ 0,
+ max_classes);
ScratchFile profile;
ScratchFile reference_profile;
@@ -486,8 +494,7 @@
SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -506,15 +513,15 @@
TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
const uint16_t kNumberOfClassesToEnableCompilation = 100;
const DexFile* dex1_100 = BuildDex("location1_100",
- /*checksum=*/ 101,
+ /*location_checksum=*/ 101,
"LUnique1_100;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 100);
+ /*num_class_ids=*/ 100);
const DexFile* dex2_100 = BuildDex("location2_100",
- /*checksum=*/ 102,
+ /*location_checksum=*/ 102,
"LUnique2_100;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 100);
+ /*num_class_ids=*/ 100);
ScratchFile profile1;
ScratchFile reference_profile;
@@ -527,8 +534,7 @@
SetupProfile(dex1_100, dex2_100, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -566,8 +572,7 @@
&reference_info, kNumberOfMethodsToEnableCompilation / 2);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs
ProfileCompilationInfo result;
@@ -600,7 +605,7 @@
SetupProfile(dex3, dex4, /*number_of_methods=*/ 0, /*number_of_classes*/ 0, profile2, &info2);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationEmptyProfiles,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationEmptyProfiles,
ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
@@ -637,7 +642,7 @@
SetupProfile(dex3, dex4, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
@@ -663,10 +668,9 @@
std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
- CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
- kNumberOfMethodsInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
+ CheckCompilationMethodPercentChange(
+ kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
@@ -675,10 +679,9 @@
std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
- kNumberOfMethodsInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kCompile,
+ CheckCompilationMethodPercentChange(
+ kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentageWithNewMin) {
@@ -686,7 +689,7 @@
const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 20%.
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
kNumberOfMethodsInRefProfile));
}
@@ -697,10 +700,9 @@
std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
- CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
- kNumberOfClassesInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
+ CheckCompilationClassPercentChange(
+ kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
@@ -709,10 +711,9 @@
std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
- kNumberOfClassesInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kCompile,
+ CheckCompilationClassPercentChange(
+ kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentageWithNewMin) {
@@ -720,7 +721,7 @@
const uint16_t kNumberOfClassesInCurProfile = 6200; // Threshold is 20%.
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
kNumberOfClassesInRefProfile));
}
@@ -744,8 +745,7 @@
dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should fail processing.
- ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
CheckProfileInfo(profile1, info1);
@@ -776,8 +776,7 @@
&reference_info);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
CheckProfileInfo(profile1, info1);
@@ -971,7 +970,7 @@
/*for_boot_image=*/ true));
ProfileCompilationInfo bootProfile(/*for_boot_image=*/ true);
- bootProfile.Load(profile.GetFilename(), /*clear_if_invalid=*/ true);
+ EXPECT_TRUE(bootProfile.Load(profile.GetFilename(), /*clear_if_invalid=*/ false));
// Generate the boot profile.
ScratchFile out_profile;
@@ -1065,10 +1064,10 @@
core_dex,
/*for_boot_image=*/ true));
- ProfileCompilationInfo boot_profile1;
- ProfileCompilationInfo boot_profile2;
- boot_profile1.Load(profile1.GetFilename(), /*for_boot_image=*/ true);
- boot_profile2.Load(profile2.GetFilename(), /*for_boot_image=*/ true);
+ ProfileCompilationInfo boot_profile1(/*for_boot_image=*/ true);
+ ProfileCompilationInfo boot_profile2(/*for_boot_image=*/ true);
+ EXPECT_TRUE(boot_profile1.Load(profile1.GetFilename(), /*clear_if_invalid=*/ false));
+ EXPECT_TRUE(boot_profile2.Load(profile2.GetFilename(), /*clear_if_invalid=*/ false));
// Generate the boot profile.
ScratchFile out_profile;
@@ -1525,8 +1524,7 @@
&reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order=*/true);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
@@ -1787,7 +1785,7 @@
argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
std::string error;
- EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kCompile) << error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCompile) << error;
// Verify that we can load the result.
@@ -1820,6 +1818,169 @@
ASSERT_TRUE(expected.Equals(result));
}
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfile) {
+ ScratchFile reference_profile;
+
+ // Use a real dex file to generate profile test data.
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+ const DexFile& d1 = *dex_files[0];
+ const DexFile& d2 = *dex_files[0];
+
+ // The reference profile info will contain the methods with indices 0-100.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(&d1,
+ &d2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationSmallDelta.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfilePassByFilename) {
+ ScratchFile reference_profile;
+
+ // Use a real dex file to generate profile test data.
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+ const DexFile& d1 = *dex_files[0];
+ const DexFile& d2 = *dex_files[0];
+
+ // The reference profile info will contain the methods with indices 0-100.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(&d1,
+ &d2,
+ /*number_of_methods=*/100,
+ /*number_of_classes=*/0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file=" + reference_profile.GetFilename());
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationSmallDelta.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfile) {
+ ScratchFile reference_profile;
+
+ // The reference profile info will only contain the header.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(/*dex_file1=*/ nullptr,
+ /*dex_file2=*/ nullptr,
+ /*number_of_methods=*/ 0,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationEmptyProfiles.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfileAfterFiltering) {
+ ScratchFile reference_profile;
+
+ // Use fake dex files to generate profile test data.
+ // All the methods will be filtered out during the profman invocation.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(dex1,
+ dex2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the real dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationEmptyProfiles.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKey) {
ScratchFile profile1;
ScratchFile reference_profile;
@@ -1846,7 +2007,8 @@
// Run profman and pass the dex file with --apk-fd.
android::base::unique_fd apk_fd(
- open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
ASSERT_GE(apk_fd.get(), 0);
std::string profman_cmd = GetProfmanCmd();
@@ -1858,7 +2020,8 @@
argv_str.push_back("--copy-and-update-profile-key");
std::string error;
- ASSERT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
+ // Must return kCopyAndUpdateSuccess.
+ ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateSuccess) << error;
// Verify that we can load the result.
ProfileCompilationInfo result;
@@ -1874,6 +2037,46 @@
}
}
+TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKeyNoUpdate) {
+ ScratchFile profile1;
+ ScratchFile reference_profile;
+
+ // Use fake dex files to generate profile test data.
+ ProfileCompilationInfo info1;
+ SetupProfile(dex1,
+ dex2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ profile1,
+ &info1);
+
+ std::string input_content;
+ ASSERT_TRUE(android::base::ReadFileToString(profile1.GetFilename(), &input_content));
+
+ // Run profman and pass the real dex file with --apk-fd. It won't match any entry in the profile.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+ argv_str.push_back("--copy-and-update-profile-key");
+ std::string error;
+
+ // Must return kCopyAndUpdateNoMatch.
+ ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateNoMatch) << error;
+
+ // Verify that the content is the same.
+ std::string output_content;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &output_content));
+ EXPECT_EQ(input_content, output_content);
+}
+
TEST_F(ProfileAssistantTest, BootImageMerge) {
ScratchFile profile;
ScratchFile reference_profile;
@@ -1900,7 +2103,7 @@
int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
- ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
+ ASSERT_EQ(return_code, ProfmanResult::kSuccess);
// Verify the result: it should be equal to info2 since info1 is a regular profile
// and should be ignored.
@@ -1918,15 +2121,15 @@
const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
const DexFile* dex1_7000 = BuildDex("location1_7000",
- /*checksum=*/ 7001,
+ /*location_checksum=*/ 7001,
"LUnique1_7000;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 7000);
+ /*num_class_ids=*/ 7000);
const DexFile* dex2_7000 = BuildDex("location2_7000",
- /*checksum=*/ 7002,
+ /*location_checksum=*/ 7002,
"LUnique2_7000;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 7000);
+ /*num_class_ids=*/ 7000);
ScratchFile profile;
ScratchFile reference_profile;
@@ -1942,7 +2145,7 @@
std::vector<const std::string> extra_args({"--force-merge"});
int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
- ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
+ ASSERT_EQ(return_code, ProfmanResult::kSuccess);
// Check that the result is the aggregation.
ProfileCompilationInfo result;
@@ -1974,7 +2177,8 @@
// Run profman and pass the dex file with --apk-fd.
android::base::unique_fd apk_fd(
- open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
ASSERT_GE(apk_fd.get(), 0);
std::string profman_cmd = GetProfmanCmd();
@@ -1987,7 +2191,7 @@
argv_str.push_back("--boot-image-merge");
std::string error;
- EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kSuccess) << error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSuccess) << error;
// Verify that we can load the result and that it equals to what we saved.
ProfileCompilationInfo result(/*for_boot_image=*/ true);
@@ -2009,17 +2213,16 @@
int reference_profile_fd = GetFd(profile2);
std::vector<const std::string> boot_image_args({"--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
- ProfileAssistant::kErrorDifferentVersions);
- ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorDifferentVersions);
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd), ProfmanResult::kErrorBadProfiles);
// Reverse the order of the profiles to verify we get the same behaviour.
profile_fds[0] = GetFd(profile2);
reference_profile_fd = GetFd(profile1);
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorBadProfiles);
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
- ProfileAssistant::kErrorDifferentVersions);
+ ProfmanResult::kErrorDifferentVersions);
}
// Under default behaviour we will abort if we cannot load a profile during a merge
@@ -2042,7 +2245,7 @@
// With force-merge we should merge successfully.
std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args),
- ProfileAssistant::kSuccess);
+ ProfmanResult::kSuccess);
ProfileCompilationInfo result(/*for_boot_image=*/ true);
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -2051,7 +2254,7 @@
// Without force-merge we should fail.
std::vector<const std::string> extra_args2({"--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args2),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorBadProfiles);
}
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index 1968468..9406733 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -36,7 +36,6 @@
#include "android-base/parsebool.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-
#include "base/array_ref.h"
#include "base/dumpable.h"
#include "base/logging.h" // For InitLogging.
@@ -64,6 +63,7 @@
#include "profile/profile_boot_info.h"
#include "profile/profile_compilation_info.h"
#include "profile_assistant.h"
+#include "profman/profman_result.h"
namespace art {
@@ -118,7 +118,10 @@
UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
UsageError(" Can be specified multiple time, in which case the data from the different");
- UsageError(" profiles will be aggregated.");
+ UsageError(" profiles will be aggregated. Can also be specified zero times, in which case");
+ UsageError(" profman will still analyze the reference profile against the given --apk and");
+ UsageError(" return exit code based on whether the reference profile is empty and whether");
+ UsageError(" an error occurs, but no merge will happen.");
UsageError("");
UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor.");
UsageError(" Cannot be used together with --profile-file.");
@@ -126,8 +129,8 @@
UsageError(" --reference-profile-file=<filename>: specify a reference profile.");
UsageError(" The data in this file will be compared with the data obtained by merging");
UsageError(" all the files specified with --profile-file or --profile-file-fd.");
- UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into");
- UsageError(" --reference-profile-file. ");
+ UsageError(" If the exit code is ProfmanResult::kCompile then all --profile-file will be");
+ UsageError(" merged into --reference-profile-file. ");
UsageError("");
UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
UsageError(" accepts a file descriptor. Cannot be used together with");
@@ -194,7 +197,7 @@
UsageError(" the min percent of new classes to trigger a compilation.");
UsageError("");
- exit(EXIT_FAILURE);
+ exit(ProfmanResult::kErrorUsage);
}
// Note: make sure you update the Usage if you change these values.
@@ -501,11 +504,10 @@
}
};
- ProfileAssistant::ProcessingResult ProcessProfiles() {
- // Validate that at least one profile file was passed, as well as a reference profile.
- if (profile_files_.empty() && profile_files_fd_.empty()) {
- Usage("No profile files specified.");
- }
+ ProfmanResult::ProcessingResult ProcessProfiles() {
+ // Validate that a reference profile was passed, at the very least. It's okay that profiles are
+ // missing, in which case profman will still analyze the reference profile (to check whether
+ // it's empty), but no merge will happen.
if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
Usage("No reference profile file specified.");
}
@@ -518,7 +520,7 @@
// Check if we have any apks which we should use to filter the profile data.
std::set<ProfileFilterKey> profile_filter_keys;
if (!GetProfileFilterKeyFromApks(&profile_filter_keys)) {
- return ProfileAssistant::kErrorIO;
+ return ProfmanResult::kErrorIO;
}
// Build the profile filter function. If the set of keys is empty it means we
@@ -536,9 +538,9 @@
}
};
- ProfileAssistant::ProcessingResult result;
+ ProfmanResult::ProcessingResult result;
- if (profile_files_.empty()) {
+ if (reference_profile_file_.empty()) {
// The file doesn't need to be flushed here (ProcessProfiles will do it)
// so don't check the usage.
File file(reference_profile_file_fd_, false);
@@ -1205,8 +1207,23 @@
for (std::string_view t :
SplitString(ic_line.substr(1), kProfileParsingInlineChacheTargetSep)) {
InlineCacheSegment out;
- DCHECK_EQ(t[0], 'L') << "Target is not a class? " << t;
- size_t recv_end = t.find_first_of(';');
+ // The target may be an array for methods defined in `j.l.Object`, such as `clone()`.
+ size_t recv_end;
+ if (UNLIKELY(t[0] == '[')) {
+ recv_end = t.find_first_not_of('[', 1u);
+ DCHECK_NE(recv_end, std::string_view::npos);
+ if (t[recv_end] == 'L') {
+ recv_end = t.find_first_of(';', recv_end + 1u);
+ DCHECK_NE(recv_end, std::string_view::npos);
+ } else {
+ // Primitive array.
+ DCHECK_NE(Primitive::GetType(t[recv_end]), Primitive::kPrimNot);
+ }
+ } else {
+ DCHECK_EQ(t[0], 'L') << "Target is not a class? " << t;
+ recv_end = t.find_first_of(';', 1u);
+ DCHECK_NE(recv_end, std::string_view::npos);
+ }
out.receiver_ = t.substr(0, recv_end + 1);
Split(t.substr(recv_end + 1), kProfileParsingTypeSep, &out.inline_caches_);
res->push_back(out);
@@ -1854,7 +1871,7 @@
return copy_and_update_profile_key_;
}
- int32_t CopyAndUpdateProfileKey() {
+ ProfmanResult::CopyAndUpdateResult CopyAndUpdateProfileKey() {
// Validate that at least one profile file was passed, as well as a reference profile.
if (!(profile_files_.size() == 1 ^ profile_files_fd_.size() == 1)) {
Usage("Only one profile file should be specified.");
@@ -1867,10 +1884,6 @@
Usage("No apk files specified");
}
- static constexpr int32_t kErrorFailedToUpdateProfile = -1;
- static constexpr int32_t kErrorFailedToSaveProfile = -2;
- static constexpr int32_t kErrorFailedToLoadProfile = -3;
-
bool use_fds = profile_files_fd_.size() == 1;
ProfileCompilationInfo profile;
@@ -1882,15 +1895,19 @@
// Open the dex files to look up classes and methods.
std::vector<std::unique_ptr<const DexFile>> dex_files;
OpenApkFilesFromLocations(&dex_files);
- if (!profile.UpdateProfileKeys(dex_files)) {
- return kErrorFailedToUpdateProfile;
+ bool matched = false;
+ if (!profile.UpdateProfileKeys(dex_files, &matched)) {
+ return ProfmanResult::kCopyAndUpdateErrorFailedToUpdateProfile;
}
bool result = use_fds
? profile.Save(reference_profile_file_fd_)
: profile.Save(reference_profile_file_, /*bytes_written=*/ nullptr);
- return result ? 0 : kErrorFailedToSaveProfile;
+ if (!result) {
+ return ProfmanResult::kCopyAndUpdateErrorFailedToSaveProfile;
+ }
+ return matched ? ProfmanResult::kCopyAndUpdateSuccess : ProfmanResult::kCopyAndUpdateNoMatch;
} else {
- return kErrorFailedToLoadProfile;
+ return ProfmanResult::kCopyAndUpdateErrorFailedToLoadProfile;
}
}
@@ -1950,7 +1967,7 @@
return ics.Dump(os);
}
-// See ProfileAssistant::ProcessingResult for return codes.
+// See ProfmanResult for return codes.
static int profman(int argc, char** argv) {
ProfMan profman;
diff --git a/runtime/Android.bp b/runtime/Android.bp
index bbc625e..a1a0b48 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -37,6 +37,12 @@
libart_cc_defaults {
name: "libart_nativeunwind_defaults",
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
android_arm: {
// Arm 32 bit does not produce complete exidx unwind information
// so keep the .debug_frame which is relatively small and does
@@ -131,7 +137,7 @@
"exec_utils.cc",
"fault_handler.cc",
"gc/allocation_record.cc",
- "gc/allocator/dlmalloc.cc",
+ "gc/allocator/art-dlmalloc.cc",
"gc/allocator/rosalloc.cc",
"gc/accounting/bitmap.cc",
"gc/accounting/card_table.cc",
@@ -175,11 +181,8 @@
"interpreter/interpreter.cc",
"interpreter/interpreter_cache.cc",
"interpreter/interpreter_common.cc",
- "interpreter/interpreter_intrinsics.cc",
"interpreter/interpreter_switch_impl0.cc",
"interpreter/interpreter_switch_impl1.cc",
- "interpreter/interpreter_switch_impl2.cc",
- "interpreter/interpreter_switch_impl3.cc",
"interpreter/lock_count_data.cc",
"interpreter/shadow_frame.cc",
"interpreter/unstarted_runtime.cc",
@@ -196,6 +199,7 @@
"jni/jni_env_ext.cc",
"jni/jni_id_manager.cc",
"jni/jni_internal.cc",
+ "jni/local_reference_table.cc",
"method_handles.cc",
"metrics/reporter.cc",
"mirror/array.cc",
@@ -210,6 +214,7 @@
"mirror/method_handles_lookup.cc",
"mirror/method_type.cc",
"mirror/object.cc",
+ "mirror/stack_frame_info.cc",
"mirror/stack_trace_element.cc",
"mirror/string.cc",
"mirror/throwable.cc",
@@ -226,6 +231,7 @@
"native/dalvik_system_ZygoteHooks.cc",
"native/java_lang_Class.cc",
"native/java_lang_Object.cc",
+ "native/java_lang_StackStreamFactory.cc",
"native/java_lang_String.cc",
"native/java_lang_StringFactory.cc",
"native/java_lang_System.cc",
@@ -255,6 +261,7 @@
"oat.cc",
"oat_file.cc",
"oat_file_assistant.cc",
+ "oat_file_assistant_context.cc",
"oat_file_manager.cc",
"oat_quick_method_header.cc",
"object_lock.cc",
@@ -270,6 +277,7 @@
"runtime.cc",
"runtime_callbacks.cc",
"runtime_common.cc",
+ "runtime_image.cc",
"runtime_intrinsics.cc",
"runtime_options.cc",
"scoped_thread_state_change.cc",
@@ -439,6 +447,10 @@
"runtime_linux.cc",
"thread_linux.cc",
],
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
shared_libs: [
"libz", // For adler32.
],
@@ -456,17 +468,20 @@
header_libs: [
"art_cmdlineparser_headers",
"cpp-define-generator-definitions",
+ "dlmalloc",
"jni_platform_headers",
"libart_headers",
"libnativehelper_header_only",
],
- export_header_lib_headers: ["libart_headers"],
+ export_header_lib_headers: [
+ "dlmalloc",
+ "libart_headers",
+ ],
whole_static_libs: [
"libcpu_features",
],
shared_libs: [
"libartpalette",
- "libbacktrace",
"libbase", // For common macros.
"liblog",
"liblz4",
@@ -496,7 +511,6 @@
name: "libart_static_base_defaults",
whole_static_libs: [
"libartpalette",
- "libbacktrace",
"libbase",
"liblog",
"liblz4",
@@ -509,6 +523,12 @@
"libz",
],
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
bionic: {
whole_static_libs: [
"libasync_safe", // libunwindstack dependency on Bionic.
@@ -572,6 +592,7 @@
"jni_id_type.h",
"linear_alloc.h",
"lock_word.h",
+ "oat.h",
"oat_file.h",
"process_state.h",
"reflective_value_visitor.h",
@@ -672,6 +693,14 @@
art_cc_defaults {
name: "libart-runtime-gtest-defaults",
+ target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
+ },
tidy_timeout_srcs: [
"common_runtime_test.cc",
],
@@ -681,9 +710,11 @@
],
shared_libs: [
"libbase",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
],
static_libs: [
"libprocinfo",
+ "libziparchive",
],
header_libs: [
"libnativehelper_header_only",
@@ -716,6 +747,14 @@
art_cc_defaults {
name: "art_runtime_tests_defaults",
+ target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
+ },
data: [
":art-gtest-jars-AllFields",
":art-gtest-jars-ErroneousA",
@@ -743,6 +782,7 @@
":art-gtest-jars-MyClass",
":art-gtest-jars-MyClassNatives",
":art-gtest-jars-Nested",
+ ":art-gtest-jars-NonStaticLeafMethods",
":art-gtest-jars-Packages",
":art-gtest-jars-ProfileTestMultiDex",
":art-gtest-jars-ProtoCompare",
@@ -775,13 +815,14 @@
srcs: [
"app_info_test.cc",
"arch/arch_test.cc",
+ "arch/arm/instruction_set_features_arm_test.cc",
+ "arch/arm64/instruction_set_features_arm64_test.cc",
"arch/instruction_set_features_test.cc",
"arch/memcmp16_test.cc",
"arch/stub_test.cc",
- "arch/arm/instruction_set_features_arm_test.cc",
- "arch/arm64/instruction_set_features_arm64_test.cc",
"arch/x86/instruction_set_features_x86_test.cc",
"arch/x86_64/instruction_set_features_x86_64_test.cc",
+ "art_method_test.cc",
"barrier_test.cc",
"base/message_queue_test.cc",
"base/mutex_test.cc",
@@ -801,12 +842,12 @@
"gc/heap_test.cc",
"gc/heap_verification_test.cc",
"gc/reference_queue_test.cc",
- "gc/space/dlmalloc_space_static_test.cc",
"gc/space/dlmalloc_space_random_test.cc",
+ "gc/space/dlmalloc_space_static_test.cc",
"gc/space/image_space_test.cc",
"gc/space/large_object_space_test.cc",
- "gc/space/rosalloc_space_static_test.cc",
"gc/space/rosalloc_space_random_test.cc",
+ "gc/space/rosalloc_space_static_test.cc",
"gc/space/space_create_test.cc",
"gc/system_weak_test.cc",
"gc/task_processor_test.cc",
@@ -819,11 +860,13 @@
"intern_table_test.cc",
"interpreter/safe_math_test.cc",
"interpreter/unstarted_runtime_test.cc",
+ "jit/jit_load_test.cc",
"jit/jit_memory_region_test.cc",
"jit/profile_saver_test.cc",
"jit/profiling_info_test.cc",
"jni/java_vm_ext_test.cc",
"jni/jni_internal_test.cc",
+ "jni/local_reference_table_test.cc",
"method_handles_test.cc",
"metrics/reporter_test.cc",
"mirror/dex_cache_test.cc",
@@ -832,12 +875,14 @@
"mirror/var_handle_test.cc",
"monitor_pool_test.cc",
"monitor_test.cc",
- "oat_file_test.cc",
+ "native_stack_dump_test.cc",
"oat_file_assistant_test.cc",
+ "oat_file_test.cc",
"parsed_options_test.cc",
"prebuilt_tools_test.cc",
"proxy_test.cc",
"reference_table_test.cc",
+ "reflection_test.cc",
"runtime_callbacks_test.cc",
"runtime_test.cc",
"subtype_check_info_test.cc",
@@ -850,10 +895,12 @@
"verifier/reg_type_test.cc",
],
shared_libs: [
- "libbacktrace",
+ "libunwindstack",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
],
static_libs: [
"libgmock",
+ "libziparchive",
],
header_libs: [
"art_cmdlineparser_headers", // For parsed_options_test.
@@ -882,6 +929,7 @@
"art_standalone_gtest_defaults",
"art_runtime_tests_defaults",
],
+ data: [":generate-boot-image"],
target: {
host: {
required: ["dex2oat"],
@@ -894,58 +942,6 @@
test_config: "art_standalone_runtime_tests.xml",
}
-art_cc_defaults {
- name: "art_runtime_compiler_tests_defaults",
- tidy_timeout_srcs: [
- "reflection_test.cc",
- ],
- srcs: [
- "reflection_test.cc",
- ],
- data: [
- ":art-gtest-jars-Main",
- ":art-gtest-jars-NonStaticLeafMethods",
- ":art-gtest-jars-StaticLeafMethods",
- ],
-}
-
-// Version of ART gtest `art_runtime_compiler_tests` bundled with the ART APEX on target.
-// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
-art_cc_test {
- name: "art_runtime_compiler_tests",
- defaults: [
- "art_gtest_defaults",
- "art_runtime_compiler_tests_defaults",
- ],
- shared_libs: [
- "libartd-compiler",
- ],
- target: {
- host: {
- required: ["dex2oatd"],
- },
- },
-}
-
-// Standalone version of ART gtest `art_runtime_compiler_tests`, not bundled with the ART APEX on
-// target.
-art_cc_test {
- name: "art_standalone_runtime_compiler_tests",
- defaults: [
- "art_standalone_gtest_defaults",
- "art_runtime_compiler_tests_defaults",
- ],
- shared_libs: [
- "libart-compiler",
- ],
- target: {
- host: {
- required: ["dex2oat"],
- },
- },
- test_config: "art_standalone_runtime_compiler_tests.xml",
-}
-
genrule {
name: "libart_mterp.x86ng",
out: ["mterp_x86ng.S"],
diff --git a/runtime/alloc_instrumentation.md b/runtime/alloc_instrumentation.md
index 513bbe3..66e9a6a 100644
--- a/runtime/alloc_instrumentation.md
+++ b/runtime/alloc_instrumentation.md
@@ -17,7 +17,7 @@
- These in turn are called by `SetStatsEnabled()`, `SetAllocationListener()`, et al, which
require the mutator lock is not held.
-- With a started runtime, `SetEntrypointsInstrumented()` calls `ScopedSupendAll(`) before updating
+- With a started runtime, `SetEntrypointsInstrumented()` calls `ScopedSuspendAll(`) before updating
the function table.
Mutual exclusion in the dispatch table is thus ensured by the fact that it is only updated while
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index 23213d9..83fd986 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -18,27 +18,13 @@
#include "art_method-inl.h"
#include "base/callee_save_type.h"
+#include "base/common_art_test.h"
#include "entrypoints/quick/callee_save_frame.h"
-#include "common_runtime_test.h"
#include "quick/quick_method_frame_info.h"
namespace art {
-class ArchTest : public CommonRuntimeTest {
- protected:
- void SetUpRuntimeOptions(RuntimeOptions *options) override {
- // Use 64-bit ISA for runtime setup to make method size potentially larger
- // than necessary (rather than smaller) during CreateCalleeSaveMethod
- options->push_back(std::make_pair("imageinstructionset", "x86_64"));
- }
-
- // Do not do any of the finalization. We don't want to run any code, we don't need the heap
- // prepared, it actually will be a problem with setting the instruction set to x86_64 in
- // SetUpRuntimeOptions.
- void FinalizeSetup() override {
- ASSERT_EQ(InstructionSet::kX86_64, Runtime::Current()->GetInstructionSet());
- }
-};
+class ArchTest : public CommonArtTest {};
// Grab architecture specific constants.
namespace arm {
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index 957ac94..7f1f470 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -313,10 +313,6 @@
DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
- RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r1
-.endm
-
.macro RETURN_OR_DELIVER_PENDING_EXCEPTION
ldr ip, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field.
cmp ip, #0
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index aff055e..a3d46c2 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -25,6 +25,8 @@
#define FRAME_SIZE_SAVE_EVERYTHING 192
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_R0_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
// The offset from the art_quick_read_barrier_mark_introspection (used for field
// loads with 32-bit LDR) to the entrypoint for field loads with 16-bit LDR,
@@ -43,22 +45,22 @@
// The offset of the reference load LDR from the return address in LR for field loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET -8
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET (-8)
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET (-4)
#else
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET -4
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET -2
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET (-4)
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET (-2)
#endif
// The offset of the reference load LDR from the return address in LR for array loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-8)
#else
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-4)
#endif
// The offset of the reference load LDR from the return address in LR for GC root loads.
-#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET -8
-#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET -6
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET (-8)
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET (-6)
// The offset of the MOV from the return address in LR for intrinsic CAS.
-#define BAKER_MARK_INTROSPECTION_INTRINSIC_CAS_MOV_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_INTRINSIC_CAS_MOV_OFFSET (-8)
#endif // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 7bd402f..974e056 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -45,76 +45,59 @@
return instr_size;
}
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED,
- void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow) {
+uintptr_t FaultManager::GetFaultPc(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context) {
struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
- *out_sp = static_cast<uintptr_t>(sc->arm_sp);
- VLOG(signals) << "sp: " << std::hex << *out_sp;
- if (*out_sp == 0) {
- return;
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ if (sc->arm_sp == 0) {
+ VLOG(signals) << "Missing SP";
+ return 0u;
}
+ return sc->arm_pc;
+}
- // In the case of a stack overflow, the stack is not valid and we can't
- // get the method from the top of the stack. However it's in r0.
- uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address);
- uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kArm));
- if (overflow_addr == fault_addr) {
- *out_method = reinterpret_cast<ArtMethod*>(sc->arm_r0);
- *out_is_stack_overflow = true;
- } else {
- // The method is at the top of the stack.
- *out_method = reinterpret_cast<ArtMethod*>(reinterpret_cast<uintptr_t*>(*out_sp)[0]);
- *out_is_stack_overflow = false;
- }
-
- // Work out the return PC. This will be the address of the instruction
- // following the faulting ldr/str instruction. This is in thumb mode so
- // the instruction might be a 16 or 32 bit one. Also, the GC map always
- // has the bottom bit of the PC set so we also need to set that.
-
- // Need to work out the size of the instruction that caused the exception.
- uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc);
- VLOG(signals) << "pc: " << std::hex << static_cast<void*>(ptr);
-
- if (ptr == nullptr) {
- // Somebody jumped to 0x0. Definitely not ours, and will definitely segfault below.
- *out_method = nullptr;
- return;
- }
-
- uint32_t instr_size = GetInstructionSize(ptr);
-
- *out_return_pc = (sc->arm_pc + instr_size) | 1;
+uintptr_t FaultManager::GetFaultSp(void* context) {
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ return sc->arm_sp;
}
bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
- if (!IsValidImplicitCheck(info)) {
+ uintptr_t fault_address = reinterpret_cast<uintptr_t>(info->si_addr);
+ if (!IsValidFaultAddress(fault_address)) {
return false;
}
- // The code that looks for the catch location needs to know the value of the
- // ARM PC at the point of call. For Null checks we insert a GC map that is immediately after
- // the load/store instruction that might cause the fault. However the mapping table has
- // the low bits set for thumb mode so we need to set the bottom bit for the LR
- // register in order to find the mapping.
+
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ ArtMethod** sp = reinterpret_cast<ArtMethod**>(sc->arm_sp);
+ if (!IsValidMethod(*sp)) {
+ return false;
+ }
+
+ // For null checks in compiled code we insert a stack map that is immediately
+ // after the load/store instruction that might cause the fault and we need to
+ // pass the return PC to the handler. For null checks in Nterp, we similarly
+ // need the return PC to recognize that this was a null check in Nterp, so
+ // that the handler can get the needed data from the Nterp frame.
+
+ // Note: Currently, Nterp is compiled to the A32 instruction set and managed
+ // code is compiled to the T32 instruction set.
+ // To find the stack map for compiled code, we need to set the bottom bit in
+ // the return PC indicating T32 just like we would if we were going to return
+ // to that PC (though we're going to jump to the exception handler instead).
// Need to work out the size of the instruction that caused the exception.
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc);
bool in_thumb_mode = sc->arm_cpsr & (1 << 5);
uint32_t instr_size = in_thumb_mode ? GetInstructionSize(ptr) : 4;
- uintptr_t gc_map_location = (sc->arm_pc + instr_size) | (in_thumb_mode ? 1 : 0);
+ uintptr_t return_pc = (sc->arm_pc + instr_size) | (in_thumb_mode ? 1 : 0);
- // Push the gc map location to the stack and pass the fault address in LR.
+ // Push the return PC to the stack and pass the fault address in LR.
sc->arm_sp -= sizeof(uintptr_t);
- *reinterpret_cast<uintptr_t*>(sc->arm_sp) = gc_map_location;
- sc->arm_lr = reinterpret_cast<uintptr_t>(info->si_addr);
+ *reinterpret_cast<uintptr_t*>(sc->arm_sp) = return_pc;
+ sc->arm_lr = fault_address;
+
+ // Arrange for the signal handler to return to the NPE entrypoint.
sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
// Make sure the thumb bit is set as the handler is in thumb mode.
sc->arm_cpsr = sc->arm_cpsr | (1 << 5);
diff --git a/runtime/arch/arm/jni_entrypoints_arm.S b/runtime/arch/arm/jni_entrypoints_arm.S
index fc57df7..d91882c 100644
--- a/runtime/arch/arm/jni_entrypoints_arm.S
+++ b/runtime/arch/arm/jni_entrypoints_arm.S
@@ -99,7 +99,7 @@
// Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
// for @FastNative or @CriticalNative.
ldr ip, [r0, #THREAD_TOP_QUICK_FRAME_OFFSET] // uintptr_t tagged_quick_frame
- bic ip, #1 // ArtMethod** sp
+ bic ip, #TAGGED_JNI_SP_MASK // ArtMethod** sp
ldr ip, [ip] // ArtMethod* method
ldr ip, [ip, #ART_METHOD_ACCESS_FLAGS_OFFSET] // uint32_t access_flags
tst ip, #(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE)
@@ -327,6 +327,11 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, rSELF
/*
+ * Trampoline to `artJniMethodEntryHook()` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_entry_hook, artJniMethodEntryHook, rSELF
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_monitored_method_start, artJniMonitoredMethodStart, rSELF
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index d6f129b..8f5d88a 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -134,16 +134,50 @@
.cfi_adjust_cfa_offset -52
.endm
-.macro RETURN_IF_RESULT_IS_ZERO
- cbnz r0, 1f @ result non-zero branch over
- bx lr @ return
+.macro RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ ldr r1, [rSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field.
+ cbnz r1, 1f
+ DEOPT_OR_RETURN r1 // Check if deopt is required
1:
+ DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO
- cbz r0, 1f @ result zero branch over
- bx lr @ return
-1:
+.macro DEOPT_OR_RETURN temp, is_ref = 0
+ ldr \temp, [rSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ bx lr
+2:
+ SETUP_SAVE_EVERYTHING_FRAME \temp
+ mov r2, \is_ref // pass if result is a reference
+ mov r1, r0 // pass the result
+ mov r0, rSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ bx lr
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+.endm
+
+.macro DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_R0 temp, is_ref
+ ldr \temp, [rSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
+ REFRESH_MARKING_REGISTER
+ bx lr
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ str r0, [sp, SAVE_EVERYTHING_FRAME_R0_OFFSET] // update result in the frame
+ mov r2, \is_ref // pass if result is a reference
+ mov r1, r0 // pass the result
+ mov r0, rSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ bx lr
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
.endm
.macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
@@ -183,12 +217,16 @@
.endm
.macro RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
- RETURN_IF_RESULT_IS_ZERO
+ cbnz r0, 1f @ result non-zero branch over
+ DEOPT_OR_RETURN r1
+1:
DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
- RETURN_IF_RESULT_IS_NON_ZERO
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+ cbz r0, 1f @ result zero branch over
+ DEOPT_OR_RETURN r1, /*is_ref=*/1
+1:
DELIVER_PENDING_EXCEPTION
.endm
@@ -517,8 +555,7 @@
bl artLockObjectFromCode @ (Object* obj, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_lock_object_no_inline
/*
@@ -548,8 +585,7 @@
bl artUnlockObjectFromCode @ (Object* obj, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_unlock_object_no_inline
/*
@@ -601,13 +637,33 @@
.cfi_rel_offset \rReg, \offset
.endm
- /*
- * Macro to insert read barrier, only used in art_quick_aput_obj.
- * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- */
-.macro READ_BARRIER rDest, rObj, offset
+ // Helper macros for `art_quick_aput_obj`.
#ifdef USE_READ_BARRIER
+#ifdef USE_BAKER_READ_BARRIER
+.macro BAKER_RB_CHECK_GRAY_BIT_AND_LOAD rDest, rObj, offset, gray_slow_path_label
+ ldr ip, [\rObj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ tst ip, #LOCK_WORD_READ_BARRIER_STATE_MASK_SHIFTED
+ bne \gray_slow_path_label
+ // False dependency to avoid needing load/load fence.
+ add \rObj, \rObj, ip, lsr #32
+ ldr \rDest, [\rObj, #\offset]
+ UNPOISON_HEAP_REF \rDest
+.endm
+
+.macro BAKER_RB_LOAD_AND_MARK rDest, rObj, offset, mark_function
+ ldr \rDest, [\rObj, #\offset]
+ UNPOISON_HEAP_REF \rDest
+ str lr, [sp, #-8]! @ Save LR with correct stack alignment.
+ .cfi_rel_offset lr, 0
+ .cfi_adjust_cfa_offset 8
+ bl \mark_function
+ ldr lr, [sp], #8 @ Restore LR.
+ .cfi_restore lr
+ .cfi_adjust_cfa_offset -8
+.endm
+#else // USE_BAKER_READ_BARRIER
+ .extern artReadBarrierSlow
+.macro READ_BARRIER_SLOW rDest, rObj, offset
push {r0-r3, ip, lr} @ 6 words for saved registers (used in art_quick_aput_obj)
.cfi_adjust_cfa_offset 24
.cfi_rel_offset r0, 0
@@ -640,30 +696,36 @@
pop {lr} @ restore lr
.cfi_adjust_cfa_offset -4
.cfi_restore lr
-#else
- ldr \rDest, [\rObj, #\offset]
- UNPOISON_HEAP_REF \rDest
-#endif // USE_READ_BARRIER
.endm
+#endif // USE_BAKER_READ_BARRIER
+#endif // USE_READ_BARRIER
-#ifdef USE_READ_BARRIER
- .extern artReadBarrierSlow
-#endif
.hidden art_quick_aput_obj
ENTRY art_quick_aput_obj
-#ifdef USE_READ_BARRIER
- @ The offset to .Ldo_aput_null is too large to use cbz due to expansion from READ_BARRIER macro.
+#if defined(USE_READ_BARRIER) && !defined(USE_BAKER_READ_BARRIER)
+ @ The offset to .Ldo_aput_null is too large to use cbz due to expansion from `READ_BARRIER_SLOW`.
tst r2, r2
- beq .Ldo_aput_null
-#else
- cbz r2, .Ldo_aput_null
+ beq .Laput_obj_null
+ READ_BARRIER_SLOW r3, r0, MIRROR_OBJECT_CLASS_OFFSET
+ READ_BARRIER_SLOW r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
+ READ_BARRIER_SLOW r4, r2, MIRROR_OBJECT_CLASS_OFFSET
+#else // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
+ cbz r2, .Laput_obj_null
+#ifdef USE_READ_BARRIER
+ cmp rMR, #0
+ bne .Laput_obj_gc_marking
#endif // USE_READ_BARRIER
- READ_BARRIER r3, r0, MIRROR_OBJECT_CLASS_OFFSET
- READ_BARRIER ip, r2, MIRROR_OBJECT_CLASS_OFFSET
- READ_BARRIER r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
- cmp r3, ip @ value's type == array's component type - trivial assignability
- bne .Lcheck_assignability
-.Ldo_aput:
+ ldr r3, [r0, #MIRROR_OBJECT_CLASS_OFFSET]
+ UNPOISON_HEAP_REF r3
+ // R4 is a scratch register in managed ARM ABI.
+ ldr r4, [r2, #MIRROR_OBJECT_CLASS_OFFSET]
+ UNPOISON_HEAP_REF r4
+ ldr r3, [r3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET]
+ UNPOISON_HEAP_REF r3
+#endif // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
+ cmp r3, r4 @ value's type == array's component type - trivial assignability
+ bne .Laput_obj_check_assignability
+.Laput_obj_store:
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
POISON_HEAP_REF r2
str r2, [r3, r1, lsl #2]
@@ -671,26 +733,22 @@
lsr r0, r0, #CARD_TABLE_CARD_SHIFT
strb r3, [r3, r0]
blx lr
-.Ldo_aput_null:
+
+.Laput_obj_null:
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
str r2, [r3, r1, lsl #2]
blx lr
-.Lcheck_assignability:
+
+.Laput_obj_check_assignability:
push {r0-r2, lr} @ save arguments
.cfi_adjust_cfa_offset 16
- .cfi_rel_offset r0, 0
- .cfi_rel_offset r1, 4
- .cfi_rel_offset r2, 8
.cfi_rel_offset lr, 12
- mov r1, ip
+ mov r1, r4
mov r0, r3
bl artIsAssignableFromCode
cbz r0, .Lthrow_array_store_exception
.cfi_remember_state
pop {r0-r2, lr}
- .cfi_restore r0
- .cfi_restore r1
- .cfi_restore r2
.cfi_restore lr
.cfi_adjust_cfa_offset -16
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
@@ -700,19 +758,52 @@
lsr r0, r0, #CARD_TABLE_CARD_SHIFT
strb r3, [r3, r0]
blx lr
+
.Lthrow_array_store_exception:
CFI_RESTORE_STATE_AND_DEF_CFA sp, 16
pop {r0-r2, lr}
- .cfi_restore r0
- .cfi_restore r1
- .cfi_restore r2
.cfi_restore lr
.cfi_adjust_cfa_offset -16
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ .cfi_remember_state
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r3
mov r1, r2
- mov r2, rSELF @ pass Thread::Current
+ mov r2, rSELF @ Pass Thread::Current.
bl artThrowArrayStoreException @ (Class*, Class*, Thread*)
- bkpt @ unreached
+ bkpt @ Unreachable.
+
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, 0
+.Laput_obj_gc_marking:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ r3, r0, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_array_class
+.Laput_obj_mark_array_class_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, .Laput_obj_mark_array_element
+.Laput_obj_mark_array_element_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ r4, r2, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_object_class
+.Laput_obj_mark_object_class_continue:
+
+ cmp r3, r4 @ value's type == array's component type - trivial assignability
+ // All registers are set up for correctly `.Laput_obj_check_assignability`.
+ bne .Laput_obj_check_assignability
+ b .Laput_obj_store
+
+.Laput_obj_mark_array_class:
+ BAKER_RB_LOAD_AND_MARK r3, r0, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_class_continue
+
+.Laput_obj_mark_array_element:
+ BAKER_RB_LOAD_AND_MARK \
+ r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_element_continue
+
+.Laput_obj_mark_object_class:
+ BAKER_RB_LOAD_AND_MARK r4, r2, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg04
+ b .Laput_obj_mark_object_class_continue
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
END art_quick_aput_obj
// Macro to facilitate adding new allocation entrypoints.
@@ -782,11 +873,8 @@
mov r1, rSELF @ pass Thread::Current
bl \entrypoint @ (uint32_t index, Thread*)
cbz r0, 1f @ If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
- REFRESH_MARKING_REGISTER
- bx lr
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+ str r0, [sp, #136] @ store result in the frame
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_R0 r1, /* is_ref= */ 1
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END \name
@@ -809,12 +897,12 @@
/*
* Called by managed code to resolve a static field and load a non-wide value.
*/
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
/*
* Called by managed code to resolve a static field and load a 64-bit primitive value.
*/
@@ -827,7 +915,7 @@
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
cbnz r2, 1f @ success if no exception pending
- bx lr @ return on success
+ DEOPT_OR_RETURN r2 @ check if deopt is required or return
1:
DELIVER_PENDING_EXCEPTION
END art_quick_get64_static
@@ -835,12 +923,12 @@
/*
* Called by managed code to resolve an instance field and load a non-wide value.
*/
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
/*
* Called by managed code to resolve an instance field and load a 64-bit primitive value.
*/
@@ -853,7 +941,7 @@
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
cbnz r2, 1f @ success if no exception pending
- bx lr @ return on success
+ DEOPT_OR_RETURN r2 @ check if deopt is required or return
1:
DELIVER_PENDING_EXCEPTION
END art_quick_get64_instance
@@ -888,8 +976,7 @@
.cfi_adjust_cfa_offset -16
RESTORE_SAVE_REFS_ONLY_FRAME @ TODO: we can clearly save an add here
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_set64_instance
.extern artSet64StaticFromCompiledCode
@@ -903,8 +990,7 @@
.cfi_adjust_cfa_offset -16
RESTORE_SAVE_REFS_ONLY_FRAME @ TODO: we can clearly save an add here
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_set64_static
// Generate the allocation entrypoints for each allocator.
@@ -1037,7 +1123,7 @@
bl \cxx_name @ (mirror::Class* cls, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \c_name
.endm
@@ -1096,10 +1182,6 @@
str r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET]
POISON_HEAP_REF r0
str r0, [r2, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
- // Fence. This is "ish" not "ishst" so
- // that the code after this allocation
- // site will see the right values in
- // the fields of the class.
mov r0, r2
// No barrier. The class is already observably initialized (otherwise the fast
// path size check above would fail) and new-instance allocations are protected
@@ -1122,7 +1204,7 @@
bl \entrypoint // (mirror::Class* klass, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1162,10 +1244,6 @@
POISON_HEAP_REF r0
str r0, [r3, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
str r1, [r3, #MIRROR_ARRAY_LENGTH_OFFSET] // Store the array length.
- // Fence. This is "ish" not "ishst" so
- // that the code after this allocation
- // site will see the right values in
- // the fields of the class.
mov r0, r3
// new-array is special. The class is loaded and immediately goes to the Initialized state
// before it is published. Therefore the only fence needed is for the publication of the object.
@@ -1195,7 +1273,7 @@
bl \entrypoint
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1448,16 +1526,27 @@
.cfi_remember_state
.cfi_def_cfa_register sp
+ // store into fpr, for when it's a fpr return...
+ vmov d0, r0, r1
+
+ LOAD_RUNTIME_INSTANCE r2
+ ldr r2, [r2, #RUN_EXIT_HOOKS_OFFSET_FROM_RUNTIME_INSTANCE]
+ cbnz r2, .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame. Skip arg registers.
add sp, #FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY
.cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- // store into fpr, for when it's a fpr return...
- vmov d0, r0, r1
bx lr // ret
+.Lcall_method_exit_hook:
+ mov r2, #FRAME_SIZE_SAVE_REFS_AND_ARGS
+ bl art_quick_method_exit_hook
+ b .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA r10, FRAME_SIZE_SAVE_REFS_AND_ARGS
.Lexception_in_native:
@@ -1497,79 +1586,6 @@
ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
/*
- * Routine that intercepts method calls and returns.
- */
- .extern artInstrumentationMethodEntryFromCode
- .extern artInstrumentationMethodExitFromCode
-ENTRY art_quick_instrumentation_entry
- @ Make stack crawlable and clobber r2 and r3 (post saving)
- SETUP_SAVE_REFS_AND_ARGS_FRAME r2
- @ preserve r0 (not normally an arg) knowing there is a spare slot in kSaveRefsAndArgs.
- str r0, [sp, #4]
- mov r2, rSELF @ pass Thread::Current
- mov r3, sp @ pass SP
- blx artInstrumentationMethodEntryFromCode @ (Method*, Object*, Thread*, SP)
- cbz r0, .Ldeliver_instrumentation_entry_exception
- .cfi_remember_state
- @ Deliver exception if we got nullptr as function.
- mov r12, r0 @ r12 holds reference to code
- ldr r0, [sp, #4] @ restore r0
- RESTORE_SAVE_REFS_AND_ARGS_FRAME
- adr lr, art_quick_instrumentation_exit + /* thumb mode */ 1
- @ load art_quick_instrumentation_exit into lr in thumb mode
- REFRESH_MARKING_REGISTER
- bx r12 @ call method with lr set to art_quick_instrumentation_exit
-.Ldeliver_instrumentation_entry_exception:
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_REFS_AND_ARGS
- @ Deliver exception for art_quick_instrumentation_entry placed after
- @ art_quick_instrumentation_exit so that the fallthrough works.
- RESTORE_SAVE_REFS_AND_ARGS_FRAME
- DELIVER_PENDING_EXCEPTION
-END art_quick_instrumentation_entry
-
-ENTRY art_quick_instrumentation_exit
- mov lr, #0 @ link register is to here, so clobber with 0 for later checks
- SETUP_SAVE_EVERYTHING_FRAME r2
-
- add r3, sp, #8 @ store fpr_res pointer, in kSaveEverything frame
- add r2, sp, #136 @ store gpr_res pointer, in kSaveEverything frame
- mov r1, sp @ pass SP
- mov r0, rSELF @ pass Thread::Current
- blx artInstrumentationMethodExitFromCode @ (Thread*, SP, gpr_res*, fpr_res*)
-
- cbz r0, .Ldo_deliver_instrumentation_exception
- @ Deliver exception if we got nullptr as function.
- .cfi_remember_state
- cbnz r1, .Ldeoptimize
- // Normal return.
- str r0, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
- @ Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- REFRESH_MARKING_REGISTER
- bx lr
-.Ldo_deliver_instrumentation_exception:
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-.Ldeoptimize:
- str r1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
- @ Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- // Jump to art_quick_deoptimize.
- b art_quick_deoptimize
-END art_quick_instrumentation_exit
-
- /*
- * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
- * will long jump to the upcall with a special exception of -1.
- */
- .extern artDeoptimize
-ENTRY art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME r0
- mov r0, rSELF @ pass Thread::Current
- blx artDeoptimize @ (Thread*)
-END art_quick_deoptimize
-
- /*
* Compiled code has requested that we deoptimize into the interpreter. The deoptimization
* will long jump to the interpreter bridge.
*/
@@ -1883,7 +1899,7 @@
bl artStringBuilderAppend @ (uint32_t, const unit32_t*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END art_quick_string_builder_append
/*
@@ -1895,13 +1911,9 @@
*
* If `reg` is different from `r0`, the generated function follows a
* non-standard runtime calling convention:
- * - register `reg` is used to pass the (sole) argument of this
- * function (instead of R0);
- * - register `reg` is used to return the result of this function
- * (instead of R0);
- * - R0 is treated like a normal (non-argument) caller-save register;
- * - everything else is the same as in the standard runtime calling
- * convention (e.g. standard callee-save registers are preserved).
+ * - register `reg` (which may be different from R0) is used to pass the (sole) argument,
+ * - register `reg` (which may be different from R0) is used to return the result,
+ * - all other registers are callee-save (the values they hold are preserved).
*/
.macro READ_BARRIER_MARK_REG name, reg
ENTRY \name
@@ -2481,29 +2493,27 @@
SETUP_SAVE_EVERYTHING_FRAME r0
ldr r0, [sp, FRAME_SIZE_SAVE_EVERYTHING] @ pass ArtMethod
mov r1, rSELF @ pass Thread::Current
- bl artMethodEntryHook @ (ArtMethod*, Thread*)
+ mov r2, sp @ pass SP
+ bl artMethodEntryHook @ (ArtMethod*, Thread*, SP)
RESTORE_SAVE_EVERYTHING_FRAME
REFRESH_MARKING_REGISTER
blx lr
END art_quick_method_entry_hook
ENTRY art_quick_method_exit_hook
- SETUP_SAVE_EVERYTHING_FRAME r2
+ SETUP_SAVE_EVERYTHING_FRAME r5
- add r3, sp, #8 @ store fpr_res pointer, in kSaveEverything frame
- add r2, sp, #136 @ store gpr_res pointer, in kSaveEverything frame
- ldr r1, [sp, #FRAME_SIZE_SAVE_EVERYTHING] @ pass ArtMethod*
+ sub sp, #4 @ align stack
+ push {r2} @ pass frame_size stack
+ add r3, sp, #(8 + 8) @ store fpr_res pointer, in kSaveEverything frame
+ add r2, sp, #(136 + 8) @ store gpr_res pointer, in kSaveEverything frame
+ add r1, sp, #(FRAME_SIZE_SAVE_EVERYTHING + 8) @ pass ArtMethod**
mov r0, rSELF @ pass Thread::Current
- blx artMethodExitHook @ (Thread*, ArtMethod*, gpr_res*, fpr_res*)
+ blx artMethodExitHook @ (Thread*, ArtMethod**, gpr_res*, fpr_res*,
+ @ frame_size)
- .cfi_remember_state
- cbnz r0, .Ldo_deliver_instrumentation_exception_exit @ Deliver exception
-
- // Normal return.
+ add sp, #8 @ pop arguments on stack
RESTORE_SAVE_EVERYTHING_FRAME
REFRESH_MARKING_REGISTER
blx lr
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
END art_quick_method_exit_hook
diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
index 987b459..d7fef6f 100644
--- a/runtime/arch/arm/quick_entrypoints_cc_arm.cc
+++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
@@ -25,6 +25,7 @@
uint32_t*);
template <bool kIsStatic>
+NO_STACK_PROTECTOR
static void quick_invoke_reg_setup(ArtMethod* method, uint32_t* args, uint32_t args_size,
Thread* self, JValue* result, const char* shorty) {
// Note: We do not follow aapcs ABI in quick code for both softfp and hardfp.
@@ -96,6 +97,7 @@
// Called by art::ArtMethod::Invoke to do entry into a non-static method.
// TODO: migrate into an assembly implementation as with ARM64.
+NO_STACK_PROTECTOR
extern "C" void art_quick_invoke_stub(ArtMethod* method, uint32_t* args, uint32_t args_size,
Thread* self, JValue* result, const char* shorty) {
quick_invoke_reg_setup<false>(method, args, args_size, self, result, shorty);
@@ -103,6 +105,7 @@
// Called by art::ArtMethod::Invoke to do entry into a static method.
// TODO: migrate into an assembly implementation as with ARM64.
+NO_STACK_PROTECTOR
extern "C" void art_quick_invoke_static_stub(ArtMethod* method, uint32_t* args,
uint32_t args_size, Thread* self, JValue* result,
const char* shorty) {
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index 887ee02..983fe3a 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -19,6 +19,7 @@
#include "asm_support.h"
+// TODO(mythria): Change these to use constants from callee_save_frame_arm64.h
#define CALLEE_SAVES_SIZE (12 * 8 + 8 * 8)
// +8 for the ArtMethod, +8 for alignment.
#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVES (CALLEE_SAVES_SIZE + 16)
@@ -27,6 +28,8 @@
#define FRAME_SIZE_SAVE_EVERYTHING 512
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_X0_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
// The offset from art_quick_read_barrier_mark_introspection to the array switch cases,
// i.e. art_quick_read_barrier_mark_introspection_arrays.
@@ -37,17 +40,17 @@
// The offset of the reference load LDR from the return address in LR for field loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET (-8)
#else
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET (-4)
#endif
// The offset of the reference load LDR from the return address in LR for array loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-8)
#else
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-4)
#endif
// The offset of the reference load LDR from the return address in LR for GC root loads.
-#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET (-8)
#endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 16f4792..569457a 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -29,6 +29,10 @@
#define __hwasan_handle_longjmp(sp)
#endif
+#if defined(__aarch64__) && defined(__BIONIC__)
+#include <bionic/malloc.h>
+#endif
+
namespace art {
namespace arm64 {
@@ -130,7 +134,21 @@
extern "C" NO_RETURN void art_quick_do_long_jump(uint64_t*, uint64_t*);
-void Arm64Context::DoLongJump() {
+#if defined(__aarch64__) && defined(__BIONIC__) && defined(M_MEMTAG_STACK_IS_ON)
+static inline __attribute__((no_sanitize("memtag"))) void untag_memory(void* from, void* to) {
+ __asm__ __volatile__(
+ ".arch_extension mte\n"
+ "1:\n"
+ "stg %[Ptr], [%[Ptr]], #16\n"
+ "cmp %[Ptr], %[End]\n"
+ "b.lt 1b\n"
+ : [Ptr] "+&r"(from)
+ : [End] "r"(to)
+ : "memory");
+}
+#endif
+
+__attribute__((no_sanitize("memtag"))) void Arm64Context::DoLongJump() {
uint64_t gprs[arraysize(gprs_)];
uint64_t fprs[kNumberOfDRegisters];
@@ -145,6 +163,13 @@
}
// Ensure the Thread Register contains the address of the current thread.
DCHECK_EQ(reinterpret_cast<uintptr_t>(Thread::Current()), gprs[TR]);
+#if defined(__aarch64__) && defined(__BIONIC__) && defined(M_MEMTAG_STACK_IS_ON)
+ bool memtag_stack;
+ // This works fine because versions of Android that did not support M_MEMTAG_STACK_ON did not
+ // support stack tagging either.
+ if (android_mallopt(M_MEMTAG_STACK_IS_ON, &memtag_stack, sizeof(memtag_stack)) && memtag_stack)
+ untag_memory(__builtin_frame_address(0), reinterpret_cast<void*>(gprs[SP]));
+#endif
// Tell HWASan about the new stack top.
__hwasan_handle_longjmp(reinterpret_cast<void*>(gprs[SP]));
// The Marking Register will be updated by art_quick_do_long_jump.
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index a5becf6..9634492 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -38,66 +38,56 @@
namespace art {
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo,
- void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow) {
- struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
-
+uintptr_t FaultManager::GetFaultPc(siginfo_t* siginfo, void* context) {
// SEGV_MTEAERR (Async MTE fault) is delivered at an arbitrary point after the actual fault.
// Register contents, including PC and SP, are unrelated to the fault and can only confuse ART
// signal handlers.
if (siginfo->si_signo == SIGSEGV && siginfo->si_code == SEGV_MTEAERR) {
- return;
+ VLOG(signals) << "Async MTE fault";
+ return 0u;
}
- *out_sp = static_cast<uintptr_t>(sc->sp);
- VLOG(signals) << "sp: " << *out_sp;
- if (*out_sp == 0) {
- return;
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ if (sc->sp == 0) {
+ VLOG(signals) << "Missing SP";
+ return 0u;
}
+ return sc->pc;
+}
- // In the case of a stack overflow, the stack is not valid and we can't
- // get the method from the top of the stack. However it's in x0.
- uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address);
- uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kArm64));
- if (overflow_addr == fault_addr) {
- *out_method = reinterpret_cast<ArtMethod*>(sc->regs[0]);
- *out_is_stack_overflow = true;
- } else {
- // The method is at the top of the stack.
- *out_method = *reinterpret_cast<ArtMethod**>(*out_sp);
- *out_is_stack_overflow = false;
- }
-
- // Work out the return PC. This will be the address of the instruction
- // following the faulting ldr/str instruction.
- VLOG(signals) << "pc: " << std::hex
- << static_cast<void*>(reinterpret_cast<uint8_t*>(sc->pc));
-
- *out_return_pc = sc->pc + 4;
+uintptr_t FaultManager::GetFaultSp(void* context) {
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ return sc->sp;
}
bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
- if (!IsValidImplicitCheck(info)) {
+ uintptr_t fault_address = reinterpret_cast<uintptr_t>(info->si_addr);
+ if (!IsValidFaultAddress(fault_address)) {
return false;
}
- // The code that looks for the catch location needs to know the value of the
- // PC at the point of call. For Null checks we insert a GC map that is immediately after
- // the load/store instruction that might cause the fault.
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ // For null checks in compiled code we insert a stack map that is immediately
+ // after the load/store instruction that might cause the fault and we need to
+ // pass the return PC to the handler. For null checks in Nterp, we similarly
+ // need the return PC to recognize that this was a null check in Nterp, so
+ // that the handler can get the needed data from the Nterp frame.
- // Push the gc map location to the stack and pass the fault address in LR.
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ ArtMethod** sp = reinterpret_cast<ArtMethod**>(sc->sp);
+ uintptr_t return_pc = sc->pc + 4u;
+ if (!IsValidMethod(*sp) || !IsValidReturnPc(sp, return_pc)) {
+ return false;
+ }
+
+ // Push the return PC to the stack and pass the fault address in LR.
sc->sp -= sizeof(uintptr_t);
- *reinterpret_cast<uintptr_t*>(sc->sp) = sc->pc + 4;
- sc->regs[30] = reinterpret_cast<uintptr_t>(info->si_addr);
+ *reinterpret_cast<uintptr_t*>(sc->sp) = return_pc;
+ sc->regs[30] = fault_address;
+ // Arrange for the signal handler to return to the NPE entrypoint.
sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
VLOG(signals) << "Generating null pointer exception";
return true;
@@ -112,12 +102,11 @@
constexpr uint32_t checkinst =
0xf9400000 | (kSuspendCheckRegister << 5) | (kSuspendCheckRegister << 0);
- struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
- VLOG(signals) << "checking suspend";
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
uint32_t inst = *reinterpret_cast<uint32_t*>(sc->pc);
- VLOG(signals) << "inst: " << std::hex << inst << " checkinst: " << checkinst;
+ VLOG(signals) << "checking suspend; inst: " << std::hex << inst << " checkinst: " << checkinst;
if (inst != checkinst) {
// The instruction is not good, not ours.
return false;
@@ -141,8 +130,8 @@
bool StackOverflowHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
void* context) {
- struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc;
VLOG(signals) << "sigcontext: " << std::hex << sc;
diff --git a/runtime/arch/arm64/jni_entrypoints_arm64.S b/runtime/arch/arm64/jni_entrypoints_arm64.S
index 463767c..9612a7b 100644
--- a/runtime/arch/arm64/jni_entrypoints_arm64.S
+++ b/runtime/arch/arm64/jni_entrypoints_arm64.S
@@ -103,7 +103,7 @@
// Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
// for @FastNative or @CriticalNative.
ldr xIP0, [x0, #THREAD_TOP_QUICK_FRAME_OFFSET] // uintptr_t tagged_quick_frame
- bic xIP0, xIP0, #1 // ArtMethod** sp
+ bic xIP0, xIP0, #TAGGED_JNI_SP_MASK // ArtMethod** sp
ldr xIP0, [xIP0] // ArtMethod* method
ldr xIP0, [xIP0, #ART_METHOD_ACCESS_FLAGS_OFFSET] // uint32_t access_flags
mov xIP1, #(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE)
@@ -366,6 +366,11 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, xSELF
/*
+ * Trampoline to `artJniMethodEntryHook` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_entry_hook, artJniMethodEntryHook, xSELF
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_monitored_method_start, artJniMonitoredMethodStart, xSELF
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index d8c91e1..af79868 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -194,26 +194,56 @@
RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
.endm
-.macro RETURN_IF_RESULT_IS_ZERO
- cbnz x0, 1f // result non-zero branch over
- ret // return
-1:
+.macro RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ ldr x1, [xSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field.
+ cbnz x1, 1f
+ DEOPT_OR_RETURN x1 // Check if deopt is required
+1: // deliver exception on current thread
+ DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO
- cbz x0, 1f // result zero branch over
- ret // return
-1:
+.macro DEOPT_OR_RETURN temp, is_ref = 0
+ ldr \temp, [xSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ ret
+2:
+ SETUP_SAVE_EVERYTHING_FRAME
+ mov x2, \is_ref // pass if result is a reference
+ mov x1, x0 // pass the result
+ mov x0, xSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
.endm
-// Same as above with x1. This is helpful in stubs that want to avoid clobbering another register.
-.macro RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
- RETURN_OR_DELIVER_PENDING_EXCEPTION_REG x1
+.macro DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_X0 temp, is_ref
+ ldr \temp, [xSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
+ REFRESH_MARKING_REGISTER
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ str x0, [sp, #SAVE_EVERYTHING_FRAME_X0_OFFSET] // update result in the frame
+ mov x2, \is_ref // pass if result is a reference
+ mov x1, x0 // pass the result
+ mov x0, xSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
.endm
+
.macro RETURN_IF_W0_IS_ZERO_OR_DELIVER
cbnz w0, 1f // result non-zero branch over
- ret // return
+ DEOPT_OR_RETURN x1
1:
DELIVER_PENDING_EXCEPTION
.endm
@@ -999,25 +1029,30 @@
.cfi_restore \xReg2
.endm
- /*
- * Macro to insert read barrier, only used in art_quick_aput_obj.
- * xDest, wDest and xObj are registers, offset is a defined literal such as
- * MIRROR_OBJECT_CLASS_OFFSET. Dest needs both x and w versions of the same register to handle
- * name mismatch between instructions. This macro uses the lower 32b of register when possible.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- */
-.macro READ_BARRIER xDest, wDest, xObj, xTemp, wTemp, offset, number
+ // Helper macros for `art_quick_aput_obj`.
#ifdef USE_READ_BARRIER
-# ifdef USE_BAKER_READ_BARRIER
- ldr \wTemp, [\xObj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
- tbnz \wTemp, #LOCK_WORD_READ_BARRIER_STATE_SHIFT, .Lrb_slowpath\number
+#ifdef USE_BAKER_READ_BARRIER
+.macro BAKER_RB_CHECK_GRAY_BIT_AND_LOAD wDest, xObj, offset, gray_slow_path_label
+ ldr wIP0, [\xObj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ tbnz wIP0, #LOCK_WORD_READ_BARRIER_STATE_SHIFT, \gray_slow_path_label
// False dependency to avoid needing load/load fence.
- add \xObj, \xObj, \xTemp, lsr #32
- ldr \wDest, [\xObj, #\offset] // Heap reference = 32b. This also zero-extends to \xDest.
+ add \xObj, \xObj, xIP0, lsr #32
+ ldr \wDest, [\xObj, #\offset] // Heap reference = 32b; zero-extends to xN.
UNPOISON_HEAP_REF \wDest
- b .Lrb_exit\number
-# endif // USE_BAKER_READ_BARRIER
-.Lrb_slowpath\number:
+.endm
+
+.macro BAKER_RB_LOAD_AND_MARK wDest, xObj, offset, mark_function
+ ldr \wDest, [\xObj, #\offset] // Heap reference = 32b; zero-extends to xN.
+ UNPOISON_HEAP_REF \wDest
+ // Save LR in a register preserved by `art_quick_read_barrier_mark_regNN`
+ // and unused by the `art_quick_aput_obj`.
+ mov x5, lr
+ bl \mark_function
+ mov lr, x5 // Restore LR.
+.endm
+#else // USE_BAKER_READ_BARRIER
+ .extern artReadBarrierSlow
+.macro READ_BARRIER_SLOW xDest, wDest, xObj, offset
// Store registers used in art_quick_aput_obj (x0-x4, LR), stack is 16B aligned.
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 48
SAVE_TWO_REGS x2, x3, 16
@@ -1042,41 +1077,44 @@
POP_REG_NE x4, 32, \xDest
RESTORE_REG xLR, 40
DECREASE_FRAME 48
-.Lrb_exit\number:
-#else
- ldr \wDest, [\xObj, #\offset] // Heap reference = 32b. This also zero-extends to \xDest.
- UNPOISON_HEAP_REF \wDest
-#endif // USE_READ_BARRIER
.endm
+#endif // USE_BAKER_READ_BARRIER
+#endif // USE_READ_BARRIER
-#ifdef USE_READ_BARRIER
- .extern artReadBarrierSlow
-#endif
ENTRY art_quick_aput_obj
- cbz x2, .Ldo_aput_null
- READ_BARRIER x3, w3, x0, x3, w3, MIRROR_OBJECT_CLASS_OFFSET, 0 // Heap reference = 32b
- // This also zero-extends to x3
- READ_BARRIER x3, w3, x3, x4, w4, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, 1 // Heap reference = 32b
- // This also zero-extends to x3
- READ_BARRIER x4, w4, x2, x4, w4, MIRROR_OBJECT_CLASS_OFFSET, 2 // Heap reference = 32b
- // This also zero-extends to x4
+ cbz x2, .Laput_obj_null
+#if defined(USE_READ_BARRIER) && !defined(USE_BAKER_READ_BARRIER)
+ READ_BARRIER_SLOW x3, w3, x0, MIRROR_OBJECT_CLASS_OFFSET
+ READ_BARRIER_SLOW x3, w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
+ READ_BARRIER_SLOW x4, w4, x2, MIRROR_OBJECT_CLASS_OFFSET
+#else // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
+#ifdef USE_READ_BARRIER
+ cbnz wMR, .Laput_obj_gc_marking
+#endif // USE_READ_BARRIER
+ ldr w3, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b; zero-extends to x3.
+ UNPOISON_HEAP_REF w3
+ ldr w3, [x3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Heap reference = 32b; zero-extends to x3.
+ UNPOISON_HEAP_REF w3
+ ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b; zero-extends to x4.
+ UNPOISON_HEAP_REF w4
+#endif // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
cmp w3, w4 // value's type == array's component type - trivial assignability
- bne .Lcheck_assignability
-.Ldo_aput:
+ bne .Laput_obj_check_assignability
+.Laput_obj_store:
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
- // "Compress" = do nothing
POISON_HEAP_REF w2
- str w2, [x3, x1, lsl #2] // Heap reference = 32b
+ str w2, [x3, x1, lsl #2] // Heap reference = 32b.
ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
lsr x0, x0, #CARD_TABLE_CARD_SHIFT
strb w3, [x3, x0]
ret
-.Ldo_aput_null:
+
+.Laput_obj_null:
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
- // "Compress" = do nothing
- str w2, [x3, x1, lsl #2] // Heap reference = 32b
+ str w2, [x3, x1, lsl #2] // Heap reference = 32b.
ret
-.Lcheck_assignability:
+
+.Laput_obj_check_assignability:
// Store arguments and link register
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32
SAVE_TWO_REGS x2, xLR, 16
@@ -1087,7 +1125,7 @@
bl artIsAssignableFromCode
// Check for exception
- cbz x0, .Lthrow_array_store_exception
+ cbz x0, .Laput_obj_throw_array_store_exception
// Restore
.cfi_remember_state
@@ -1095,23 +1133,56 @@
RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
- // "Compress" = do nothing
POISON_HEAP_REF w2
- str w2, [x3, x1, lsl #2] // Heap reference = 32b
+ str w2, [x3, x1, lsl #2] // Heap reference = 32b.
ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
lsr x0, x0, #CARD_TABLE_CARD_SHIFT
strb w3, [x3, x0]
ret
CFI_RESTORE_STATE_AND_DEF_CFA sp, 32
-.Lthrow_array_store_exception:
+
+.Laput_obj_throw_array_store_exception:
RESTORE_TWO_REGS x2, xLR, 16
RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ .cfi_remember_state
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
mov x1, x2 // Pass value.
mov x2, xSELF // Pass Thread::Current.
bl artThrowArrayStoreException // (Object*, Object*, Thread*).
- brk 0 // Unreached.
+ brk 0 // Unreachable.
+
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, 0
+.Laput_obj_gc_marking:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ w3, x0, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_array_class
+.Laput_obj_mark_array_class_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, .Laput_obj_mark_array_element
+.Laput_obj_mark_array_element_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ w4, x2, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_object_class
+.Laput_obj_mark_object_class_continue:
+ cmp w3, w4 // value's type == array's component type - trivial assignability
+ bne .Laput_obj_check_assignability
+ b .Laput_obj_store
+
+.Laput_obj_mark_array_class:
+ BAKER_RB_LOAD_AND_MARK w3, x0, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_class_continue
+
+.Laput_obj_mark_array_element:
+ BAKER_RB_LOAD_AND_MARK \
+ w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_element_continue
+
+.Laput_obj_mark_object_class:
+ BAKER_RB_LOAD_AND_MARK w4, x2, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg04
+ b .Laput_obj_mark_object_class_continue
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
END art_quick_aput_obj
// Macro to facilitate adding new allocation entrypoints.
@@ -1214,11 +1285,7 @@
mov x1, xSELF // pass Thread::Current
bl \entrypoint // (int32_t index, Thread* self)
cbz w0, 1f // If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
- REFRESH_MARKING_REGISTER
- ret // return
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_X0 x1, /* is_ref= */ 1
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END \name
@@ -1228,13 +1295,14 @@
ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
- cbz w0, 1f // result zero branch over
- ret // return
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+ cbz w0, 1f // result zero branch over
+ DEOPT_OR_RETURN x1, /*is_ref=*/1 // check for deopt or return
1:
DELIVER_PENDING_EXCEPTION
.endm
+
/*
* Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on
* failure.
@@ -1256,21 +1324,21 @@
// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc.
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
@@ -1410,7 +1478,7 @@
bl \cxx_name
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \c_name
.endm
@@ -1439,10 +1507,6 @@
str x5, [xSELF, #THREAD_LOCAL_OBJECTS_OFFSET]
POISON_HEAP_REF w0
str w0, [x4, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
- // Fence. This is "ish" not "ishst" so
- // that the code after this allocation
- // site will see the right values in
- // the fields of the class.
mov x0, x4
// No barrier. The class is already observably initialized (otherwise the fast
// path size check above would fail) and new-instance allocations are protected
@@ -1465,7 +1529,7 @@
bl \entrypoint // (mirror::Class*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1510,7 +1574,6 @@
POISON_HEAP_REF \wClass
str \wClass, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
str \wCount, [x0, #MIRROR_ARRAY_LENGTH_OFFSET] // Store the array length.
- // Fence.
// new-array is special. The class is loaded and immediately goes to the Initialized state
// before it is published. Therefore the only fence needed is for the publication of the object.
// See ClassLinker::CreateArrayClass() for more details.
@@ -1539,7 +1602,7 @@
bl \entrypoint
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1837,6 +1900,11 @@
.cfi_remember_state
.cfi_def_cfa_register sp
+ LOAD_RUNTIME_INSTANCE x1
+ ldrb w1, [x1, #RUN_EXIT_HOOKS_OFFSET_FROM_RUNTIME_INSTANCE]
+ cbnz w1, .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame.
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
@@ -1845,6 +1913,12 @@
fmov d0, x0
ret
+.Lcall_method_exit_hook:
+ fmov d0, x0
+ mov x4, FRAME_SIZE_SAVE_REFS_AND_ARGS
+ bl art_quick_method_exit_hook
+ b .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA x28, FRAME_SIZE_SAVE_REFS_AND_ARGS
.Lexception_in_native:
@@ -1889,77 +1963,6 @@
*/
ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
-
-//
-// Instrumentation-related stubs
-//
- .extern artInstrumentationMethodEntryFromCode
-ENTRY art_quick_instrumentation_entry
- SETUP_SAVE_REFS_AND_ARGS_FRAME
-
- mov x20, x0 // Preserve method reference in a callee-save.
-
- mov x2, xSELF
- mov x3, sp // Pass SP
- bl artInstrumentationMethodEntryFromCode // (Method*, Object*, Thread*, SP)
-
- mov xIP0, x0 // x0 = result of call.
- mov x0, x20 // Reload method reference.
-
- RESTORE_SAVE_REFS_AND_ARGS_FRAME // Note: will restore xSELF
- REFRESH_MARKING_REGISTER
- cbz xIP0, 1f // Deliver the pending exception if method is null.
- adr xLR, art_quick_instrumentation_exit
- br xIP0 // Tail-call method with lr set to art_quick_instrumentation_exit.
-
-1:
- DELIVER_PENDING_EXCEPTION
-END art_quick_instrumentation_entry
-
- .extern artInstrumentationMethodExitFromCode
-ENTRY art_quick_instrumentation_exit
- mov xLR, #0 // Clobber LR for later checks.
- SETUP_SAVE_EVERYTHING_FRAME
-
- add x3, sp, #16 // Pass floating-point result pointer, in kSaveEverything frame.
- add x2, sp, #272 // Pass integer result pointer, in kSaveEverything frame.
- mov x1, sp // Pass SP.
- mov x0, xSELF // Pass Thread.
- bl artInstrumentationMethodExitFromCode // (Thread*, SP, gpr_res*, fpr_res*)
-
- cbz x0, .Ldo_deliver_instrumentation_exception
- .cfi_remember_state
- // Handle error
- cbnz x1, .Ldeoptimize
- // Normal return.
- str x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 8]
- // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- REFRESH_MARKING_REGISTER
- br lr
-.Ldo_deliver_instrumentation_exception:
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-.Ldeoptimize:
- str x1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 8]
- // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- // Jump to art_quick_deoptimize.
- b art_quick_deoptimize
-END art_quick_instrumentation_exit
-
- /*
- * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
- * will long jump to the upcall with a special exception of -1.
- */
- .extern artDeoptimize
-ENTRY art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME
- mov x0, xSELF // Pass thread.
- bl artDeoptimize // (Thread*)
- brk 0
-END art_quick_deoptimize
-
/*
* Compiled code has requested that we deoptimize into the interpreter. The deoptimization
* will long jump to the upcall with a special exception of -1.
@@ -2102,7 +2105,7 @@
bl artStringBuilderAppend // (uint32_t, const unit32_t*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END art_quick_string_builder_append
/*
@@ -2111,15 +2114,10 @@
* `wreg` (corresponding to X register `xreg`), saving and restoring
* all caller-save registers.
*
- * If `wreg` is different from `w0`, the generated function follows a
- * non-standard runtime calling convention:
- * - register `wreg` is used to pass the (sole) argument of this
- * function (instead of W0);
- * - register `wreg` is used to return the result of this function
- * (instead of W0);
- * - W0 is treated like a normal (non-argument) caller-save register;
- * - everything else is the same as in the standard runtime calling
- * convention (e.g. standard callee-save registers are preserved).
+ * The generated function follows a non-standard runtime calling convention:
+ * - register `reg` (which may be different from W0) is used to pass the (sole) argument,
+ * - register `reg` (which may be different from W0) is used to return the result,
+ * - all other registers are callee-save (the values they hold are preserved).
*/
.macro READ_BARRIER_MARK_REG name, wreg, xreg
ENTRY \name
@@ -2598,7 +2596,8 @@
ldr x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // pass ArtMethod*
mov x1, xSELF // pass Thread::Current
- bl artMethodEntryHook // (ArtMethod*, Thread*)
+ mov x2, sp // pass SP
+ bl artMethodEntryHook // (ArtMethod*, Thread*, SP)
RESTORE_SAVE_EVERYTHING_FRAME // Note: will restore xSELF
REFRESH_MARKING_REGISTER
@@ -2609,21 +2608,16 @@
ENTRY art_quick_method_exit_hook
SETUP_SAVE_EVERYTHING_FRAME
+ // frame_size is passed from JITed code in x4
add x3, sp, #16 // floating-point result ptr in kSaveEverything frame
add x2, sp, #272 // integer result ptr in kSaveEverything frame
- ldr x1, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // ArtMethod*
+ add x1, sp, #FRAME_SIZE_SAVE_EVERYTHING // ArtMethod**
mov x0, xSELF // Thread::Current
- bl artMethodExitHook // (Thread*, ArtMethod*, gpr_res*, fpr_res*)
-
- .cfi_remember_state
- cbnz x0, .Ldo_deliver_instrumentation_exception_exit // Handle exception
+ bl artMethodExitHook // (Thread*, ArtMethod**, gpr_res*, fpr_res*,
+ // frame_size)
// Normal return.
RESTORE_SAVE_EVERYTHING_FRAME
REFRESH_MARKING_REGISTER
ret
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
END art_quick_method_exit_hook
-
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index 32888ed..5d4b24b 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -16,27 +16,27 @@
.macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix
// Called by managed code to allocate an object of a resolved class.
-ONE_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate an object of an initialized class.
-ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate an object when the caller doesn't know whether it has access
// to the created type.
-ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string if it could not be removed by any optimizations
-ONE_ARG_DOWNCALL art_quick_alloc_string_object\c_suffix, artAllocStringObject\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_string_object\c_suffix, artAllocStringObject\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate an array of a resolve class.
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string from bytes
-FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string from chars
-THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars\c_suffix, artAllocStringFromCharsFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars\c_suffix, artAllocStringFromCharsFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string from string
-ONE_ARG_DOWNCALL art_quick_alloc_string_from_string\c_suffix, artAllocStringFromStringFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_string_from_string\c_suffix, artAllocStringFromStringFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
.endm
.macro GENERATE_ALL_ALLOC_ENTRYPOINTS
@@ -58,29 +58,29 @@
// GENERATE_ALL_ALLOC_ENTRYPOINTS for selectively implementing allocation fast paths in
// hand-written assembly.
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_string_object ## c_suffix, artAllocStringObject ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_string_object ## c_suffix, artAllocStringObject ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
- FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
- THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars ## c_suffix, artAllocStringFromCharsFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars ## c_suffix, artAllocStringFromCharsFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_string_from_string ## c_suffix, artAllocStringFromStringFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_string_from_string ## c_suffix, artAllocStringFromStringFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
.macro GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR
// This is to be separately defined for each architecture to allow a hand-written assembly fast path.
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 458a69b..759cd9a 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -961,7 +961,6 @@
TEST_F(StubTest, StringCompareTo) {
- TEST_DISABLED_FOR_STRING_COMPRESSION();
// There is no StringCompareTo runtime entrypoint for __arm__ or __aarch64__.
#if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__))
// TODO: Check the "Unresolved" allocation stubs
@@ -1919,74 +1918,74 @@
// TODO: Exercise the ReadBarrierMarkRegX entry points.
TEST_F(StubTest, ReadBarrier) {
-#if defined(ART_USE_READ_BARRIER) && (defined(__i386__) || defined(__arm__) || \
- defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)))
- Thread* self = Thread::Current();
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) ||\
+ (defined(__x86_64__) && !defined(__APPLE__))
+ if (gUseReadBarrier) {
+ Thread* self = Thread::Current();
- const uintptr_t readBarrierSlow = StubTest::GetEntrypoint(self, kQuickReadBarrierSlow);
+ const uintptr_t readBarrierSlow = StubTest::GetEntrypoint(self, kQuickReadBarrierSlow);
- // Create an object
- ScopedObjectAccess soa(self);
- // garbage is created during ClassLinker::Init
+ // Create an object
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> c(
- hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> c(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- // Build an object instance
- Handle<mirror::Object> obj(hs.NewHandle(c->AllocObject(soa.Self())));
+ // Build an object instance
+ Handle<mirror::Object> obj(hs.NewHandle(c->AllocObject(soa.Self())));
- EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_FALSE(self->IsExceptionPending());
- size_t result = Invoke3(0U, reinterpret_cast<size_t>(obj.Get()),
- mirror::Object::ClassOffset().SizeValue(), readBarrierSlow, self);
+ size_t result = Invoke3(0U, reinterpret_cast<size_t>(obj.Get()),
+ mirror::Object::ClassOffset().SizeValue(), readBarrierSlow, self);
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
- mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
- EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
-
- // Tests done.
-#else
+ EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
+ mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
+ EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
+ return;
+ }
+#endif
LOG(INFO) << "Skipping read_barrier_slow";
// Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping read_barrier_slow" << std::endl;
-#endif
}
TEST_F(StubTest, ReadBarrierForRoot) {
-#if defined(ART_USE_READ_BARRIER) && (defined(__i386__) || defined(__arm__) || \
- defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)))
- Thread* self = Thread::Current();
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) ||\
+ (defined(__x86_64__) && !defined(__APPLE__))
+ if (gUseReadBarrier) {
+ Thread* self = Thread::Current();
- const uintptr_t readBarrierForRootSlow =
- StubTest::GetEntrypoint(self, kQuickReadBarrierForRootSlow);
+ const uintptr_t readBarrierForRootSlow =
+ StubTest::GetEntrypoint(self, kQuickReadBarrierForRootSlow);
- // Create an object
- ScopedObjectAccess soa(self);
- // garbage is created during ClassLinker::Init
+ // Create an object
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
- StackHandleScope<1> hs(soa.Self());
+ StackHandleScope<1> hs(soa.Self());
- Handle<mirror::String> obj(
- hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
+ Handle<mirror::String> obj(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
- EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_FALSE(self->IsExceptionPending());
- GcRoot<mirror::Class> root(GetClassRoot<mirror::String>());
- size_t result = Invoke3(reinterpret_cast<size_t>(&root), 0U, 0U, readBarrierForRootSlow, self);
+ GcRoot<mirror::Class> root(GetClassRoot<mirror::String>());
+ size_t result = Invoke3(reinterpret_cast<size_t>(&root), 0U, 0U, readBarrierForRootSlow, self);
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
- mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
- EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
-
- // Tests done.
-#else
+ EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
+ mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
+ EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
+ return;
+ }
+#endif
LOG(INFO) << "Skipping read_barrier_for_root_slow";
// Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping read_barrier_for_root_slow" << std::endl;
-#endif
}
} // namespace art
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index 737d736..f688933 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -25,5 +25,7 @@
#define FRAME_SIZE_SAVE_EVERYTHING (48 + 64)
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_EAX_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index 3a08ec5..c485f0d 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -25,6 +25,7 @@
#include "base/logging.h" // For VLOG.
#include "base/macros.h"
#include "base/safe_copy.h"
+#include "oat_quick_method_header.h"
#include "runtime_globals.h"
#include "thread-current-inl.h"
@@ -77,30 +78,18 @@
// Get the size of an instruction in bytes.
// Return 0 if the instruction is not handled.
-static uint32_t GetInstructionSize(const uint8_t* pc) {
- // Don't segfault if pc points to garbage.
- char buf[15]; // x86/x86-64 have a maximum instruction length of 15 bytes.
- ssize_t bytes = SafeCopy(buf, pc, sizeof(buf));
-
- if (bytes == 0) {
- // Nothing was readable.
- return 0;
- }
-
- if (bytes == -1) {
- // SafeCopy not supported, assume that the entire range is readable.
- bytes = 16;
- } else {
- pc = reinterpret_cast<uint8_t*>(buf);
- }
-
-#define INCREMENT_PC() \
- do { \
- pc++; \
- if (pc - startpc > bytes) { \
- return 0; \
- } \
+static uint32_t GetInstructionSize(const uint8_t* pc, size_t bytes) {
+#define FETCH_OR_SKIP_BYTE(assignment) \
+ do { \
+ if (bytes == 0u) { \
+ return 0u; \
+ } \
+ (assignment); \
+ ++pc; \
+ --bytes; \
} while (0)
+#define FETCH_BYTE(var) FETCH_OR_SKIP_BYTE((var) = *pc)
+#define SKIP_BYTE() FETCH_OR_SKIP_BYTE((void)0)
#if defined(__x86_64)
const bool x86_64 = true;
@@ -110,8 +99,8 @@
const uint8_t* startpc = pc;
- uint8_t opcode = *pc;
- INCREMENT_PC();
+ uint8_t opcode;
+ FETCH_BYTE(opcode);
uint8_t modrm;
bool has_modrm = false;
bool two_byte = false;
@@ -143,8 +132,7 @@
// Group 4
case 0x67:
- opcode = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(opcode);
prefix_present = true;
break;
}
@@ -154,15 +142,13 @@
}
if (x86_64 && opcode >= 0x40 && opcode <= 0x4f) {
- opcode = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(opcode);
}
if (opcode == 0x0f) {
// Two byte opcode
two_byte = true;
- opcode = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(opcode);
}
bool unhandled_instruction = false;
@@ -175,8 +161,7 @@
case 0xb7:
case 0xbe: // movsx
case 0xbf:
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
break;
default:
@@ -195,32 +180,28 @@
case 0x3c:
case 0x3d:
case 0x85: // test.
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
break;
case 0x80: // group 1, byte immediate.
case 0x83:
case 0xc6:
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
immediate_size = 1;
break;
case 0x81: // group 1, word immediate.
case 0xc7: // mov
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
immediate_size = operand_size_prefix ? 2 : 4;
break;
case 0xf6:
case 0xf7:
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
switch ((modrm >> 3) & 7) { // Extract "reg/opcode" from "modr/m".
case 0: // test
@@ -255,7 +236,7 @@
// Check for SIB.
if (mod != 3U /* 0b11 */ && (modrm & 7U /* 0b111 */) == 4) {
- INCREMENT_PC(); // SIB
+ SKIP_BYTE(); // SIB
}
switch (mod) {
@@ -271,86 +252,79 @@
pc += displacement_size + immediate_size;
VLOG(signals) << "x86 instruction length calculated as " << (pc - startpc);
- if (pc - startpc > bytes) {
- return 0;
- }
return pc - startpc;
+
+#undef SKIP_BYTE
+#undef FETCH_BYTE
+#undef FETCH_OR_SKIP_BYTE
}
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow) {
+uintptr_t FaultManager::GetFaultPc(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context) {
struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
- *out_sp = static_cast<uintptr_t>(uc->CTX_ESP);
- VLOG(signals) << "sp: " << std::hex << *out_sp;
- if (*out_sp == 0) {
- return;
+ if (uc->CTX_ESP == 0) {
+ VLOG(signals) << "Missing SP";
+ return 0u;
}
+ return uc->CTX_EIP;
+}
- // In the case of a stack overflow, the stack is not valid and we can't
- // get the method from the top of the stack. However it's in EAX(x86)/RDI(x86_64).
- uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(siginfo->si_addr);
- uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
-#if defined(__x86_64__)
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kX86_64));
-#else
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kX86));
-#endif
- if (overflow_addr == fault_addr) {
- *out_method = reinterpret_cast<ArtMethod*>(uc->CTX_METHOD);
- *out_is_stack_overflow = true;
- } else {
- // The method is at the top of the stack.
- *out_method = *reinterpret_cast<ArtMethod**>(*out_sp);
- *out_is_stack_overflow = false;
- }
-
- uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
- VLOG(signals) << HexDump(pc, 32, true, "PC ");
-
- if (pc == nullptr) {
- // Somebody jumped to 0x0. Definitely not ours, and will definitely segfault below.
- *out_method = nullptr;
- return;
- }
-
- uint32_t instr_size = GetInstructionSize(pc);
- if (instr_size == 0) {
- // Unknown instruction, tell caller it's not ours.
- *out_method = nullptr;
- return;
- }
- *out_return_pc = reinterpret_cast<uintptr_t>(pc + instr_size);
+uintptr_t FaultManager::GetFaultSp(void* context) {
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ return uc->CTX_ESP;
}
bool NullPointerHandler::Action(int, siginfo_t* sig, void* context) {
- if (!IsValidImplicitCheck(sig)) {
- return false;
- }
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
- uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP);
-
- uint32_t instr_size = GetInstructionSize(pc);
- if (instr_size == 0) {
- // Unknown instruction, can't really happen.
+ uintptr_t fault_address = reinterpret_cast<uintptr_t>(sig->si_addr);
+ if (!IsValidFaultAddress(fault_address)) {
return false;
}
- // We need to arrange for the signal handler to return to the null pointer
- // exception generator. The return address must be the address of the
- // next instruction (this instruction + instruction size). The return address
- // is on the stack at the top address of the current frame.
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ ArtMethod** sp = reinterpret_cast<ArtMethod**>(uc->CTX_ESP);
+ ArtMethod* method = *sp;
+ if (!IsValidMethod(method)) {
+ return false;
+ }
- // Push the return address and fault address onto the stack.
- uintptr_t retaddr = reinterpret_cast<uintptr_t>(pc + instr_size);
- uintptr_t* next_sp = reinterpret_cast<uintptr_t*>(sp - 2 * sizeof(uintptr_t));
- next_sp[1] = retaddr;
- next_sp[0] = reinterpret_cast<uintptr_t>(sig->si_addr);
+ // For null checks in compiled code we insert a stack map that is immediately
+ // after the load/store instruction that might cause the fault and we need to
+ // pass the return PC to the handler. For null checks in Nterp, we similarly
+ // need the return PC to recognize that this was a null check in Nterp, so
+ // that the handler can get the needed data from the Nterp frame.
+
+ // Note: Allowing nested faults if `IsValidMethod()` returned a false positive.
+ // Note: The `ArtMethod::GetOatQuickMethodHeader()` can acquire locks, which is
+ // essentially unsafe in a signal handler, but we allow that here just like in
+ // `NullPointerHandler::IsValidReturnPc()`. For more details see comments there.
+ uintptr_t pc = uc->CTX_EIP;
+ const OatQuickMethodHeader* method_header = method->GetOatQuickMethodHeader(pc);
+ if (method_header == nullptr) {
+ VLOG(signals) << "No method header.";
+ return false;
+ }
+ const uint8_t* pc_ptr = reinterpret_cast<const uint8_t*>(pc);
+ size_t offset = pc_ptr - method_header->GetCode();
+ size_t code_size = method_header->GetCodeSize();
+ CHECK_LT(offset, code_size);
+ size_t max_instr_size = code_size - offset;
+ uint32_t instr_size = GetInstructionSize(pc_ptr, max_instr_size);
+ if (instr_size == 0u) {
+ // Unknown instruction (can't really happen) or not enough bytes until end of method code.
+ return false;
+ }
+
+ uintptr_t return_pc = reinterpret_cast<uintptr_t>(pc + instr_size);
+ if (!IsValidReturnPc(sp, return_pc)) {
+ return false;
+ }
+
+ // Push the return PC and fault address onto the stack.
+ uintptr_t* next_sp = reinterpret_cast<uintptr_t*>(sp) - 2;
+ next_sp[1] = return_pc;
+ next_sp[0] = fault_address;
uc->CTX_ESP = reinterpret_cast<uintptr_t>(next_sp);
+ // Arrange for the signal handler to return to the NPE entrypoint.
uc->CTX_EIP = reinterpret_cast<uintptr_t>(
art_quick_throw_null_pointer_exception_from_signal);
VLOG(signals) << "Generating null pointer exception";
@@ -385,7 +359,7 @@
#endif
uint8_t checkinst2[] = {0x85, 0x00};
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP);
@@ -441,7 +415,7 @@
// address for the previous method is on the stack at ESP.
bool StackOverflowHandler::Action(int, siginfo_t* info, void* context) {
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
uintptr_t sp = static_cast<uintptr_t>(uc->CTX_ESP);
uintptr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr);
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index 325f47f..f11aca9 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -44,6 +44,9 @@
"atom",
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
"default",
};
@@ -52,24 +55,36 @@
"atom",
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_sse4_1[] = {
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_sse4_2[] = {
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_popcnt[] = {
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_avx[] = {
diff --git a/runtime/arch/x86/instruction_set_features_x86_test.cc b/runtime/arch/x86/instruction_set_features_x86_test.cc
index ce8e9f4..c50360a 100644
--- a/runtime/arch/x86/instruction_set_features_x86_test.cc
+++ b/runtime/arch/x86/instruction_set_features_x86_test.cc
@@ -110,6 +110,81 @@
EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
}
+TEST(X86InstructionSetFeaturesTest, X86FeaturesFromGoldmontVariant) {
+ // Build features for a 32-bit x86 goldmont processor.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> x86_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86, "goldmont", &error_msg));
+ ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_features->GetInstructionSet(), InstructionSet::kX86);
+ EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_features->AsBitmap(), 39U);
+
+ // Build features for a 64-bit x86-64 goldmont processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "goldmont", &error_msg));
+ ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_64_features->GetInstructionSet(), InstructionSet::kX86_64);
+ EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_64_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
+
+ EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+}
+
+TEST(X86InstructionSetFeaturesTest, X86FeaturesFromGoldmontPlusVariant) {
+ // Build features for a 32-bit x86 goldmont-plus processor.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> x86_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86, "goldmont-plus", &error_msg));
+ ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_features->GetInstructionSet(), InstructionSet::kX86);
+ EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_features->AsBitmap(), 39U);
+
+ // Build features for a 64-bit x86-64 goldmont-plus processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "goldmont-plus", &error_msg));
+ ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_64_features->GetInstructionSet(), InstructionSet::kX86_64);
+ EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_64_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
+
+ EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+}
+
+TEST(X86InstructionSetFeaturesTest, X86FeaturesFromTremontVariant) {
+ // Build features for a 32-bit x86 tremont processor.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> x86_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86, "tremont", &error_msg));
+ ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_features->GetInstructionSet(), InstructionSet::kX86);
+ EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_features->AsBitmap(), 39U);
+
+ // Build features for a 64-bit x86-64 tremont processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "tremont", &error_msg));
+ ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_64_features->GetInstructionSet(), InstructionSet::kX86_64);
+ EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_64_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
+
+ EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+}
+
TEST(X86InstructionSetFeaturesTest, X86FeaturesFromKabylakeVariant) {
// Build features for a 32-bit kabylake x86 processor.
std::string error_msg;
diff --git a/runtime/arch/x86/jni_entrypoints_x86.S b/runtime/arch/x86/jni_entrypoints_x86.S
index d827509..c7cf856 100644
--- a/runtime/arch/x86/jni_entrypoints_x86.S
+++ b/runtime/arch/x86/jni_entrypoints_x86.S
@@ -98,7 +98,7 @@
// for @FastNative or @CriticalNative.
movl (%esp), %eax // Thread* self
movl THREAD_TOP_QUICK_FRAME_OFFSET(%eax), %eax // uintptr_t tagged_quick_frame
- andl LITERAL(0xfffffffe), %eax // ArtMethod** sp
+ andl LITERAL(TAGGED_JNI_SP_MASK_TOGGLED32), %eax // ArtMethod** sp
movl (%eax), %eax // ArtMethod* method
testl LITERAL(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE), \
ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
@@ -286,6 +286,12 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, fs:THREAD_SELF_OFFSET
/*
+ * Trampoline to `artJniMethodEntryHook` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
+ art_jni_method_entry_hook, artJniMethodEntryHook, fs:THREAD_SELF_OFFSET
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 7f1311c..dcf81d5 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -794,12 +794,9 @@
call CALLVAR(cxx_name) // cxx_name(arg1, Thread*)
addl MACRO_LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
- testl %eax, %eax // If result is null, deliver the OOME.
+ testl %eax, %eax // If result is null deliver pending exception
jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX // restore frame up to return address
- ret // return
- CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_EAX ebx, /* is_ref= */1 // Check for deopt
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END_FUNCTION VAR(c_name)
@@ -809,18 +806,72 @@
ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
END_MACRO
-MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
- testl %eax, %eax // eax == 0 ?
- jz 1f // if eax == 0 goto 1
- ret // return
-1: // deliver exception on current thread
+MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER)
+ testl %eax, %eax // eax == 0 ?
+ jz 1f // if eax == 0 goto 1
+ DEOPT_OR_RETURN ebx, /*is_ref=*/1 // check if deopt is required
+1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
+MACRO0(RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION)
+ cmpl MACRO_LITERAL(0),%fs:THREAD_EXCEPTION_OFFSET // exception field == 0 ?
+ jne 1f // if exception field != 0 goto 1
+ DEOPT_OR_RETURN ebx // check if deopt is required
+1: // deliver exception on current thread
+ DELIVER_PENDING_EXCEPTION
+END_MACRO
+
+MACRO2(DEOPT_OR_RETURN, temp, is_ref = 0)
+ cmpl LITERAL(0), %fs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ ret
+2:
+ SETUP_SAVE_EVERYTHING_FRAME \temp
+ subl MACRO_LITERAL(4), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(4)
+ pushl MACRO_LITERAL(\is_ref) // is_ref
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH_ARG eax // result
+ pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current
+ CFI_ADJUST_CFA_OFFSET(4)
+ call SYMBOL(artDeoptimizeIfNeeded)
+ addl LITERAL(16), %esp // pop arguments
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+MACRO2(DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_EAX, temp, is_ref = 0)
+ cmpl LITERAL(0), %fs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ movl %eax, SAVE_EVERYTHING_FRAME_EAX_OFFSET(%esp) // update eax in the frame
+ subl MACRO_LITERAL(4), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(4)
+ pushl MACRO_LITERAL(\is_ref) // is_ref
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH_ARG eax // result
+ pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current
+ CFI_ADJUST_CFA_OFFSET(4)
+ call SYMBOL(artDeoptimizeIfNeeded)
+ addl LITERAL(16), %esp // pop arguments
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+
MACRO0(RETURN_IF_EAX_ZERO)
testl %eax, %eax // eax == 0 ?
jnz 1f // if eax != 0 goto 1
- ret // return
+ DEOPT_OR_RETURN ebx // check if deopt is needed
1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
@@ -927,7 +978,7 @@
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_name)
END_MACRO
@@ -974,7 +1025,7 @@
addl LITERAL(16), %esp
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_MACRO
MACRO2(ART_QUICK_ALLOC_OBJECT_TLAB, c_name, cxx_name)
@@ -1107,7 +1158,7 @@
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_entrypoint)
END_MACRO
@@ -1254,126 +1305,102 @@
.endif
END_MACRO
- /*
- * Macro to insert read barrier, only used in art_quick_aput_obj.
- * obj_reg and dest_reg are registers, offset is a defined literal such as
- * MIRROR_OBJECT_CLASS_OFFSET.
- * pop_eax is a boolean flag, indicating if eax is popped after the call.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- */
-MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax)
-#ifdef USE_READ_BARRIER
- PUSH eax // save registers used in art_quick_aput_obj
- PUSH ebx
- PUSH edx
- PUSH ecx
- // Outgoing argument set up
- pushl MACRO_LITERAL((RAW_VAR(offset))) // pass offset, double parentheses are necessary
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH RAW_VAR(obj_reg) // pass obj_reg
- PUSH eax // pass ref, just pass eax for now since parameter ref is unused
- call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj_reg, offset)
- // No need to unpoison return value in eax, artReadBarrierSlow() would do the unpoisoning.
- .ifnc RAW_VAR(dest_reg), eax
- movl %eax, REG_VAR(dest_reg) // save loaded ref in dest_reg
- .endif
- addl MACRO_LITERAL(12), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-12)
- POP_REG_NE ecx, RAW_VAR(dest_reg) // Restore args except dest_reg
- POP_REG_NE edx, RAW_VAR(dest_reg)
- POP_REG_NE ebx, RAW_VAR(dest_reg)
- .ifc RAW_VAR(pop_eax), true
- POP_REG_NE eax, RAW_VAR(dest_reg)
- .endif
-#else
- movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg)
- UNPOISON_HEAP_REF RAW_VAR(dest_reg)
-#endif // USE_READ_BARRIER
-END_MACRO
-
DEFINE_FUNCTION art_quick_aput_obj
test %edx, %edx // store of null
- jz .Ldo_aput_null
- READ_BARRIER eax, MIRROR_OBJECT_CLASS_OFFSET, ebx, true
- READ_BARRIER ebx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ebx, true
- // value's type == array's component type - trivial assignability
-#if defined(USE_READ_BARRIER)
- READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, false
- cmpl %eax, %ebx
- POP eax // restore eax from the push in the beginning of READ_BARRIER macro
- // This asymmetric push/pop saves a push of eax and maintains stack alignment.
-#elif defined(USE_HEAP_POISONING)
- PUSH eax // save eax
- movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
- UNPOISON_HEAP_REF eax
- cmpl %eax, %ebx
- POP eax // restore eax
-#else
- cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx
-#endif
- jne .Lcheck_assignability
-.Ldo_aput:
+ jz .Laput_obj_null
+ movl MIRROR_OBJECT_CLASS_OFFSET(%eax), %ebx
+ UNPOISON_HEAP_REF ebx
+#ifdef USE_READ_BARRIER
+ cmpl LITERAL(0), %fs:THREAD_IS_GC_MARKING_OFFSET
+ jnz .Laput_obj_gc_marking
+#endif // USE_READ_BARRIER
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+ cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx // Both poisoned if heap poisoning is enabled.
+ jne .Laput_obj_check_assignability
+.Laput_obj_store:
POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %eax
movb %dl, (%edx, %eax)
ret
-.Ldo_aput_null:
+
+.Laput_obj_null:
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
ret
-.Lcheck_assignability:
- PUSH eax // save arguments
- PUSH ecx
- PUSH edx
-#if defined(USE_READ_BARRIER)
- subl LITERAL(4), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(4)
- READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, true
- subl LITERAL(4), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH eax // pass arg2 - type of the value to be stored
-#elif defined(USE_HEAP_POISONING)
- subl LITERAL(8), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(8)
+
+.Laput_obj_check_assignability:
+ UNPOISON_HEAP_REF ebx // Unpoison array component type if poisoning is enabled.
+ PUSH_ARG eax // Save `art_quick_aput_obj()` arguments.
+ PUSH_ARG ecx
+ PUSH_ARG edx
+ INCREASE_FRAME 8 // Alignment padding.
+ // Pass arg2 - type of the value to be stored.
+#if defined(USE_HEAP_POISONING)
movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
UNPOISON_HEAP_REF eax
- PUSH eax // pass arg2 - type of the value to be stored
+ PUSH_ARG eax
#else
- subl LITERAL(8), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(8)
- pushl MIRROR_OBJECT_CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored
+ pushl MIRROR_OBJECT_CLASS_OFFSET(%edx)
CFI_ADJUST_CFA_OFFSET(4)
#endif
- PUSH ebx // pass arg1 - component type of the array
+.Laput_obj_check_assignability_call:
+ PUSH_ARG ebx // Pass arg1 - component type of the array.
call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
- addl LITERAL(16), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-16)
+ DECREASE_FRAME 16 // Pop `artIsAssignableFromCode()` arguments
testl %eax, %eax
+ POP_ARG edx // Pop `art_quick_aput_obj()` arguments; flags unaffected.
+ POP_ARG ecx
+ POP_ARG eax
jz .Lthrow_array_store_exception
- POP edx
- POP ecx
- POP eax
POISON_HEAP_REF edx
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // Do the aput.
movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %eax
movb %dl, (%edx, %eax)
ret
- CFI_ADJUST_CFA_OFFSET(12) // 3 POP after the jz for unwinding.
+
.Lthrow_array_store_exception:
- POP edx
- POP ecx
- POP eax
- SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx // save all registers as basis for long jump context
- // Outgoing argument set up
- PUSH eax // alignment padding
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH edx // pass arg2 - value
- PUSH eax // pass arg1 - array
+#ifdef USE_READ_BARRIER
+ CFI_REMEMBER_STATE
+#endif // USE_READ_BARRIER
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx // Save all registers as basis for long jump context.
+ // Outgoing argument set up.
+ PUSH_ARG eax // Alignment padding.
+ PUSH_ARG fs:THREAD_SELF_OFFSET // Pass Thread::Current()
+ PUSH_ARG edx // Pass arg2 - value.
+ PUSH_ARG eax // Pass arg1 - array.
call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*)
UNREACHABLE
+
+#ifdef USE_READ_BARRIER
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, 4
+.Laput_obj_gc_marking:
+ PUSH_ARG eax // Save `art_quick_aput_obj()` arguments.
+ PUSH_ARG ecx // We need to align stack for `art_quick_read_barrier_mark_regNN`
+ PUSH_ARG edx // and use a register (EAX) as a temporary for the object class.
+ call SYMBOL(art_quick_read_barrier_mark_reg03) // Mark EBX.
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+ UNPOISON_HEAP_REF ebx
+ call SYMBOL(art_quick_read_barrier_mark_reg03) // Mark EBX.
+ movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
+ UNPOISON_HEAP_REF eax
+ call SYMBOL(art_quick_read_barrier_mark_reg00) // Mark EAX.
+ cmpl %eax, %ebx
+ jne .Laput_obj_check_assignability_gc_marking
+ POP_ARG edx // Restore `art_quick_aput_obj()` arguments.
+ POP_ARG ecx
+ POP_ARG eax
+ jmp .Laput_obj_store
+
+.Laput_obj_check_assignability_gc_marking:
+ // Prepare arguments in line with `.Laput_obj_check_assignability_call` and jump there.
+ // (EAX, ECX and EDX were already saved in the right stack slots.)
+ INCREASE_FRAME 8 // Alignment padding.
+ PUSH_ARG eax // Pass arg2 - type of the value to be stored.
+ // The arg1 shall be pushed at `.Laput_obj_check_assignability_call`.
+ jmp .Laput_obj_check_assignability_call
+#endif // USE_READ_BARRIER
END_FUNCTION art_quick_aput_obj
DEFINE_FUNCTION art_quick_memcpy
@@ -1501,21 +1528,21 @@
// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc.
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_EAX_ZERO
@@ -1616,7 +1643,7 @@
movl %eax, %edi // remember code pointer in EDI
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
- test %eax, %eax // if code pointer is null goto deliver pending exception
+ test %eax, %eax // if code pointer is null goto deliver the OOME.
jz 1f
RESTORE_SAVE_REFS_AND_ARGS_FRAME_AND_JUMP
1:
@@ -1686,6 +1713,16 @@
CFI_REMEMBER_STATE
CFI_DEF_CFA_REGISTER(esp)
+ // Quick expects the return value to be in xmm0.
+ movd %eax, %xmm0
+ movd %edx, %xmm1
+ punpckldq %xmm1, %xmm0
+
+ LOAD_RUNTIME_INSTANCE ebx
+ cmpb MACRO_LITERAL(0), RUN_EXIT_HOOKS_OFFSET_FROM_RUNTIME_INSTANCE(%ebx)
+ jne .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame.
// Remove space for FPR args and EAX
addl LITERAL(4 + 4 * 8), %esp
@@ -1698,12 +1735,13 @@
POP ebp // Restore callee saves
POP esi
POP edi
- // Quick expects the return value to be in xmm0.
- movd %eax, %xmm0
- movd %edx, %xmm1
- punpckldq %xmm1, %xmm0
ret
+.Lcall_method_exit_hook:
+ movl LITERAL(FRAME_SIZE_SAVE_REFS_AND_ARGS), %ebx
+ call art_quick_method_exit_hook
+ jmp .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA ebp, 64
.Lexception_in_native:
@@ -1751,115 +1789,6 @@
ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
/*
- * Routine that intercepts method calls and returns.
- */
-DEFINE_FUNCTION art_quick_instrumentation_entry
- SETUP_SAVE_REFS_AND_ARGS_FRAME edx
- PUSH eax // Save eax which will be clobbered by the callee-save method.
- subl LITERAL(16), %esp // Align stack (12 bytes) and reserve space for the SP argument
- CFI_ADJUST_CFA_OFFSET(16) // (4 bytes). We lack the scratch registers to calculate the SP
- // right now, so we will just fill it in later.
- pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH ecx // Pass receiver.
- PUSH eax // Pass Method*.
- leal 32(%esp), %eax // Put original SP into eax
- movl %eax, 12(%esp) // set SP
- call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP)
-
- addl LITERAL(28), %esp // Pop arguments upto saved Method*.
- CFI_ADJUST_CFA_OFFSET(-28)
-
- testl %eax, %eax
- jz 1f // Test for null return (indicating exception) and handle it.
-
- movl 60(%esp), %edi // Restore edi.
- movl %eax, 60(%esp) // Place code* over edi, just under return pc.
- SETUP_PC_REL_BASE ebx, .Linstrumentation_entry_pc_rel_base
- leal SYMBOL(art_quick_instrumentation_exit) - .Linstrumentation_entry_pc_rel_base(%ebx), %ebx
- // Place instrumentation exit as return pc. ebx holds the GOT computed on entry.
- movl %ebx, 64(%esp)
- movl 0(%esp), %eax // Restore eax.
- // Restore FPRs (extra 4 bytes of offset due to EAX push at top).
- movsd 8(%esp), %xmm0
- movsd 16(%esp), %xmm1
- movsd 24(%esp), %xmm2
- movsd 32(%esp), %xmm3
-
- // Restore GPRs.
- movl 40(%esp), %ecx // Restore ecx.
- movl 44(%esp), %edx // Restore edx.
- movl 48(%esp), %ebx // Restore ebx.
- movl 52(%esp), %ebp // Restore ebp.
- movl 56(%esp), %esi // Restore esi.
- addl LITERAL(60), %esp // Wind stack back upto code*.
- CFI_ADJUST_CFA_OFFSET(-60)
- ret // Call method (and pop).
-1:
- // Make caller handle exception
- addl LITERAL(4), %esp
- CFI_ADJUST_CFA_OFFSET(-4)
- RESTORE_SAVE_REFS_AND_ARGS_FRAME
- DELIVER_PENDING_EXCEPTION
-END_FUNCTION art_quick_instrumentation_entry
-
-DEFINE_FUNCTION_CUSTOM_CFA art_quick_instrumentation_exit, 0
- pushl LITERAL(0) // Push a fake return PC as there will be none on the stack.
- CFI_ADJUST_CFA_OFFSET(4)
- SETUP_SAVE_EVERYTHING_FRAME ebx
-
- movl %esp, %ecx // Remember SP
- subl LITERAL(8), %esp // Align stack.
- CFI_ADJUST_CFA_OFFSET(8)
- PUSH edx // Save gpr return value. edx and eax need to be together,
- // which isn't the case in kSaveEverything frame.
- PUSH eax
- leal 32(%esp), %eax // Get pointer to fpr_result, in kSaveEverything frame
- movl %esp, %edx // Get pointer to gpr_result
- PUSH eax // Pass fpr_result
- PUSH edx // Pass gpr_result
- PUSH ecx // Pass SP
- pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current.
- CFI_ADJUST_CFA_OFFSET(4)
-
- call SYMBOL(artInstrumentationMethodExitFromCode) // (Thread*, SP, gpr_result*, fpr_result*)
- // Return result could have been changed if it's a reference.
- movl 16(%esp), %ecx
- movl %ecx, (80+32)(%esp)
- addl LITERAL(32), %esp // Pop arguments and grp_result.
- CFI_ADJUST_CFA_OFFSET(-32)
-
- testl %eax, %eax // Check if we returned error.
- jz .Ldo_deliver_instrumentation_exception
- testl %edx, %edx
- jnz .Ldeoptimize
- // Normal return.
- movl %eax, FRAME_SIZE_SAVE_EVERYTHING-4(%esp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- ret
-.Ldeoptimize:
- mov %edx, (FRAME_SIZE_SAVE_EVERYTHING-4)(%esp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- jmp SYMBOL(art_quick_deoptimize)
-.Ldo_deliver_instrumentation_exception:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_instrumentation_exit
-
- /*
- * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
- * will long jump to the upcall with a special exception of -1.
- */
-DEFINE_FUNCTION art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME ebx
- subl LITERAL(12), %esp // Align stack.
- CFI_ADJUST_CFA_OFFSET(12)
- pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
- CFI_ADJUST_CFA_OFFSET(4)
- call SYMBOL(artDeoptimize) // (Thread*)
- UNREACHABLE
-END_FUNCTION art_quick_deoptimize
-
- /*
* Compiled code has requested that we deoptimize into the interpreter. The deoptimization
* will long jump to the interpreter bridge.
*/
@@ -1974,34 +1903,29 @@
SETUP_SAVE_REFS_ONLY_FRAME ebx // save ref containing registers for GC
// Outgoing argument set up
leal FRAME_SIZE_SAVE_REFS_ONLY + __SIZEOF_POINTER__(%esp), %edi // prepare args
- push %eax // push padding
+ push %eax // push padding
CFI_ADJUST_CFA_OFFSET(4)
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
- push %edi // pass args
+ push %edi // pass args
CFI_ADJUST_CFA_OFFSET(4)
- push %eax // pass format
+ push %eax // pass format
CFI_ADJUST_CFA_OFFSET(4)
- call SYMBOL(artStringBuilderAppend) // (uint32_t, const unit32_t*, Thread*)
- addl MACRO_LITERAL(16), %esp // pop arguments
+ call SYMBOL(artStringBuilderAppend) // (uint32_t, const unit32_t*, Thread*)
+ addl MACRO_LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
- RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION art_quick_string_builder_append
// Create a function `name` calling the ReadBarrier::Mark routine,
// getting its argument and returning its result through register
// `reg`, saving and restoring all caller-save registers.
//
-// If `reg` is different from `eax`, the generated function follows a
-// non-standard runtime calling convention:
-// - register `reg` is used to pass the (sole) argument of this function
-// (instead of EAX);
-// - register `reg` is used to return the result of this function
-// (instead of EAX);
-// - EAX is treated like a normal (non-argument) caller-save register;
-// - everything else is the same as in the standard runtime calling
-// convention (e.g. standard callee-save registers are preserved).
+// The generated function follows a non-standard runtime calling convention:
+// - register `reg` (which may differ from EAX) is used to pass the (sole) argument,
+// - register `reg` (which may differ from EAX) is used to return the result,
+// - all other registers are callee-save (the values they hold are preserved).
MACRO2(READ_BARRIER_MARK_REG, name, reg)
DEFINE_FUNCTION VAR(name)
// Null check so that we can load the lock word.
@@ -2313,15 +2237,18 @@
DEFINE_FUNCTION art_quick_method_entry_hook
SETUP_SAVE_EVERYTHING_FRAME edx
mov FRAME_SIZE_SAVE_EVERYTHING(%esp), %eax // Fetch ArtMethod
- subl LITERAL(8), %esp
- CFI_ADJUST_CFA_OFFSET(8)
+ mov %esp, %edx // Store esp before pushing anything on stack.
+ subl LITERAL(4), %esp
+ CFI_ADJUST_CFA_OFFSET(4)
+ push %edx // Pass SP
+ CFI_ADJUST_CFA_OFFSET(4)
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
CFI_ADJUST_CFA_OFFSET(4)
pushl %eax // Pass Method*.
CFI_ADJUST_CFA_OFFSET(4)
- call SYMBOL(artMethodEntryHook) // (Method*, Thread*)
+ call SYMBOL(artMethodEntryHook) // (Method*, Thread*, SP)
addl LITERAL(16), %esp // Pop arguments.
CFI_ADJUST_CFA_OFFSET(-16)
@@ -2331,40 +2258,34 @@
END_FUNCTION art_quick_method_entry_hook
DEFINE_FUNCTION art_quick_method_exit_hook
- SETUP_SAVE_EVERYTHING_FRAME ebx
+ PUSH edi
+ SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED edi
- mov FRAME_SIZE_SAVE_EVERYTHING(%esp), %ebx // Remember ArtMethod*
- subl LITERAL(8), %esp // Align stack.
- CFI_ADJUST_CFA_OFFSET(8)
+ leal FRAME_SIZE_SAVE_EVERYTHING(%esp), %edi // Remember ArtMethod**
+ subl LITERAL(4), %esp // Align stack.
+ CFI_ADJUST_CFA_OFFSET(4)
+
PUSH_ARG edx // Save gpr return value. edx and eax need to be together
// which isn't the case in kSaveEverything frame.
PUSH_ARG eax
movl %esp, %edx // Get pointer to gpr_result
- leal 32(%esp), %eax // Get pointer to fpr_result, in kSaveEverything frame
+ leal 28(%esp), %eax // Get pointer to fpr_result, in kSaveEverything frame
+ PUSH_ARG ebx // push frame_size
PUSH_ARG eax // Pass fpr_result
PUSH_ARG edx // Pass gpr_result
- PUSH_ARG ebx // Pass ArtMethod*
+ PUSH_ARG edi // Pass ArtMethod**
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current.
CFI_ADJUST_CFA_OFFSET(4)
- call SYMBOL(artMethodExitHook) // (Thread*, ArtMethod*, gpr_result*, fpr_result*)
+ call SYMBOL(artMethodExitHook) // (Thread*, ArtMethod**, gpr_result*, fpr_result*,
+ // frame_size)
// Return result could have been changed if it's a reference.
- movl 16(%esp), %ecx
+ movl 20(%esp), %ecx
movl %ecx, (80+32)(%esp)
addl LITERAL(32), %esp // Pop arguments and grp_result.
CFI_ADJUST_CFA_OFFSET(-32)
- cmpl LITERAL(1), %eax // Check if we returned error.
- CFI_REMEMBER_STATE
- je .Ldo_deliver_instrumentation_exception_exit
-
// Normal return.
RESTORE_SAVE_EVERYTHING_FRAME
ret
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
END_FUNCTION art_quick_method_exit_hook
-
-
-
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index bfec8c0..a2aa686 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -485,11 +485,10 @@
END_MACRO
MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION)
- movq %gs:THREAD_EXCEPTION_OFFSET, %rcx // get exception field
- testq %rcx, %rcx // rcx == 0 ?
- jnz 1f // if rcx != 0 goto 1
- ret // return
-1: // deliver exception on current thread
+ cmpq MACRO_LITERAL(0), %gs:THREAD_EXCEPTION_OFFSET // compare exception field with 0
+ jne 1f // if exception != 0 goto 1
+ ret // return
+1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h
index 51befbe..e389c78 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.h
+++ b/runtime/arch/x86_64/asm_support_x86_64.h
@@ -25,5 +25,7 @@
#define FRAME_SIZE_SAVE_EVERYTHING (144 + 16*8)
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_RAX_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
#endif // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
diff --git a/runtime/arch/x86_64/jni_entrypoints_x86_64.S b/runtime/arch/x86_64/jni_entrypoints_x86_64.S
index 0d5fa3f..55f01b7 100644
--- a/runtime/arch/x86_64/jni_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/jni_entrypoints_x86_64.S
@@ -118,7 +118,7 @@
// Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
// for @FastNative or @CriticalNative.
movq THREAD_TOP_QUICK_FRAME_OFFSET(%rdi), %rax // uintptr_t tagged_quick_frame
- andq LITERAL(0xfffffffffffffffe), %rax // ArtMethod** sp
+ andq LITERAL(TAGGED_JNI_SP_MASK_TOGGLED64), %rax // ArtMethod** sp
movq (%rax), %rax // ArtMethod* method
testl LITERAL(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE), \
ART_METHOD_ACCESS_FLAGS_OFFSET(%rax)
@@ -400,6 +400,12 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, gs:THREAD_SELF_OFFSET
/*
+ * Trampoline to `artJniMethodEntryHook` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
+ art_jni_method_entry_hook, artJniMethodEntryHook, gs:THREAD_SELF_OFFSET
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 673696c..3703cad 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -731,12 +731,9 @@
movl %eax, %edi // pass the index of the constant as arg0
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
- testl %eax, %eax // If result is null, deliver the OOME.
+ testl %eax, %eax // If result is null, deliver pending exception.
jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address
- ret
- CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_RAX /*is_ref=*/1
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END_FUNCTION VAR(c_name)
@@ -746,18 +743,65 @@
ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
END_MACRO
-MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
+MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER)
testq %rax, %rax // rax == 0 ?
jz 1f // if rax == 0 goto 1
- ret // return
+ DEOPT_OR_RETURN /*is_ref=*/1 // Check if deopt is required
1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
+
+MACRO0(RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION)
+ movq %gs:THREAD_EXCEPTION_OFFSET, %rcx // get exception field
+ testq %rcx, %rcx // rcx == 0 ?
+ jnz 1f // if rcx != 0 goto 1
+ DEOPT_OR_RETURN // Check if deopt is required
+1: // deliver exception on current thread
+ DELIVER_PENDING_EXCEPTION
+END_MACRO
+
+MACRO1(DEOPT_OR_RETURN, is_ref = 0)
+ cmpl LITERAL(0), %gs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ ret
+2:
+ SETUP_SAVE_EVERYTHING_FRAME
+ movq LITERAL(\is_ref), %rdx // pass if result is a reference
+ movq %rax, %rsi // pass the result
+ movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current
+ call SYMBOL(artDeoptimizeIfNeeded)
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+MACRO1(DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_RAX, is_ref = 0)
+ cmpl LITERAL(0), %gs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ movq %rax, SAVE_EVERYTHING_FRAME_RAX_OFFSET(%rsp) // update result in the frame
+ movq LITERAL(\is_ref), %rdx // pass if result is a reference
+ movq %rax, %rsi // pass the result
+ movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current
+ call SYMBOL(artDeoptimizeIfNeeded)
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+
+
MACRO0(RETURN_IF_EAX_ZERO)
testl %eax, %eax // eax == 0 ?
jnz 1f // if eax != 0 goto 1
- ret // return
+ DEOPT_OR_RETURN // Check if we need a deopt
1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
@@ -859,7 +903,7 @@
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_name)
END_MACRO
@@ -931,7 +975,7 @@
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_MACRO
// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be
@@ -1019,7 +1063,7 @@
movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, arg1, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_entrypoint)
END_MACRO
@@ -1163,134 +1207,89 @@
.endif
END_MACRO
- /*
- * Macro to insert read barrier, used in art_quick_aput_obj.
- * obj_reg and dest_reg{32|64} are registers, offset is a defined literal such as
- * MIRROR_OBJECT_CLASS_OFFSET. dest_reg needs two versions to handle the mismatch between
- * 64b PUSH/POP and 32b argument.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- *
- * As with art_quick_aput_obj function, the 64b versions are in comments.
- */
-MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
-#ifdef USE_READ_BARRIER
- PUSH rax // save registers that might be used
- PUSH rdi
- PUSH rsi
- PUSH rdx
- PUSH rcx
- SETUP_FP_CALLEE_SAVE_FRAME
- // Outgoing argument set up
- // movl REG_VAR(ref_reg32), %edi // pass ref, no-op for now since parameter ref is unused
- // // movq REG_VAR(ref_reg64), %rdi
- movl REG_VAR(obj_reg), %esi // pass obj_reg
- // movq REG_VAR(obj_reg), %rsi
- movl MACRO_LITERAL((RAW_VAR(offset))), %edx // pass offset, double parentheses are necessary
- // movq MACRO_LITERAL((RAW_VAR(offset))), %rdx
- call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj_reg, offset)
- // No need to unpoison return value in rax, artReadBarrierSlow() would do the unpoisoning.
- .ifnc RAW_VAR(dest_reg32), eax
- // .ifnc RAW_VAR(dest_reg64), rax
- movl %eax, REG_VAR(dest_reg32) // save loaded ref in dest_reg
- // movq %rax, REG_VAR(dest_reg64)
- .endif
- RESTORE_FP_CALLEE_SAVE_FRAME
- POP_REG_NE rcx, RAW_VAR(dest_reg64) // Restore registers except dest_reg
- POP_REG_NE rdx, RAW_VAR(dest_reg64)
- POP_REG_NE rsi, RAW_VAR(dest_reg64)
- POP_REG_NE rdi, RAW_VAR(dest_reg64)
- POP_REG_NE rax, RAW_VAR(dest_reg64)
-#else
- movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg32)
- // movq RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg64)
- UNPOISON_HEAP_REF RAW_VAR(dest_reg32) // UNPOISON_HEAP_REF only takes a 32b register
-#endif // USE_READ_BARRIER
-END_MACRO
-
DEFINE_FUNCTION art_quick_aput_obj
- testl %edx, %edx // store of null
-// test %rdx, %rdx
- jz .Ldo_aput_null
- READ_BARRIER edi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
- // READ_BARRIER rdi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
- READ_BARRIER ecx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
- // READ_BARRIER rcx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
-#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
- READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax // rax is free.
- // READ_BARRIER rdx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax
- cmpl %eax, %ecx // value's type == array's component type - trivial assignability
-#else
- cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability
-// cmpq MIRROR_CLASS_OFFSET(%rdx), %rcx
-#endif
- jne .Lcheck_assignability
-.Ldo_aput:
+ test %edx, %edx // store of null
+ jz .Laput_obj_null
+ movl MIRROR_OBJECT_CLASS_OFFSET(%rdi), %ecx
+ UNPOISON_HEAP_REF ecx
+#ifdef USE_READ_BARRIER
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jnz .Laput_obj_gc_marking
+#endif // USE_READ_BARRIER
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx
+ cmpl MIRROR_OBJECT_CLASS_OFFSET(%rdx), %ecx // Both poisoned if heap poisoning is enabled.
+ jne .Laput_obj_check_assignability
+.Laput_obj_store:
POISON_HEAP_REF edx
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %edi
-// shrl LITERAL(CARD_TABLE_CARD_SHIFT), %rdi
- movb %dl, (%rdx, %rdi) // Note: this assumes that top 32b of %rdi are zero
+ movb %dl, (%rdx, %rdi)
ret
-.Ldo_aput_null:
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+
+.Laput_obj_null:
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
ret
-.Lcheck_assignability:
- // Save arguments.
- PUSH rdi
- PUSH rsi
- PUSH rdx
+
+.Laput_obj_check_assignability:
+ UNPOISON_HEAP_REF ecx // Unpoison array component type if poisoning is enabled.
+ PUSH_ARG rdi // Save arguments.
+ PUSH_ARG rsi
+ PUSH_ARG rdx
+ movl MIRROR_OBJECT_CLASS_OFFSET(%rdx), %esi // Pass arg2 = value's class.
+ UNPOISON_HEAP_REF esi
+.Laput_obj_check_assignability_call:
+ movl %ecx, %edi // Pass arg1 = array's component type.
SETUP_FP_CALLEE_SAVE_FRAME
-
-#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
- // The load of MIRROR_OBJECT_CLASS_OFFSET(%edx) is redundant, eax still holds the value.
- movl %eax, %esi // Pass arg2 = value's class.
- // movq %rax, %rsi
-#else
- // "Uncompress" = do nothing, as already zero-extended on load.
- movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class.
-#endif
- movq %rcx, %rdi // Pass arg1 = array's component type.
-
call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
-
- // Exception?
- testq %rax, %rax
- jz .Lthrow_array_store_exception
-
- RESTORE_FP_CALLEE_SAVE_FRAME
- // Restore arguments.
- POP rdx
- POP rsi
- POP rdi
-
+ RESTORE_FP_CALLEE_SAVE_FRAME // Resore FP registers.
+ POP_ARG rdx // Restore arguments.
+ POP_ARG rsi
+ POP_ARG rdi
+ testq %rax, %rax // Check for exception.
+ jz .Laput_obj_throw_array_store_exception
POISON_HEAP_REF edx
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %edi
-// shrl LITERAL(CARD_TABLE_CARD_SHIFT), %rdi
- movb %dl, (%rdx, %rdi) // Note: this assumes that top 32b of %rdi are zero
-// movb %dl, (%rdx, %rdi)
+ movb %dl, (%rdx, %rdi)
ret
- CFI_ADJUST_CFA_OFFSET(24 + 4 * 8) // Reset unwind info so following code unwinds.
-.Lthrow_array_store_exception:
- RESTORE_FP_CALLEE_SAVE_FRAME
- // Restore arguments.
- POP rdx
- POP rsi
- POP rdi
+.Laput_obj_throw_array_store_exception:
+#ifdef USE_READ_BARRIER
+ CFI_REMEMBER_STATE
+#endif // USE_READ_BARRIER
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // Save all registers as basis for long jump context.
-
// Outgoing argument set up.
movq %rdx, %rsi // Pass arg 2 = value.
movq %gs:THREAD_SELF_OFFSET, %rdx // Pass arg 3 = Thread::Current().
// Pass arg 1 = array.
call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*)
UNREACHABLE
+
+#ifdef USE_READ_BARRIER
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, 4
+.Laput_obj_gc_marking:
+ // We need to align stack for `art_quick_read_barrier_mark_regNN`.
+ INCREASE_FRAME 8 // Stack alignment.
+ call SYMBOL(art_quick_read_barrier_mark_reg01) // Mark ECX
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx
+ UNPOISON_HEAP_REF ecx
+ call SYMBOL(art_quick_read_barrier_mark_reg01) // Mark ECX
+ movl MIRROR_OBJECT_CLASS_OFFSET(%rdx), %eax
+ UNPOISON_HEAP_REF eax
+ call SYMBOL(art_quick_read_barrier_mark_reg00) // Mark EAX
+ DECREASE_FRAME 8 // Remove stack alignment.
+ cmpl %eax, %ecx
+ je .Laput_obj_store
+ // Prepare arguments in line with `.Laput_obj_check_assignability_call` and jump there.
+ PUSH_ARG rdi // Save arguments.
+ PUSH_ARG rsi
+ PUSH_ARG rdx
+ movl %eax, %esi // Pass arg2 - type of the value to be stored.
+ // The arg1 shall be moved at `.Ldo_assignability_check_call`.
+ jmp .Laput_obj_check_assignability_call
+#endif // USE_READ_BARRIER
END_FUNCTION art_quick_aput_obj
// TODO: This is quite silly on X86_64 now.
@@ -1324,27 +1323,27 @@
THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_EAX_ZERO
THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_EAX_ZERO
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCompiledCode, RETURN_IF_EAX_ZERO
-TWO_ARG_REF_DOWNCALL art_quick_set64_static, artSet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_set64_static, artSet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCompiledCode, RETURN_IF_EAX_ZERO
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
DEFINE_FUNCTION art_quick_proxy_invoke_handler
SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_RDI
@@ -1575,6 +1574,14 @@
CFI_REMEMBER_STATE
CFI_DEF_CFA_REGISTER(rsp)
+ // store into fpr, for when it's a fpr return...
+ movq %rax, %xmm0
+
+ LOAD_RUNTIME_INSTANCE rcx
+ cmpb MACRO_LITERAL(0), RUN_EXIT_HOOKS_OFFSET_FROM_RUNTIME_INSTANCE(%rcx)
+ jne .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame.
// Load FPRs.
// movq %xmm0, 16(%rsp) // doesn't make sense!!!
@@ -1604,10 +1611,13 @@
POP r13 // Callee save.
POP r14 // Callee save.
POP r15 // Callee save.
- // store into fpr, for when it's a fpr return...
- movq %rax, %xmm0
ret
+.Lcall_method_exit_hook:
+ movq LITERAL(FRAME_SIZE_SAVE_REFS_AND_ARGS), %r8
+ call art_quick_method_exit_hook
+ jmp .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA rbp, 208
.Lexception_in_native:
@@ -1645,82 +1655,6 @@
ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
/*
- * Routine that intercepts method calls and returns.
- */
-DEFINE_FUNCTION art_quick_instrumentation_entry
-#if defined(__APPLE__)
- int3
- int3
-#else
- SETUP_SAVE_REFS_AND_ARGS_FRAME
-
- movq %rdi, %r12 // Preserve method pointer in a callee-save.
-
- movq %gs:THREAD_SELF_OFFSET, %rdx // Pass thread.
- movq %rsp, %rcx // Pass SP.
-
- call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP)
-
- // %rax = result of call.
- testq %rax, %rax
- jz 1f
-
- movq %r12, %rdi // Reload method pointer.
- leaq art_quick_instrumentation_exit(%rip), %r12 // Set up return through instrumentation
- movq %r12, FRAME_SIZE_SAVE_REFS_AND_ARGS-8(%rsp) // exit.
-
- RESTORE_SAVE_REFS_AND_ARGS_FRAME
-
- jmp *%rax // Tail call to intended method.
-1:
- RESTORE_SAVE_REFS_AND_ARGS_FRAME
- DELIVER_PENDING_EXCEPTION
-#endif // __APPLE__
-END_FUNCTION art_quick_instrumentation_entry
-
-DEFINE_FUNCTION_CUSTOM_CFA art_quick_instrumentation_exit, 0
- pushq LITERAL(0) // Push a fake return PC as there will be none on the stack.
- CFI_ADJUST_CFA_OFFSET(8)
-
- SETUP_SAVE_EVERYTHING_FRAME
-
- leaq 16(%rsp), %rcx // Pass floating-point result pointer, in kSaveEverything frame.
- leaq 144(%rsp), %rdx // Pass integer result pointer, in kSaveEverything frame.
- movq %rsp, %rsi // Pass SP.
- movq %gs:THREAD_SELF_OFFSET, %rdi // Pass Thread.
-
- call SYMBOL(artInstrumentationMethodExitFromCode) // (Thread*, SP, gpr_res*, fpr_res*)
-
- testq %rax, %rax // Check if we have a return-pc to go to. If we don't then there was
- // an exception
- jz .Ldo_deliver_instrumentation_exception
- testq %rdx, %rdx
- jnz .Ldeoptimize
- // Normal return.
- movq %rax, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- ret
-.Ldeoptimize:
- movq %rdx, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- // Jump to art_quick_deoptimize.
- jmp SYMBOL(art_quick_deoptimize)
-.Ldo_deliver_instrumentation_exception:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_instrumentation_exit
-
- /*
- * Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
- * will long jump to the upcall with a special exception of -1.
- */
-DEFINE_FUNCTION art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME // Stack should be aligned now.
- movq %gs:THREAD_SELF_OFFSET, %rdi // Pass Thread.
- call SYMBOL(artDeoptimize) // (Thread*)
- UNREACHABLE
-END_FUNCTION art_quick_deoptimize
-
- /*
* Compiled code has requested that we deoptimize into the interpreter. The deoptimization
* will long jump to the interpreter bridge.
*/
@@ -1846,7 +1780,7 @@
movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
call artStringBuilderAppend // (uint32_t, const unit32_t*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deopt or deliver exception
END_FUNCTION art_quick_string_builder_append
// Create a function `name` calling the ReadBarrier::Mark routine,
@@ -1855,16 +1789,9 @@
//
// The generated function follows a non-standard runtime calling
// convention:
-// - register `reg` (which may be different from RDI) is used to pass
-// the (sole) argument of this function;
-// - register `reg` (which may be different from RAX) is used to return
-// the result of this function (instead of RAX);
-// - if `reg` is different from `rdi`, RDI is treated like a normal
-// (non-argument) caller-save register;
-// - if `reg` is different from `rax`, RAX is treated like a normal
-// (non-result) caller-save register;
-// - everything else is the same as in the standard runtime calling
-// convention (e.g. standard callee-save registers are preserved).
+// - register `reg` (which may be different from RDI) is used to pass the (sole) argument,
+// - register `reg` (which may be different from RAX) is used to return the result,
+// - all other registers are callee-save (the values they hold are preserved).
MACRO2(READ_BARRIER_MARK_REG, name, reg)
DEFINE_FUNCTION VAR(name)
// Null check so that we can load the lock word.
@@ -2164,33 +2091,28 @@
movq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rdi // pass ArtMethod
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
+ movq %rsp, %rdx // SP
- call SYMBOL(artMethodEntryHook) // (ArtMethod*, Thread*)
+ call SYMBOL(artMethodEntryHook) // (ArtMethod*, Thread*, sp)
RESTORE_SAVE_EVERYTHING_FRAME
ret
END_FUNCTION art_quick_method_entry_hook
// On entry, method is at the bottom of the stack.
-// and r8 has should_deopt_frame value.
DEFINE_FUNCTION art_quick_method_exit_hook
SETUP_SAVE_EVERYTHING_FRAME
+ // R8 passed from JITed code contains frame_size
leaq 16(%rsp), %rcx // floating-point result pointer in kSaveEverything
// frame
leaq 144(%rsp), %rdx // integer result pointer in kSaveEverything frame
- movq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rsi // ArtMethod
+ leaq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rsi // ArtMethod**
movq %gs:THREAD_SELF_OFFSET, %rdi // Thread::Current
- call SYMBOL(artMethodExitHook) // (Thread*, SP, gpr_res*, fpr_res*)
-
- cmpq LITERAL(1), %rax
- CFI_REMEMBER_STATE
- je .Ldo_deliver_instrumentation_exception_exit
+ call SYMBOL(artMethodExitHook) // (Thread*, ArtMethod**, gpr_res*, fpr_res*,
+ // frame_size)
// Normal return.
RESTORE_SAVE_EVERYTHING_FRAME
ret
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_method_entry_hook
+END_FUNCTION art_quick_method_exit_hook
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 2a1e15b..f6a99ac 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -40,15 +40,11 @@
return GetDeclaringClass<kWithoutReadBarrier>()->IsProxyClass<kVerifyNone>();
}
-// We are only ever allowed to set our own final fields. We do need to be careful since if a
-// structural redefinition occurs during <clinit> we can end up trying to set the non-obsolete
-// class's fields from the obsolete class. This is something we want to allow. This is tested by
-// run-test 2002-virtual-structural-initializing.
+// We are only ever allowed to set our own final fields
inline bool ArtField::CanBeChangedBy(ArtMethod* method) {
ObjPtr<mirror::Class> declaring_class(GetDeclaringClass());
ObjPtr<mirror::Class> referring_class(method->GetDeclaringClass());
- return !IsFinal() || (declaring_class == referring_class) ||
- UNLIKELY(referring_class->IsObsoleteVersionOf(declaring_class));
+ return !IsFinal() || (declaring_class == referring_class);
}
template<ReadBarrierOption kReadBarrierOption>
diff --git a/runtime/art_method-alloc-inl.h b/runtime/art_method-alloc-inl.h
new file mode 100644
index 0000000..13051b1
--- /dev/null
+++ b/runtime/art_method-alloc-inl.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ART_METHOD_ALLOC_INL_H_
+#define ART_RUNTIME_ART_METHOD_ALLOC_INL_H_
+
+#include "art_method-inl.h"
+
+#include "handle.h"
+#include "handle_scope.h"
+#include "mirror/class-alloc-inl.h"
+
+namespace art {
+
+namespace detail {
+
+template <char Shorty>
+struct HandleShortyTraits {
+ using Type = typename ShortyTraits<Shorty>::Type;
+ static Type Extract(Type value) ALWAYS_INLINE { return value; }
+};
+
+template <> struct HandleShortyTraits<'L'> {
+ using Type = Handle<mirror::Object>;
+ static typename ShortyTraits<'L'>::Type Extract(Type value)
+ REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
+ return value.Get();
+ }
+};
+
+} // namespace detail
+
+template <char... ArgType, typename HandleScopeType>
+inline Handle<mirror::Object> ArtMethod::NewObject(
+ HandleScopeType& hs,
+ Thread* self,
+ typename detail::HandleShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(GetDeclaringClass()->IsInitialized());
+ DCHECK(IsConstructor());
+ DCHECK(!IsStatic());
+ MutableHandle<mirror::Object> new_object = hs.NewHandle(GetDeclaringClass()->AllocObject(self));
+ DCHECK_EQ(new_object == nullptr, self->IsExceptionPending());
+ if (LIKELY(new_object != nullptr)) {
+ InvokeInstance<'V', ArgType...>(
+ self, new_object.Get(), detail::HandleShortyTraits<ArgType>::Extract(args)...);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ new_object.Assign(nullptr);
+ }
+ }
+ return new_object;
+}
+
+template <char... ArgType>
+inline ObjPtr<mirror::Object> ArtMethod::NewObject(
+ Thread* self, typename detail::HandleShortyTraits<ArgType>::Type... args) {
+ StackHandleScope<1u> hs(self);
+ return NewObject<ArgType...>(hs, self, args...).Get();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_ART_METHOD_ALLOC_INL_H_
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 6499bac..3ea5130 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -48,6 +48,201 @@
namespace art {
+namespace detail {
+
+template <> struct ShortyTraits<'V'> {
+ using Type = void;
+ static Type Get(const JValue& value ATTRIBUTE_UNUSED) {}
+ // `kVRegCount` and `Set()` are not defined.
+};
+
+template <> struct ShortyTraits<'Z'> {
+ // Despite using `uint8_t` for `boolean` in `JValue`, we shall use `bool` here.
+ using Type = bool;
+ static Type Get(const JValue& value) { return value.GetZ() != 0u; }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value ? 1u : 0u); }
+};
+
+template <> struct ShortyTraits<'B'> {
+ using Type = int8_t;
+ static Type Get(const JValue& value) { return value.GetB(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'C'> {
+ using Type = uint16_t;
+ static Type Get(const JValue& value) { return value.GetC(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'S'> {
+ using Type = int16_t;
+ static Type Get(const JValue& value) { return value.GetS(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'I'> {
+ using Type = int32_t;
+ static Type Get(const JValue& value) { return value.GetI(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'J'> {
+ using Type = int64_t;
+ static Type Get(const JValue& value) { return value.GetJ(); }
+ static constexpr size_t kVRegCount = 2u;
+ static void Set(uint32_t* args, Type value) {
+ // Little-endian representation.
+ args[0] = static_cast<uint32_t>(value);
+ args[1] = static_cast<uint32_t>(static_cast<uint64_t>(value) >> 32);
+ }
+};
+
+template <> struct ShortyTraits<'F'> {
+ using Type = float;
+ static Type Get(const JValue& value) { return value.GetF(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = bit_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'D'> {
+ using Type = double;
+ static Type Get(const JValue& value) { return value.GetD(); }
+ static constexpr size_t kVRegCount = 2u;
+ static void Set(uint32_t* args, Type value) {
+ // Little-endian representation.
+ uint64_t v = bit_cast<uint64_t>(value);
+ args[0] = static_cast<uint32_t>(v);
+ args[1] = static_cast<uint32_t>(v >> 32);
+ }
+};
+
+template <> struct ShortyTraits<'L'> {
+ using Type = ObjPtr<mirror::Object>;
+ static Type Get(const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return value.GetL();
+ }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ args[0] = StackReference<mirror::Object>::FromMirrorPtr(value.Ptr()).AsVRegValue();
+ }
+};
+
+template <char... Shorty>
+constexpr auto MaterializeShorty() {
+ constexpr size_t kSize = std::size({Shorty...}) + 1u;
+ return std::array<char, kSize>{Shorty..., '\0'};
+}
+
+template <char... ArgType>
+constexpr size_t NumberOfVRegs() {
+ constexpr size_t kArgVRegCount[] = {
+ ShortyTraits<ArgType>::kVRegCount...
+ };
+ size_t sum = 0u;
+ for (size_t count : kArgVRegCount) {
+ sum += count;
+ }
+ return sum;
+}
+
+template <char... ArgType>
+inline ALWAYS_INLINE void FillVRegs(uint32_t* vregs ATTRIBUTE_UNUSED,
+ typename ShortyTraits<ArgType>::Type... args ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {}
+
+template <char FirstArgType, char... ArgType>
+inline ALWAYS_INLINE void FillVRegs(uint32_t* vregs,
+ typename ShortyTraits<FirstArgType>::Type first_arg,
+ typename ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ShortyTraits<FirstArgType>::Set(vregs, first_arg);
+ FillVRegs<ArgType...>(vregs + ShortyTraits<FirstArgType>::kVRegCount, args...);
+}
+
+template <char... ArgType>
+inline ALWAYS_INLINE auto MaterializeVRegs(typename ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ constexpr size_t kNumVRegs = NumberOfVRegs<ArgType...>();
+ std::array<uint32_t, kNumVRegs> vregs;
+ FillVRegs<ArgType...>(vregs.data(), args...);
+ return vregs;
+}
+
+} // namespace detail
+
+template <char ReturnType, char... ArgType>
+inline typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeStatic(Thread* self, typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(IsStatic());
+ DCHECK(GetDeclaringClass()->IsInitialized()); // Used only for initialized well-known classes.
+ JValue result;
+ constexpr auto shorty = detail::MaterializeShorty<ReturnType, ArgType...>();
+ auto vregs = detail::MaterializeVRegs<ArgType...>(args...);
+ Invoke(self, vregs.data(), sizeof(vregs), &result, shorty.data());
+ return detail::ShortyTraits<ReturnType>::Get(result);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeInstance(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ JValue result;
+ constexpr auto shorty = detail::MaterializeShorty<ReturnType, ArgType...>();
+ auto vregs = detail::MaterializeVRegs<'L', ArgType...>(receiver, args...);
+ Invoke(self, vregs.data(), sizeof(vregs), &result, shorty.data());
+ return detail::ShortyTraits<ReturnType>::Get(result);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeFinal(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(IsFinal() || GetDeclaringClass()->IsFinal());
+ DCHECK(receiver != nullptr);
+ return InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeVirtual(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(!IsFinal());
+ DCHECK(receiver != nullptr);
+ ArtMethod* target_method =
+ receiver->GetClass()->FindVirtualMethodForVirtual(this, kRuntimePointerSize);
+ DCHECK(target_method != nullptr);
+ return target_method->InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeInterface(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(receiver != nullptr);
+ ArtMethod* target_method =
+ receiver->GetClass()->FindVirtualMethodForInterface(this, kRuntimePointerSize);
+ DCHECK(target_method != nullptr);
+ return target_method->InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
template <ReadBarrierOption kReadBarrierOption>
inline ObjPtr<mirror::Class> ArtMethod::GetDeclaringClassUnchecked() {
GcRootSource gc_root_source(this);
@@ -102,6 +297,15 @@
return type;
}
+inline bool ArtMethod::IsStringConstructor() {
+ uint32_t access_flags = GetAccessFlags();
+ DCHECK(!IsClassInitializer(access_flags));
+ return IsConstructor(access_flags) &&
+ // No read barrier needed for reading a constant reference only to read
+ // a constant string class flag. See `ReadBarrierOption`.
+ GetDeclaringClass<kWithoutReadBarrier>()->IsStringClass();
+}
+
inline bool ArtMethod::IsOverridableByDefaultMethod() {
// It is safe to avoid the read barrier here since the constant interface flag
// in the `Class` object is stored before creating the `ArtMethod` and storing
@@ -332,7 +536,7 @@
return klass->GetDexCache<kDefaultVerifyFlags, kReadBarrierOption>();
} else {
DCHECK(!IsProxyMethod());
- return GetObsoleteDexCache();
+ return GetObsoleteDexCache<kReadBarrierOption>();
}
}
@@ -378,9 +582,10 @@
return ResolveClassFromTypeIndex(GetReturnTypeIndex());
}
-template <ReadBarrierOption kReadBarrierOption>
inline bool ArtMethod::HasSingleImplementation() {
- if (IsFinal() || GetDeclaringClass<kReadBarrierOption>()->IsFinal()) {
+ // No read barrier needed for reading a constant reference only to read
+ // a constant final class flag. See `ReadBarrierOption`.
+ if (IsFinal() || GetDeclaringClass<kWithoutReadBarrier>()->IsFinal()) {
// We don't set kAccSingleImplementation for these cases since intrinsic
// can use the flag also.
return true;
@@ -464,6 +669,43 @@
}
}
+template <ReadBarrierOption kReadBarrierOption>
+inline bool ArtMethod::StillNeedsClinitCheck() {
+ if (!NeedsClinitCheckBeforeCall()) {
+ return false;
+ }
+ ObjPtr<mirror::Class> klass = GetDeclaringClass<kReadBarrierOption>();
+ return !klass->IsVisiblyInitialized();
+}
+
+inline bool ArtMethod::StillNeedsClinitCheckMayBeDead() {
+ if (!NeedsClinitCheckBeforeCall()) {
+ return false;
+ }
+ ObjPtr<mirror::Class> klass = GetDeclaringClassMayBeDead();
+ return !klass->IsVisiblyInitialized();
+}
+
+inline bool ArtMethod::IsDeclaringClassVerifiedMayBeDead() {
+ ObjPtr<mirror::Class> klass = GetDeclaringClassMayBeDead();
+ return klass->IsVerified();
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::GetDeclaringClassMayBeDead() {
+ // Helper method for checking the status of the declaring class which may be dead.
+ //
+ // To avoid resurrecting an unreachable object, or crashing the GC in some GC phases,
+ // we must not use a full read barrier. Therefore we read the declaring class without
+ // a read barrier and check if it's already marked. If yes, we check the status of the
+ // to-space class object as intended. Otherwise, there is no to-space object and the
+ // from-space class object contains the most recent value of the status field; even if
+ // this races with another thread doing a read barrier and updating the status, that's
+ // no different from a race with a thread that just updates the status.
+ ObjPtr<mirror::Class> klass = GetDeclaringClass<kWithoutReadBarrier>();
+ ObjPtr<mirror::Class> marked = ReadBarrier::IsMarked(klass.Ptr());
+ return (marked != nullptr) ? marked : klass;
+}
+
inline CodeItemInstructionAccessor ArtMethod::DexInstructions() {
return CodeItemInstructionAccessor(*GetDexFile(), GetCodeItem());
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index f6f8b5f..b500d9b 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -111,35 +111,63 @@
return executable->GetArtMethod();
}
+template <ReadBarrierOption kReadBarrierOption>
ObjPtr<mirror::DexCache> ArtMethod::GetObsoleteDexCache() {
+ // Note: The class redefinition happens with GC disabled, so at the point where we
+ // create obsolete methods, the `ClassExt` and its obsolete methods and dex caches
+ // members are reachable without a read barrier. If we start a GC later, and we
+ // look at these objects without read barriers (`kWithoutReadBarrier`), the method
+ // pointers shall be the same in from-space array as in to-space array (if these
+ // arrays are different) and the dex cache array entry can point to from-space or
+ // to-space `DexCache` but either is a valid result for `kWithoutReadBarrier`.
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ std::optional<ScopedDebugDisallowReadBarriers> sddrb(std::nullopt);
+ if (kIsDebugBuild && kReadBarrierOption == kWithoutReadBarrier) {
+ sddrb.emplace(Thread::Current());
+ }
PointerSize pointer_size = kRuntimePointerSize;
DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod();
DCHECK(IsObsolete());
- ObjPtr<mirror::ClassExt> ext(GetDeclaringClass()->GetExtData());
- ObjPtr<mirror::PointerArray> obsolete_methods(ext.IsNull() ? nullptr : ext->GetObsoleteMethods());
- int32_t len = (obsolete_methods.IsNull() ? 0 : obsolete_methods->GetLength());
- DCHECK(len == 0 || len == ext->GetObsoleteDexCaches()->GetLength())
- << "len=" << len << " ext->GetObsoleteDexCaches()=" << ext->GetObsoleteDexCaches();
+ ObjPtr<mirror::Class> declaring_class = GetDeclaringClass<kReadBarrierOption>();
+ ObjPtr<mirror::ClassExt> ext =
+ declaring_class->GetExtData<kDefaultVerifyFlags, kReadBarrierOption>();
+ ObjPtr<mirror::PointerArray> obsolete_methods(
+ ext.IsNull() ? nullptr : ext->GetObsoleteMethods<kDefaultVerifyFlags, kReadBarrierOption>());
+ int32_t len = 0;
+ ObjPtr<mirror::ObjectArray<mirror::DexCache>> obsolete_dex_caches = nullptr;
+ if (!obsolete_methods.IsNull()) {
+ len = obsolete_methods->GetLength();
+ obsolete_dex_caches = ext->GetObsoleteDexCaches<kDefaultVerifyFlags, kReadBarrierOption>();
+ // FIXME: `ClassExt::SetObsoleteArrays()` is not atomic, so one of the arrays we see here
+ // could be extended for a new class redefinition while the other may be shorter.
+ // Furthermore, there is no synchronization to ensure that copied contents of an old
+ // obsolete array are visible to a thread reading the new array.
+ DCHECK_EQ(len, obsolete_dex_caches->GetLength())
+ << " ext->GetObsoleteDexCaches()=" << obsolete_dex_caches;
+ }
// Using kRuntimePointerSize (instead of using the image's pointer size) is fine since images
// should never have obsolete methods in them so they should always be the same.
DCHECK_EQ(pointer_size, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
for (int32_t i = 0; i < len; i++) {
if (this == obsolete_methods->GetElementPtrSize<ArtMethod*>(i, pointer_size)) {
- return ext->GetObsoleteDexCaches()->Get(i);
+ return obsolete_dex_caches->GetWithoutChecks<kDefaultVerifyFlags, kReadBarrierOption>(i);
}
}
- CHECK(GetDeclaringClass()->IsObsoleteObject())
+ CHECK(declaring_class->IsObsoleteObject())
<< "This non-structurally obsolete method does not appear in the obsolete map of its class: "
- << GetDeclaringClass()->PrettyClass() << " Searched " << len << " caches.";
+ << declaring_class->PrettyClass() << " Searched " << len << " caches.";
CHECK_EQ(this,
std::clamp(this,
- &(*GetDeclaringClass()->GetMethods(pointer_size).begin()),
- &(*GetDeclaringClass()->GetMethods(pointer_size).end())))
+ &(*declaring_class->GetMethods(pointer_size).begin()),
+ &(*declaring_class->GetMethods(pointer_size).end())))
<< "class is marked as structurally obsolete method but not found in normal obsolete-map "
<< "despite not being the original method pointer for " << GetDeclaringClass()->PrettyClass();
- return GetDeclaringClass()->GetDexCache();
+ return declaring_class->template GetDexCache<kDefaultVerifyFlags, kReadBarrierOption>();
}
+template ObjPtr<mirror::DexCache> ArtMethod::GetObsoleteDexCache<kWithReadBarrier>();
+template ObjPtr<mirror::DexCache> ArtMethod::GetObsoleteDexCache<kWithoutReadBarrier>();
+
uint16_t ArtMethod::FindObsoleteDexClassDefIndex() {
DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod();
DCHECK(IsObsolete());
@@ -150,10 +178,34 @@
return dex_file->GetIndexForClassDef(*class_def);
}
-void ArtMethod::ThrowInvocationTimeError() {
+void ArtMethod::ThrowInvocationTimeError(ObjPtr<mirror::Object> receiver) {
DCHECK(!IsInvokable());
if (IsDefaultConflicting()) {
ThrowIncompatibleClassChangeErrorForMethodConflict(this);
+ } else if (GetDeclaringClass()->IsInterface() && receiver != nullptr) {
+ // If this was an interface call, check whether there is a method in the
+ // superclass chain that isn't public. In this situation, we should throw an
+ // IllegalAccessError.
+ DCHECK(IsAbstract());
+ ObjPtr<mirror::Class> current = receiver->GetClass();
+ while (current != nullptr) {
+ for (ArtMethod& method : current->GetDeclaredMethodsSlice(kRuntimePointerSize)) {
+ ArtMethod* np_method = method.GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ if (!np_method->IsStatic() &&
+ np_method->GetNameView() == GetNameView() &&
+ np_method->GetSignature() == GetSignature()) {
+ if (!np_method->IsPublic()) {
+ ThrowIllegalAccessErrorForImplementingMethod(receiver->GetClass(), np_method, this);
+ return;
+ } else if (np_method->IsAbstract()) {
+ ThrowAbstractMethodError(this);
+ return;
+ }
+ }
+ }
+ current = current->GetSuperClass();
+ }
+ ThrowAbstractMethodError(this);
} else {
DCHECK(IsAbstract());
ThrowAbstractMethodError(this);
@@ -310,6 +362,7 @@
return found_dex_pc;
}
+NO_STACK_PROTECTOR
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
@@ -532,10 +585,6 @@
}
const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) {
- // Our callers should make sure they don't pass the instrumentation exit pc,
- // as this method does not look at the side instrumentation stack.
- DCHECK_NE(pc, reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()));
-
if (IsRuntimeMethod()) {
return nullptr;
}
@@ -551,11 +600,16 @@
return nullptr;
}
+ // We should not reach here with a pc of 0. pc can be 0 for downcalls when walking the stack.
+ // For native methods this case is handled by the caller by checking the quick frame tag. See
+ // StackVisitor::WalkStack for more details. For non-native methods pc can be 0 only for runtime
+ // methods or proxy invoke handlers which are handled earlier.
+ DCHECK_NE(pc, 0u) << "PC 0 for " << PrettyMethod();
+
// Check whether the current entry point contains this pc.
if (!class_linker->IsQuickGenericJniStub(existing_entry_point) &&
!class_linker->IsQuickResolutionStub(existing_entry_point) &&
!class_linker->IsQuickToInterpreterBridge(existing_entry_point) &&
- existing_entry_point != GetQuickInstrumentationEntryPoint() &&
existing_entry_point != GetInvokeObsoleteMethodStub()) {
OatQuickMethodHeader* method_header =
OatQuickMethodHeader::FromEntryPoint(existing_entry_point);
@@ -592,21 +646,16 @@
OatFile::OatMethod oat_method =
FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found);
if (!found) {
- if (IsNative()) {
- // We are running the GenericJNI stub. The entrypoint may point
- // to different entrypoints or to a JIT-compiled JNI stub.
- DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) ||
- class_linker->IsQuickResolutionStub(existing_entry_point) ||
- existing_entry_point == GetQuickInstrumentationEntryPoint() ||
- (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point)))
- << " entrypoint: " << existing_entry_point
- << " size: " << OatQuickMethodHeader::FromEntryPoint(existing_entry_point)->GetCodeSize()
- << " pc: " << reinterpret_cast<const void*>(pc);
- return nullptr;
- }
- // Only for unit tests.
- // TODO(ngeoffray): Update these tests to pass the right pc?
- return OatQuickMethodHeader::FromEntryPoint(existing_entry_point);
+ CHECK(IsNative());
+ // We are running the GenericJNI stub. The entrypoint may point
+ // to different entrypoints or to a JIT-compiled JNI stub.
+ DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) ||
+ class_linker->IsQuickResolutionStub(existing_entry_point) ||
+ (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point)))
+ << " entrypoint: " << existing_entry_point
+ << " size: " << OatQuickMethodHeader::FromEntryPoint(existing_entry_point)->GetCodeSize()
+ << " pc: " << reinterpret_cast<const void*>(pc);
+ return nullptr;
}
const void* oat_entry_point = oat_method.GetQuickCode();
if (oat_entry_point == nullptr || class_linker->IsQuickGenericJniStub(oat_entry_point)) {
@@ -615,10 +664,13 @@
}
OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(oat_entry_point);
- if (pc == 0) {
- // This is a downcall, it can only happen for a native method.
- DCHECK(IsNative());
- return method_header;
+ // We could have existing Oat code for native methods but we may not use it if the runtime is java
+ // debuggable or when profiling boot class path. There is no easy way to check if the pc
+ // corresponds to QuickGenericJniStub. Since we have eliminated all the other cases, if the pc
+ // doesn't correspond to the AOT code then we must be running QuickGenericJniStub.
+ if (IsNative() && !method_header->Contains(pc)) {
+ DCHECK_NE(pc, 0u) << "PC 0 for " << PrettyMethod();
+ return nullptr;
}
DCHECK(method_header->Contains(pc))
@@ -728,16 +780,16 @@
// the entry point to the JIT code, but this would require taking the JIT code cache
// lock to notify it, which we do not want at this level.
Runtime* runtime = Runtime::Current();
+ const void* entry_point = GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size);
if (runtime->UseJitCompilation()) {
- if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
+ if (runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point)) {
SetEntryPointFromQuickCompiledCodePtrSize(
src->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge(),
image_pointer_size);
}
}
- if (interpreter::IsNterpSupported() &&
- (GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size) ==
- interpreter::GetNterpEntryPoint())) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (interpreter::IsNterpSupported() && class_linker->IsNterpEntryPoint(entry_point)) {
// If the entrypoint is nterp, it's too early to check if the new method
// will support it. So for simplicity, use the interpreter bridge.
SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index fef58a7..dce4066 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -68,6 +68,22 @@
class String;
} // namespace mirror
+namespace detail {
+template <char Shorty> struct ShortyTraits;
+template <> struct ShortyTraits<'V'>;
+template <> struct ShortyTraits<'Z'>;
+template <> struct ShortyTraits<'B'>;
+template <> struct ShortyTraits<'C'>;
+template <> struct ShortyTraits<'S'>;
+template <> struct ShortyTraits<'I'>;
+template <> struct ShortyTraits<'J'>;
+template <> struct ShortyTraits<'F'>;
+template <> struct ShortyTraits<'D'>;
+template <> struct ShortyTraits<'L'>;
+template <char Shorty> struct HandleShortyTraits;
+template <> struct HandleShortyTraits<'L'>;
+} // namespace detail
+
class ArtMethod final {
public:
// Should the class state be checked on sensitive operations?
@@ -149,27 +165,47 @@
// Returns true if the method is declared public.
bool IsPublic() const {
- return (GetAccessFlags() & kAccPublic) != 0;
+ return IsPublic(GetAccessFlags());
+ }
+
+ static bool IsPublic(uint32_t access_flags) {
+ return (access_flags & kAccPublic) != 0;
}
// Returns true if the method is declared private.
bool IsPrivate() const {
- return (GetAccessFlags() & kAccPrivate) != 0;
+ return IsPrivate(GetAccessFlags());
+ }
+
+ static bool IsPrivate(uint32_t access_flags) {
+ return (access_flags & kAccPrivate) != 0;
}
// Returns true if the method is declared static.
bool IsStatic() const {
- return (GetAccessFlags() & kAccStatic) != 0;
+ return IsStatic(GetAccessFlags());
+ }
+
+ static bool IsStatic(uint32_t access_flags) {
+ return (access_flags & kAccStatic) != 0;
}
// Returns true if the method is a constructor according to access flags.
bool IsConstructor() const {
- return (GetAccessFlags() & kAccConstructor) != 0;
+ return IsConstructor(GetAccessFlags());
+ }
+
+ static bool IsConstructor(uint32_t access_flags) {
+ return (access_flags & kAccConstructor) != 0;
}
// Returns true if the method is a class initializer according to access flags.
bool IsClassInitializer() const {
- return IsConstructor() && IsStatic();
+ return IsClassInitializer(GetAccessFlags());
+ }
+
+ static bool IsClassInitializer(uint32_t access_flags) {
+ return IsConstructor(access_flags) && IsStatic(access_flags);
}
// Returns true if the method is static, private, or a constructor.
@@ -184,16 +220,30 @@
// Returns true if the method is declared synchronized.
bool IsSynchronized() const {
+ return IsSynchronized(GetAccessFlags());
+ }
+
+ static bool IsSynchronized(uint32_t access_flags) {
constexpr uint32_t synchonized = kAccSynchronized | kAccDeclaredSynchronized;
- return (GetAccessFlags() & synchonized) != 0;
+ return (access_flags & synchonized) != 0;
}
+ // Returns true if the method is declared final.
bool IsFinal() const {
- return (GetAccessFlags() & kAccFinal) != 0;
+ return IsFinal(GetAccessFlags());
}
+ static bool IsFinal(uint32_t access_flags) {
+ return (access_flags & kAccFinal) != 0;
+ }
+
+ // Returns true if the method is an intrinsic.
bool IsIntrinsic() const {
- return (GetAccessFlags() & kAccIntrinsic) != 0;
+ return IsIntrinsic(GetAccessFlags());
+ }
+
+ static bool IsIntrinsic(uint32_t access_flags) {
+ return (access_flags & kAccIntrinsic) != 0;
}
ALWAYS_INLINE void SetIntrinsic(uint32_t intrinsic) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -210,53 +260,76 @@
void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if the method is a copied method.
bool IsCopied() const {
+ return IsCopied(GetAccessFlags());
+ }
+
+ static bool IsCopied(uint32_t access_flags) {
// We do not have intrinsics for any default methods and therefore intrinsics are never copied.
// So we are using a flag from the intrinsic flags range and need to check `kAccIntrinsic` too.
static_assert((kAccCopied & kAccIntrinsicBits) != 0,
"kAccCopied deliberately overlaps intrinsic bits");
- const bool copied = (GetAccessFlags() & (kAccIntrinsic | kAccCopied)) == kAccCopied;
+ const bool copied = (access_flags & (kAccIntrinsic | kAccCopied)) == kAccCopied;
// (IsMiranda() || IsDefaultConflicting()) implies copied
- DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied)
+ DCHECK(!(IsMiranda(access_flags) || IsDefaultConflicting(access_flags)) || copied)
<< "Miranda or default-conflict methods must always be copied.";
return copied;
}
bool IsMiranda() const {
+ return IsMiranda(GetAccessFlags());
+ }
+
+ static bool IsMiranda(uint32_t access_flags) {
// Miranda methods are marked as copied and abstract but not default.
// We need to check the kAccIntrinsic too, see `IsCopied()`.
static constexpr uint32_t kMask = kAccIntrinsic | kAccCopied | kAccAbstract | kAccDefault;
static constexpr uint32_t kValue = kAccCopied | kAccAbstract;
- return (GetAccessFlags() & kMask) == kValue;
+ return (access_flags & kMask) == kValue;
}
// A default conflict method is a special sentinel method that stands for a conflict between
// multiple default methods. It cannot be invoked, throwing an IncompatibleClassChangeError
// if one attempts to do so.
bool IsDefaultConflicting() const {
+ return IsDefaultConflicting(GetAccessFlags());
+ }
+
+ static bool IsDefaultConflicting(uint32_t access_flags) {
// Default conflct methods are marked as copied, abstract and default.
// We need to check the kAccIntrinsic too, see `IsCopied()`.
static constexpr uint32_t kMask = kAccIntrinsic | kAccCopied | kAccAbstract | kAccDefault;
static constexpr uint32_t kValue = kAccCopied | kAccAbstract | kAccDefault;
- return (GetAccessFlags() & kMask) == kValue;
+ return (access_flags & kMask) == kValue;
}
// Returns true if invoking this method will not throw an AbstractMethodError or
// IncompatibleClassChangeError.
bool IsInvokable() const {
- // Default conflicting methods are marked with `kAccAbstract` (as well as `kAccCopied`
- // and `kAccDefault`) but they are not considered abstract, see `IsAbstract()`.
- DCHECK_EQ((GetAccessFlags() & kAccAbstract) == 0, !IsDefaultConflicting() && !IsAbstract());
- return (GetAccessFlags() & kAccAbstract) == 0;
+ return IsInvokable(GetAccessFlags());
}
+ static bool IsInvokable(uint32_t access_flags) {
+ // Default conflicting methods are marked with `kAccAbstract` (as well as `kAccCopied`
+ // and `kAccDefault`) but they are not considered abstract, see `IsAbstract()`.
+ DCHECK_EQ((access_flags & kAccAbstract) == 0,
+ !IsDefaultConflicting(access_flags) && !IsAbstract(access_flags));
+ return (access_flags & kAccAbstract) == 0;
+ }
+
+ // Returns true if the method is marked as pre-compiled.
bool IsPreCompiled() const {
+ return IsPreCompiled(GetAccessFlags());
+ }
+
+ static bool IsPreCompiled(uint32_t access_flags) {
// kAccCompileDontBother and kAccPreCompiled overlap with kAccIntrinsicBits.
static_assert((kAccCompileDontBother & kAccIntrinsicBits) != 0);
static_assert((kAccPreCompiled & kAccIntrinsicBits) != 0);
static constexpr uint32_t kMask = kAccIntrinsic | kAccCompileDontBother | kAccPreCompiled;
static constexpr uint32_t kValue = kAccCompileDontBother | kAccPreCompiled;
- return (GetAccessFlags() & kMask) == kValue;
+ return (access_flags & kMask) == kValue;
}
void SetPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -272,41 +345,51 @@
AddAccessFlags(kAccPreCompiled | kAccCompileDontBother);
}
+ void ClearPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClearAccessFlags(kAccPreCompiled | kAccCompileDontBother);
+ }
+
+ // Returns true if the method resides in shared memory.
bool IsMemorySharedMethod() {
- return (GetAccessFlags() & kAccMemorySharedMethod) != 0;
+ return IsMemorySharedMethod(GetAccessFlags());
+ }
+
+ static bool IsMemorySharedMethod(uint32_t access_flags) {
+ return (access_flags & kAccMemorySharedMethod) != 0;
}
void SetMemorySharedMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
- // Disable until we make sure critical code is AOTed.
- static constexpr bool kEnabledMemorySharedMethod = false;
- if (kEnabledMemorySharedMethod && !IsIntrinsic() && !IsAbstract()) {
+ uint32_t access_flags = GetAccessFlags();
+ if (!IsIntrinsic(access_flags) && !IsAbstract(access_flags)) {
AddAccessFlags(kAccMemorySharedMethod);
SetHotCounter();
}
}
void ClearMemorySharedMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
- if (IsIntrinsic() || IsAbstract()) {
+ uint32_t access_flags = GetAccessFlags();
+ if (IsIntrinsic(access_flags) || IsAbstract(access_flags)) {
return;
}
- if (IsMemorySharedMethod()) {
+ if (IsMemorySharedMethod(access_flags)) {
ClearAccessFlags(kAccMemorySharedMethod);
}
}
- void ClearPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
- ClearAccessFlags(kAccPreCompiled | kAccCompileDontBother);
+ // Returns true if the method can be compiled.
+ bool IsCompilable() const {
+ return IsCompilable(GetAccessFlags());
}
- bool IsCompilable() const {
- if (IsIntrinsic()) {
+ static bool IsCompilable(uint32_t access_flags) {
+ if (IsIntrinsic(access_flags)) {
// kAccCompileDontBother overlaps with kAccIntrinsicBits.
return true;
}
- if (IsPreCompiled()) {
+ if (IsPreCompiled(access_flags)) {
return true;
}
- return (GetAccessFlags() & kAccCompileDontBother) == 0;
+ return (access_flags & kAccCompileDontBother) == 0;
}
void ClearDontCompile() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -321,52 +404,107 @@
// This is set by the class linker.
bool IsDefault() const {
- static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
- "kAccDefault conflicts with intrinsic modifier");
- return (GetAccessFlags() & kAccDefault) != 0;
+ return IsDefault(GetAccessFlags());
}
+ static bool IsDefault(uint32_t access_flags) {
+ static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
+ "kAccDefault conflicts with intrinsic modifier");
+ return (access_flags & kAccDefault) != 0;
+ }
+
+ // Returns true if the method is obsolete.
bool IsObsolete() const {
- return (GetAccessFlags() & kAccObsoleteMethod) != 0;
+ return IsObsolete(GetAccessFlags());
+ }
+
+ static bool IsObsolete(uint32_t access_flags) {
+ return (access_flags & kAccObsoleteMethod) != 0;
}
void SetIsObsolete() REQUIRES_SHARED(Locks::mutator_lock_) {
AddAccessFlags(kAccObsoleteMethod);
}
+ // Returns true if the method is native.
bool IsNative() const {
- return (GetAccessFlags() & kAccNative) != 0;
+ return IsNative(GetAccessFlags());
+ }
+
+ static bool IsNative(uint32_t access_flags) {
+ return (access_flags & kAccNative) != 0;
}
// Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative.
bool IsFastNative() const {
+ return IsFastNative(GetAccessFlags());
+ }
+
+ static bool IsFastNative(uint32_t access_flags) {
// The presence of the annotation is checked by ClassLinker and recorded in access flags.
// The kAccFastNative flag value is used with a different meaning for non-native methods,
// so we need to check the kAccNative flag as well.
constexpr uint32_t mask = kAccFastNative | kAccNative;
- return (GetAccessFlags() & mask) == mask;
+ return (access_flags & mask) == mask;
}
// Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative.
bool IsCriticalNative() const {
+ return IsCriticalNative(GetAccessFlags());
+ }
+
+ static bool IsCriticalNative(uint32_t access_flags) {
// The presence of the annotation is checked by ClassLinker and recorded in access flags.
// The kAccCriticalNative flag value is used with a different meaning for non-native methods,
// so we need to check the kAccNative flag as well.
constexpr uint32_t mask = kAccCriticalNative | kAccNative;
- return (GetAccessFlags() & mask) == mask;
+ return (access_flags & mask) == mask;
}
+ // Returns true if the method is managed (not native).
+ bool IsManaged() const {
+ return IsManaged(GetAccessFlags());
+ }
+
+ static bool IsManaged(uint32_t access_flags) {
+ return !IsNative(access_flags);
+ }
+
+ // Returns true if the method is managed (not native) and invokable.
+ bool IsManagedAndInvokable() const {
+ return IsManagedAndInvokable(GetAccessFlags());
+ }
+
+ static bool IsManagedAndInvokable(uint32_t access_flags) {
+ return IsManaged(access_flags) && IsInvokable(access_flags);
+ }
+
+ // Returns true if the method is abstract.
bool IsAbstract() const {
+ return IsAbstract(GetAccessFlags());
+ }
+
+ static bool IsAbstract(uint32_t access_flags) {
// Default confliciting methods have `kAccAbstract` set but they are not actually abstract.
- return (GetAccessFlags() & kAccAbstract) != 0 && !IsDefaultConflicting();
+ return (access_flags & kAccAbstract) != 0 && !IsDefaultConflicting(access_flags);
}
+ // Returns true if the method is declared synthetic.
bool IsSynthetic() const {
- return (GetAccessFlags() & kAccSynthetic) != 0;
+ return IsSynthetic(GetAccessFlags());
}
+ static bool IsSynthetic(uint32_t access_flags) {
+ return (access_flags & kAccSynthetic) != 0;
+ }
+
+ // Returns true if the method is declared varargs.
bool IsVarargs() const {
- return (GetAccessFlags() & kAccVarargs) != 0;
+ return IsVarargs(GetAccessFlags());
+ }
+
+ static bool IsVarargs(uint32_t access_flags) {
+ return (access_flags & kAccVarargs) != 0;
}
bool IsProxyMethod() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -390,10 +528,15 @@
ClearAccessFlags(kAccSkipAccessChecks);
}
+ // Returns true if the method has previously been warm.
bool PreviouslyWarm() const {
+ return PreviouslyWarm(GetAccessFlags());
+ }
+
+ static bool PreviouslyWarm(uint32_t access_flags) {
// kAccPreviouslyWarm overlaps with kAccIntrinsicBits. Return true for intrinsics.
constexpr uint32_t mask = kAccPreviouslyWarm | kAccIntrinsic;
- return (GetAccessFlags() & mask) != 0u;
+ return (access_flags & mask) != 0u;
}
void SetPreviouslyWarm() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -407,10 +550,14 @@
// Should this method be run in the interpreter and count locks (e.g., failed structured-
// locking verification)?
bool MustCountLocks() const {
- if (IsIntrinsic()) {
+ return MustCountLocks(GetAccessFlags());
+ }
+
+ static bool MustCountLocks(uint32_t access_flags) {
+ if (IsIntrinsic(access_flags)) {
return false;
}
- return (GetAccessFlags() & kAccMustCountLocks) != 0;
+ return (access_flags & kAccMustCountLocks) != 0;
}
void ClearMustCountLocks() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -418,13 +565,18 @@
}
void SetMustCountLocks() REQUIRES_SHARED(Locks::mutator_lock_) {
- AddAccessFlags(kAccMustCountLocks);
ClearAccessFlags(kAccSkipAccessChecks);
+ AddAccessFlags(kAccMustCountLocks);
}
+ // Returns true if the method is using the nterp entrypoint fast path.
bool HasNterpEntryPointFastPathFlag() const {
+ return HasNterpEntryPointFastPathFlag(GetAccessFlags());
+ }
+
+ static bool HasNterpEntryPointFastPathFlag(uint32_t access_flags) {
constexpr uint32_t mask = kAccNative | kAccNterpEntryPointFastPathFlag;
- return (GetAccessFlags() & mask) == kAccNterpEntryPointFastPathFlag;
+ return (access_flags & mask) == kAccNterpEntryPointFastPathFlag;
}
void SetNterpEntryPointFastPathFlag() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -436,14 +588,21 @@
AddAccessFlags(kAccNterpInvokeFastPathFlag);
}
+ // Returns whether the method is a string constructor. The method must not
+ // be a class initializer. (Class initializers are called from a different
+ // context where we do not need to check for string constructors.)
+ bool IsStringConstructor() REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
bool CheckIncompatibleClassChange(InvokeType type) REQUIRES_SHARED(Locks::mutator_lock_);
// Throws the error that would result from trying to invoke this method (i.e.
- // IncompatibleClassChangeError or AbstractMethodError). Only call if !IsInvokable();
- void ThrowInvocationTimeError() REQUIRES_SHARED(Locks::mutator_lock_);
+ // IncompatibleClassChangeError, AbstractMethodError, or IllegalAccessError).
+ // Only call if !IsInvokable();
+ void ThrowInvocationTimeError(ObjPtr<mirror::Object> receiver)
+ REQUIRES_SHARED(Locks::mutator_lock_);
uint16_t GetMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -508,6 +667,80 @@
void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeStatic(Thread* self, typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeInstance(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeFinal(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeVirtual(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeInterface(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char... ArgType, typename HandleScopeType>
+ Handle<mirror::Object> NewObject(HandleScopeType& hs,
+ Thread* self,
+ typename detail::HandleShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char... ArgType>
+ ObjPtr<mirror::Object> NewObject(Thread* self,
+ typename detail::HandleShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Returns true if the method needs a class initialization check according to access flags.
+ // Only static methods other than the class initializer need this check.
+ // The caller is responsible for performing the actual check.
+ bool NeedsClinitCheckBeforeCall() const {
+ return NeedsClinitCheckBeforeCall(GetAccessFlags());
+ }
+
+ static bool NeedsClinitCheckBeforeCall(uint32_t access_flags) {
+ // The class initializer is special as it is invoked during initialization
+ // and does not need the check.
+ return IsStatic(access_flags) && !IsConstructor(access_flags);
+ }
+
+ // Check if the method needs a class initialization check before call
+ // and its declaring class is not yet visibly initialized.
+ // (The class needs to be visibly initialized before we can use entrypoints
+ // to compiled code for static methods. See b/18161648 .)
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ bool StillNeedsClinitCheck() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Similar to `StillNeedsClinitCheck()` but the method's declaring class may
+ // be dead but not yet reclaimed by the GC, so we cannot do a full read barrier
+ // but we still want to check the class status in the to-space class if any.
+ // Note: JIT can hold and use such methods during managed heap GC.
+ bool StillNeedsClinitCheckMayBeDead() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Check if the declaring class has been verified and look at the to-space
+ // class object, if any, as in `StillNeedsClinitCheckMayBeDead()`.
+ bool IsDeclaringClassVerifiedMayBeDead() REQUIRES_SHARED(Locks::mutator_lock_);
+
const void* GetEntryPointFromQuickCompiledCode() const {
return GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize);
}
@@ -556,7 +789,6 @@
SetDataPtrSize(table, pointer_size);
}
- template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE bool HasSingleImplementation() REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE void SetHasSingleImplementation(bool single_impl)
@@ -630,7 +862,11 @@
}
bool HasCodeItem() REQUIRES_SHARED(Locks::mutator_lock_) {
- return !IsRuntimeMethod() && !IsNative() && !IsProxyMethod() && !IsAbstract();
+ uint32_t access_flags = GetAccessFlags();
+ return !IsNative(access_flags) &&
+ !IsAbstract(access_flags) &&
+ !IsRuntimeMethod() &&
+ !IsProxyMethod();
}
// We need to explicitly indicate whether the code item is obtained from the compact dex file,
@@ -711,6 +947,7 @@
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ObjPtr<mirror::DexCache> GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
+ template <ReadBarrierOption kReadBarrierOption>
ObjPtr<mirror::DexCache> GetObsoleteDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE ArtMethod* GetInterfaceMethodForProxyUnchecked(PointerSize pointer_size)
@@ -844,7 +1081,7 @@
// Entry within a dispatch table for this method. For static/direct methods the index is into
// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
- // ifTable.
+ // interface's method array in `IfTable`s of implementing classes.
uint16_t method_index_;
union {
@@ -938,6 +1175,10 @@
access_flags_.fetch_and(~flag, std::memory_order_relaxed);
}
+ // Helper method for checking the class status of a possibly dead declaring class.
+ // See `StillNeedsClinitCheckMayBeDead()` and `IsDeclaringClassVerifierMayBeDead()`.
+ ObjPtr<mirror::Class> GetDeclaringClassMayBeDead() REQUIRES_SHARED(Locks::mutator_lock_);
+
// Used by GetName and GetNameView to share common code.
const char* GetRuntimeMethodName() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/art_method_test.cc b/runtime/art_method_test.cc
new file mode 100644
index 0000000..b71f8a3
--- /dev/null
+++ b/runtime/art_method_test.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 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 <type_traits>
+
+#include "art_method-alloc-inl.h"
+
+#include "base/casts.h"
+#include "common_runtime_test.h"
+#include "mirror/class-alloc-inl.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+namespace {
+// Helper function to avoid `ASSERT_EQ` with floating point types.
+int32_t ToIntegralType(float value) { return bit_cast<int32_t>(value); }
+int64_t ToIntegralType(double value) { return bit_cast<int64_t>(value); }
+template <typename T> T ToIntegralType(T value) { return value; }
+}
+
+class ArtMethodTest : public CommonRuntimeTest {
+ protected:
+ ArtMethodTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
+ // Test primitive type boxing and unboxing.
+ //
+ // This provides basic checks that the translation of the compile-time shorty
+ // to argument types and return type are correct and that values are passed
+ // correctly for these single-argument calls (`ArtMethod::InvokeStatic()` with
+ // primitive args and `ArtMethod::InvokeInstance()` with a reference arg).
+ template <typename Type, char kPrimitive>
+ void TestBoxUnbox(ArtMethod* value_of, const char* unbox_name, Type value) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ ASSERT_STREQ(value_of->GetName(), "valueOf");
+ std::string unbox_signature = std::string("()") + kPrimitive;
+ ArtMethod* unbox_method = value_of->GetDeclaringClass()->FindClassMethod(
+ unbox_name, unbox_signature.c_str(), kRuntimePointerSize);
+ ASSERT_TRUE(unbox_method != nullptr);
+ ASSERT_FALSE(unbox_method->IsStatic());
+ ASSERT_TRUE(value_of->GetDeclaringClass()->IsFinal());
+
+ static_assert(std::is_same_v<ObjPtr<mirror::Object> (ArtMethod::*)(Thread*, Type),
+ decltype(&ArtMethod::InvokeStatic<'L', kPrimitive>)>);
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::Object> boxed =
+ hs.NewHandle(value_of->InvokeStatic<'L', kPrimitive>(self, value));
+ ASSERT_TRUE(boxed != nullptr);
+ ASSERT_OBJ_PTR_EQ(boxed->GetClass(), value_of->GetDeclaringClass());
+ static_assert(std::is_same_v<Type (ArtMethod::*)(Thread*, ObjPtr<mirror::Object>),
+ decltype(&ArtMethod::InvokeInstance<kPrimitive>)>);
+ // Exercise both `InvokeInstance()` and `InvokeFinal()` (boxing classes are final).
+ Type unboxed1 = unbox_method->InvokeInstance<kPrimitive>(self, boxed.Get());
+ ASSERT_EQ(ToIntegralType(value), ToIntegralType(unboxed1));
+ Type unboxed2 = unbox_method->InvokeFinal<kPrimitive>(self, boxed.Get());
+ ASSERT_EQ(ToIntegralType(value), ToIntegralType(unboxed2));
+ }
+};
+
+TEST_F(ArtMethodTest, BoxUnboxBoolean) {
+ TestBoxUnbox<bool, 'Z'>(WellKnownClasses::java_lang_Boolean_valueOf, "booleanValue", true);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxByte) {
+ TestBoxUnbox<int8_t, 'B'>(WellKnownClasses::java_lang_Byte_valueOf, "byteValue", -12);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxChar) {
+ TestBoxUnbox<uint16_t, 'C'>(WellKnownClasses::java_lang_Character_valueOf, "charValue", 0xffaa);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxShort) {
+ TestBoxUnbox<int16_t, 'S'>(WellKnownClasses::java_lang_Short_valueOf, "shortValue", -0x1234);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxInt) {
+ TestBoxUnbox<int32_t, 'I'>(WellKnownClasses::java_lang_Integer_valueOf, "intValue", -0x12345678);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxLong) {
+ TestBoxUnbox<int64_t, 'J'>(
+ WellKnownClasses::java_lang_Long_valueOf, "longValue", UINT64_C(-0x1234567887654321));
+}
+
+TEST_F(ArtMethodTest, BoxUnboxFloat) {
+ TestBoxUnbox<float, 'F'>(WellKnownClasses::java_lang_Float_valueOf, "floatValue", -2.0f);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxDouble) {
+ TestBoxUnbox<double, 'D'>(WellKnownClasses::java_lang_Double_valueOf, "doubleValue", 8.0);
+}
+
+TEST_F(ArtMethodTest, ArrayList) {
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<4u> hs(self);
+ Handle<mirror::Class> list_class =
+ hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/util/List;"));
+ ASSERT_TRUE(list_class != nullptr);
+ Handle<mirror::Class> abstract_list_class =
+ hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/util/AbstractList;"));
+ ASSERT_TRUE(abstract_list_class != nullptr);
+ Handle<mirror::Class> array_list_class =
+ hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/util/ArrayList;"));
+ ASSERT_TRUE(array_list_class != nullptr);
+ ASSERT_TRUE(abstract_list_class->Implements(list_class.Get()));
+ ASSERT_TRUE(array_list_class->IsSubClass(abstract_list_class.Get()));
+
+ ArtMethod* init = array_list_class->FindClassMethod("<init>", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(init != nullptr);
+ ArtMethod* array_list_size_method =
+ array_list_class->FindClassMethod("size", "()I", kRuntimePointerSize);
+ DCHECK(array_list_size_method != nullptr);
+ ArtMethod* abstract_list_size_method =
+ abstract_list_class->FindClassMethod("size", "()I", kRuntimePointerSize);
+ DCHECK(abstract_list_size_method != nullptr);
+ ArtMethod* list_size_method =
+ list_class->FindInterfaceMethod("size", "()I", kRuntimePointerSize);
+ DCHECK(list_size_method != nullptr);
+
+ Handle<mirror::Object> array_list = init->NewObject<>(hs, self);
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_TRUE(array_list != nullptr);
+
+ // Invoke `ArrayList.size()` directly, with virtual dispatch from
+ // `AbstractList.size()` and with interface dispatch from `List.size()`.
+ int32_t size = array_list_size_method->InvokeInstance<'I'>(self, array_list.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_EQ(0, size);
+ size = abstract_list_size_method->InvokeVirtual<'I'>(self, array_list.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_EQ(0, size);
+ size = list_size_method->InvokeInterface<'I'>(self, array_list.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_EQ(0, size);
+
+ // Try to invoke abstract method `AbstractList.size()` directly.
+ abstract_list_size_method->InvokeInstance<'I'>(self, array_list.Get());
+ ASSERT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+}
+
+} // namespace art
diff --git a/runtime/art_standalone_runtime_compiler_tests.xml b/runtime/art_standalone_runtime_compiler_tests.xml
deleted file mode 100644
index 3ae3a82..0000000
--- a/runtime/art_standalone_runtime_compiler_tests.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
-<configuration description="Runs art_standalone_runtime_compiler_tests.">
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="art_standalone_runtime_compiler_tests->/data/local/tmp/art_standalone_runtime_compiler_tests/art_standalone_runtime_compiler_tests" />
- <option name="append-bitness" value="true" />
- </target_preparer>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/art_standalone_runtime_compiler_tests/art-gtest-jars-Main.jar" />
- <option name="push" value="art-gtest-jars-NonStaticLeafMethods.jar->/data/local/tmp/art_standalone_runtime_compiler_tests/art-gtest-jars-NonStaticLeafMethods.jar" />
- <option name="push" value="art-gtest-jars-StaticLeafMethods.jar->/data/local/tmp/art_standalone_runtime_compiler_tests/art-gtest-jars-StaticLeafMethods.jar" />
- </target_preparer>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="append-bitness" value="true" />
- <option name="push-file" key="generate-boot-image" value="/data/local/tmp/art_standalone_runtime_compiler_tests/generate-boot-image" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="throw-if-cmd-fail" value="true" />
- <option name="run-command" value="mkdir -p /data/local/tmp/art_standalone_runtime_compiler_tests/art_boot_images" />
- <option name="run-command" value="/data/local/tmp/art_standalone_runtime_compiler_tests/generate-boot-image --output-dir=/data/local/tmp/art_standalone_runtime_compiler_tests/art_boot_images" />
- <option name="teardown-command" value="rm -rf /data/local/tmp/art_standalone_runtime_compiler_tests/art_boot_images" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp/art_standalone_runtime_compiler_tests" />
- <option name="module-name" value="art_standalone_runtime_compiler_tests" />
- <option name="ld-library-path-32" value="/apex/com.android.art/lib" />
- <option name="ld-library-path-64" value="/apex/com.android.art/lib64" />
- </test>
-
- <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
- one of the Mainline modules below is present on the device used for testing. -->
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <!-- ART Mainline Module (internal version). -->
- <option name="mainline-module-package-name" value="com.google.android.art" />
- <!-- ART Mainline Module (external (AOSP) version). -->
- <option name="mainline-module-package-name" value="com.android.art" />
- </object>
-
- <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
-</configuration>
diff --git a/runtime/art_standalone_runtime_tests.xml b/runtime/art_standalone_runtime_tests.xml
index 0137aff..b210f5e 100644
--- a/runtime/art_standalone_runtime_tests.xml
+++ b/runtime/art_standalone_runtime_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_runtime_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_runtime_tests->/data/local/tmp/art_standalone_runtime_tests/art_standalone_runtime_tests" />
@@ -48,6 +50,7 @@
<option name="push" value="art-gtest-jars-MyClass.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-MyClass.jar" />
<option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-MyClassNatives.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-Nested.jar" />
+ <option name="push" value="art-gtest-jars-NonStaticLeafMethods.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-NonStaticLeafMethods.jar" />
<option name="push" value="art-gtest-jars-Packages.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-Packages.jar" />
<option name="push" value="art-gtest-jars-ProfileTestMultiDex.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-ProfileTestMultiDex.jar" />
<option name="push" value="art-gtest-jars-ProtoCompare.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-ProtoCompare.jar" />
@@ -93,7 +96,7 @@
<option name="exclude-filter" value="HiddenApiTest.DexDomain_SystemSystemExtFrameworkDir" />
<option name="exclude-filter" value="HiddenApiTest.DexDomain_SystemSystemExtFrameworkDir_MultiDex" />
<option name="exclude-filter" value="JniInternalTest.CallVarArgMethodBadPrimitive" />
- <option name="exclude-filter" value="OatFileAssistantTest.SystemFrameworkDir" />
+ <option name="exclude-filter" value="OatFileAssistantBaseTest.SystemFrameworkDir" />
<option name="exclude-filter" value="StubTest.Fields16" />
<option name="exclude-filter" value="StubTest.Fields32" />
<option name="exclude-filter" value="StubTest.Fields64" />
diff --git a/runtime/backtrace_helper.cc b/runtime/backtrace_helper.cc
index d76a501..260843d 100644
--- a/runtime/backtrace_helper.cc
+++ b/runtime/backtrace_helper.cc
@@ -131,14 +131,6 @@
CHECK_LT(num_frames_, max_depth_);
out_frames_[num_frames_++] = static_cast<uintptr_t>(it->pc);
- // Expected early end: Instrumentation breaks unwinding (b/138296821).
- // Inexact compare because the unwinder does not give us exact return address,
- // but rather it tries to guess the address of the preceding call instruction.
- size_t exit_pc = reinterpret_cast<size_t>(GetQuickInstrumentationExitPc());
- if (exit_pc - 4 <= it->pc && it->pc <= exit_pc) {
- return true;
- }
-
if (kStrictUnwindChecks) {
if (it->function_name.empty()) {
return false;
diff --git a/runtime/backtrace_helper.h b/runtime/backtrace_helper.h
index a74d0e0..9be2550 100644
--- a/runtime/backtrace_helper.h
+++ b/runtime/backtrace_helper.h
@@ -26,7 +26,7 @@
namespace art {
-// Using libbacktrace
+// Using libunwindstack
class BacktraceCollector {
public:
BacktraceCollector(uintptr_t* out_frames, size_t max_depth, size_t skip_count)
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index 5ec24bc..52959bd 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -52,6 +52,10 @@
class BarrierTest : public CommonRuntimeTest {
public:
+ BarrierTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
static int32_t num_threads;
};
diff --git a/runtime/base/locks.h b/runtime/base/locks.h
index 829adff..c15e5de 100644
--- a/runtime/base/locks.h
+++ b/runtime/base/locks.h
@@ -68,12 +68,12 @@
// Can be held while GC related work is done, and thus must be above kMarkSweepMarkStackLock
kThreadWaitLock,
kCHALock,
- kJitCodeCacheLock,
kRosAllocGlobalLock,
kRosAllocBracketLock,
kRosAllocBulkFreeLock,
kAllocSpaceLock,
kTaggingLockLevel,
+ kJitCodeCacheLock,
kTransactionLogLock,
kCustomTlsLock,
kJniFunctionTableLock,
diff --git a/runtime/base/message_queue_test.cc b/runtime/base/message_queue_test.cc
index 7a788a9..09dbc32 100644
--- a/runtime/base/message_queue_test.cc
+++ b/runtime/base/message_queue_test.cc
@@ -20,10 +20,16 @@
#include "common_runtime_test.h"
#include "thread-current-inl.h"
+#include "runtime.h"
namespace art {
-class MessageQueueTest : public CommonRuntimeTest {};
+class MessageQueueTest : public CommonRuntimeTest {
+ protected:
+ MessageQueueTest() {
+ this->use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
namespace {
@@ -81,6 +87,8 @@
}
TEST_F(MessageQueueTest, TwoWayMessaging) {
+ CHECK(Runtime::Current() != nullptr); // Runtime is needed by Mutex.
+
TestMessageQueue queue1;
TestMessageQueue queue2;
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 5709333..728dc84 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -28,6 +28,7 @@
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/value_object.h"
+#include "monitor.h"
#include "mutex-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
@@ -59,18 +60,19 @@
};
#if ART_USE_FUTEXES
+// Compute a relative timespec as *result_ts = lhs - rhs.
+// Return false (and produce an invalid *result_ts) if lhs < rhs.
static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) {
const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds.
+ static_assert(std::is_signed<decltype(result_ts->tv_sec)>::value); // Signed on Linux.
result_ts->tv_sec = lhs.tv_sec - rhs.tv_sec;
result_ts->tv_nsec = lhs.tv_nsec - rhs.tv_nsec;
if (result_ts->tv_nsec < 0) {
result_ts->tv_sec--;
result_ts->tv_nsec += one_sec;
- } else if (result_ts->tv_nsec > one_sec) {
- result_ts->tv_sec++;
- result_ts->tv_nsec -= one_sec;
}
- return result_ts->tv_sec < 0;
+ DCHECK(result_ts->tv_nsec >= 0 && result_ts->tv_nsec < one_sec);
+ return result_ts->tv_sec >= 0;
}
#endif
@@ -462,7 +464,10 @@
do {
timespec timeout_ts;
timeout_ts.tv_sec = 0;
- timeout_ts.tv_nsec = Runtime::Current()->GetMonitorTimeoutNs();
+ // NB: Some tests use the mutex without the runtime.
+ timeout_ts.tv_nsec = Runtime::Current() != nullptr
+ ? Runtime::Current()->GetMonitorTimeoutNs()
+ : Monitor::kDefaultMonitorTimeoutMs;
if (futex(state_and_contenders_.Address(), FUTEX_WAIT_PRIVATE, cur_state,
enable_monitor_timeout_ ? &timeout_ts : nullptr , nullptr, 0) != 0) {
// We only went to sleep after incrementing and contenders and checking that the
@@ -512,6 +517,7 @@
Locks::thread_list_lock_->ExclusiveLock(self);
std::string owner_stack_dump;
pid_t owner_tid = GetExclusiveOwnerTid();
+ CHECK(Runtime::Current() != nullptr);
Thread *owner = Runtime::Current()->GetThreadList()->FindThreadByTid(owner_tid);
if (owner != nullptr) {
if (IsDumpFrequent(owner, try_times)) {
@@ -852,7 +858,7 @@
timespec now_abs_ts;
InitTimeSpec(true, CLOCK_MONOTONIC, 0, 0, &now_abs_ts);
timespec rel_ts;
- if (ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) {
+ if (!ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) {
return false; // Timed out.
}
ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
@@ -869,6 +875,7 @@
// EAGAIN and EINTR both indicate a spurious failure,
// recompute the relative time out from now and try again.
// We don't use TEMP_FAILURE_RETRY so we can recompute rel_ts;
+ num_contenders_.fetch_sub(1); // Unlikely to matter.
PLOG(FATAL) << "timed futex wait failed for " << name_;
}
}
diff --git a/runtime/base/mutex_test.cc b/runtime/base/mutex_test.cc
index 7eba50b..f1b4e49 100644
--- a/runtime/base/mutex_test.cc
+++ b/runtime/base/mutex_test.cc
@@ -21,7 +21,12 @@
namespace art {
-class MutexTest : public CommonRuntimeTest {};
+class MutexTest : public CommonRuntimeTest {
+ protected:
+ MutexTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
struct MutexTester {
static void AssertDepth(Mutex& mu, uint32_t expected_depth) {
@@ -37,6 +42,9 @@
};
TEST_F(MutexTest, LockUnlock) {
+ // TODO: Remove `Mutex` dependency on `Runtime` or at least make sure it works
+ // without a `Runtime` with reasonable defaults (and without dumping stack for timeout).
+ ASSERT_TRUE(Runtime::Current() != nullptr);
Mutex mu("test mutex");
MutexTester::AssertDepth(mu, 0U);
mu.Lock(Thread::Current());
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index abf4f58..c39b44e 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -33,8 +33,6 @@
namespace art {
-constexpr size_t TimingLogger::kIndexNotFound;
-
CumulativeLogger::CumulativeLogger(const std::string& name)
: name_(name),
lock_name_("CumulativeLoggerLock" + name),
diff --git a/runtime/base/timing_logger_test.cc b/runtime/base/timing_logger_test.cc
index 6f8d8cd..38ae9a5 100644
--- a/runtime/base/timing_logger_test.cc
+++ b/runtime/base/timing_logger_test.cc
@@ -16,11 +16,11 @@
#include "timing_logger.h"
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
namespace art {
-class TimingLoggerTest : public CommonRuntimeTest {};
+class TimingLoggerTest : public CommonArtTest {};
// TODO: Negative test cases (improper pairing of EndSplit, etc.)
diff --git a/runtime/cha.cc b/runtime/cha.cc
index d19d4f6..a40afb4 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -144,14 +144,14 @@
ArtMethod* super_method = super_it->
GetVTableEntry<kDefaultVerifyFlags, kWithoutReadBarrier>(vtbl_index, pointer_size);
if (super_method->IsAbstract() &&
- super_method->HasSingleImplementation<kWithoutReadBarrier>() &&
+ super_method->HasSingleImplementation() &&
super_method->GetSingleImplementation(pointer_size) == method) {
// Do like there was no single implementation defined previously
// for this method of the superclass.
super_method->SetSingleImplementation(nullptr, pointer_size);
} else {
// No related SingleImplementations could possibly be found any further.
- DCHECK(!super_method->HasSingleImplementation<kWithoutReadBarrier>());
+ DCHECK(!super_method->HasSingleImplementation());
break;
}
}
@@ -168,7 +168,7 @@
j < count;
++j) {
ArtMethod* method = interface->GetVirtualMethod(j, pointer_size);
- if (method->HasSingleImplementation<kWithoutReadBarrier>() &&
+ if (method->HasSingleImplementation() &&
alloc->ContainsUnsafe(method->GetSingleImplementation(pointer_size)) &&
!method->IsDefault()) {
// Do like there was no single implementation defined previously for this method.
@@ -692,8 +692,9 @@
}
}
-void ClassHierarchyAnalysis::RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc) {
- MutexLock mu(Thread::Current(), *Locks::cha_lock_);
+void ClassHierarchyAnalysis::RemoveDependenciesForLinearAlloc(Thread* self,
+ const LinearAlloc* linear_alloc) {
+ MutexLock mu(self, *Locks::cha_lock_);
for (auto it = cha_dependency_map_.begin(); it != cha_dependency_map_.end(); ) {
// Use unsafe to avoid locking since the allocator is going to be deleted.
if (linear_alloc->ContainsUnsafe(it->first)) {
diff --git a/runtime/cha.h b/runtime/cha.h
index 14af43e..5a403ec 100644
--- a/runtime/cha.h
+++ b/runtime/cha.h
@@ -126,7 +126,7 @@
// Remove all of the dependencies for a linear allocator. This is called when dex cache unloading
// occurs.
- void RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc)
+ void RemoveDependenciesForLinearAlloc(Thread* self, const LinearAlloc* linear_alloc)
REQUIRES(!Locks::cha_lock_);
private:
diff --git a/runtime/cha_test.cc b/runtime/cha_test.cc
index c60720f..48fd06d 100644
--- a/runtime/cha_test.cc
+++ b/runtime/cha_test.cc
@@ -16,11 +16,12 @@
#include "cha.h"
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
+#include "thread-current-inl.h"
namespace art {
-class CHATest : public CommonRuntimeTest {};
+class CHATest : public CommonArtTest {};
// Mocks some methods.
#define METHOD1 (reinterpret_cast<ArtMethod*>(8u))
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index a7c3e45..d03139b 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -91,7 +91,7 @@
CodeItemDataAccessor accessor(m->DexInstructionData());
uint16_t number_of_dex_registers = accessor.RegistersSize();
- if (!Runtime::Current()->IsAsyncDeoptimizeable(GetCurrentQuickFramePc())) {
+ if (!Runtime::Current()->IsAsyncDeoptimizeable(GetOuterMethod(), GetCurrentQuickFramePc())) {
// We can only guarantee dex register info presence for debuggable methods.
return;
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index b79f3f5..8b6d45d 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -305,129 +305,32 @@
return resolved;
}
-template <InvokeType type, ClassLinker::ResolveMode kResolveMode>
-inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) {
- DCHECK(referrer != nullptr);
- // Note: The referrer can be a Proxy constructor. In that case, we need to do the
- // lookup in the context of the original method from where it steals the code.
- // However, we delay the GetInterfaceMethodIfProxy() until needed.
- DCHECK_IMPLIES(referrer->IsProxyMethod(), referrer->IsConstructor());
- // We do not need the read barrier for getting the DexCache for the initial resolved method
- // lookup as both from-space and to-space copies point to the same native resolved methods array.
- ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
- method_idx);
- if (resolved_method == nullptr) {
- return nullptr;
- }
- DCHECK(!resolved_method->IsRuntimeMethod());
- if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
- referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
- // Check if the invoke type matches the class type.
- ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
- ObjPtr<mirror::ClassLoader> class_loader = referrer->GetClassLoader();
- const dex::MethodId& method_id = referrer->GetDexFile()->GetMethodId(method_idx);
- ObjPtr<mirror::Class> cls = LookupResolvedType(method_id.class_idx_, dex_cache, class_loader);
- if (cls == nullptr) {
- // The verifier breaks the invariant that a resolved method must have its
- // class in the class table. Because this method should only lookup and not
- // resolve class, return null. The caller is responsible for calling
- // `ResolveMethod` afterwards.
- // b/73760543
- return nullptr;
- }
- if (CheckInvokeClassMismatch</* kThrow= */ false>(dex_cache, type, method_idx, class_loader)) {
- return nullptr;
- }
- // Check access.
- ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- if (!referring_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
- resolved_method,
- dex_cache,
- method_idx)) {
- return nullptr;
- }
- // Check if the invoke type matches the method type.
- if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
- return nullptr;
- }
- }
- return resolved_method;
-}
-
template <ClassLinker::ResolveMode kResolveMode>
inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
uint32_t method_idx,
ArtMethod* referrer,
InvokeType type) {
DCHECK(referrer != nullptr);
- // Note: The referrer can be a Proxy constructor. In that case, we need to do the
- // lookup in the context of the original method from where it steals the code.
- // However, we delay the GetInterfaceMethodIfProxy() until needed.
DCHECK_IMPLIES(referrer->IsProxyMethod(), referrer->IsConstructor());
+
Thread::PoisonObjectPointersIfDebug();
- // We do not need the read barrier for getting the DexCache for the initial resolved method
- // lookup as both from-space and to-space copies point to the same native resolved methods array.
- ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
- method_idx);
- DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod());
- if (UNLIKELY(resolved_method == nullptr)) {
- referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
- ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache()));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- resolved_method = ResolveMethod<kResolveMode>(method_idx,
- h_dex_cache,
- h_class_loader,
- referrer,
- type);
- } else if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
- referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
- const dex::MethodId& method_id = referrer->GetDexFile()->GetMethodId(method_idx);
- ObjPtr<mirror::Class> cls =
- LookupResolvedType(method_id.class_idx_,
- referrer->GetDexCache(),
- referrer->GetClassLoader());
- if (cls == nullptr) {
- // The verifier breaks the invariant that a resolved method must have its
- // class in the class table, so resolve the type in case we haven't found it.
- // b/73760543
- StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache()));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(referrer->GetClassLoader()));
- cls = ResolveType(method_id.class_idx_, h_dex_cache, h_class_loader);
- if (hs.Self()->IsExceptionPending()) {
- return nullptr;
- }
- }
- // Check if the invoke type matches the class type.
- if (CheckInvokeClassMismatch</* kThrow= */ true>(
- referrer->GetDexCache(), type, [cls]() { return cls; })) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr;
- }
- // Check access.
- ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- if (!referring_class->CheckResolvedMethodAccess(resolved_method->GetDeclaringClass(),
- resolved_method,
- referrer->GetDexCache(),
- method_idx,
- type)) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr;
- }
- // Check if the invoke type matches the method type.
- if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
- ThrowIncompatibleClassChangeError(type,
- resolved_method->GetInvokeType(),
- resolved_method,
- referrer);
- return nullptr;
+ // Fast path: no checks and in the dex cache.
+ if (kResolveMode == ResolveMode::kNoChecks) {
+ ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod(method_idx);
+ if (resolved_method != nullptr) {
+ DCHECK(!resolved_method->IsRuntimeMethod());
+ return resolved_method;
}
}
- // Note: We cannot check here to see whether we added the method to the cache. It
- // might be an erroneous class, which results in it being hidden from us.
- return resolved_method;
+
+ // For a Proxy constructor, we need to do the lookup in the context of the original method
+ // from where it steals the code.
+ referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(referrer->GetDeclaringClass()->GetClassLoader()));
+ return ResolveMethod<kResolveMode>(method_idx, dex_cache, class_loader, referrer, type);
}
template <ClassLinker::ResolveMode kResolveMode>
@@ -436,10 +339,11 @@
Handle<mirror::ClassLoader> class_loader,
ArtMethod* referrer,
InvokeType type) {
+ DCHECK(dex_cache != nullptr);
DCHECK(dex_cache->GetClassLoader() == class_loader.Get());
DCHECK(!Thread::Current()->IsExceptionPending()) << Thread::Current()->GetException()->Dump();
- DCHECK(dex_cache != nullptr);
DCHECK(referrer == nullptr || !referrer->IsProxyMethod());
+
// Check for hit in the dex cache.
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
Thread::PoisonObjectPointersIfDebug();
@@ -476,6 +380,11 @@
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
+ // Look for the method again in case the type resolution updated the cache.
+ resolved = dex_cache->GetResolvedMethod(method_idx);
+ if (kResolveMode == ResolveMode::kNoChecks && resolved != nullptr) {
+ return resolved;
+ }
}
// Check if the invoke type matches the class type.
@@ -494,12 +403,20 @@
if (kResolveMode == ResolveMode::kCheckICCEAndIAE && resolved != nullptr && referrer != nullptr) {
ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass();
ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- if (!referring_class->CheckResolvedMethodAccess(methods_class,
- resolved,
- dex_cache.Get(),
- method_idx,
- type)) {
- DCHECK(Thread::Current()->IsExceptionPending());
+ if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
+ // The referrer class can't access the method's declaring class but may still be able
+ // to access the method if the MethodId specifies an accessible subclass of the declaring
+ // class rather than the declaring class itself.
+ if (UNLIKELY(!referring_class->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClassForMethodDispatch(referring_class,
+ klass,
+ resolved,
+ type);
+ return nullptr;
+ }
+ }
+ if (UNLIKELY(!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags()))) {
+ ThrowIllegalAccessErrorMethod(referring_class, resolved);
return nullptr;
}
}
@@ -509,23 +426,23 @@
LIKELY(kResolveMode == ResolveMode::kNoChecks ||
!resolved->CheckIncompatibleClassChange(type))) {
return resolved;
- } else {
- // If we had a method, or if we can find one with another lookup type,
- // it's an incompatible-class-change error.
- if (resolved == nullptr) {
- resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
- }
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
- } else {
- // We failed to find the method (using all lookup types), so throw a NoSuchMethodError.
- const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
- const Signature signature = dex_file.GetMethodSignature(method_id);
- ThrowNoSuchMethodError(type, klass, name, signature);
- }
- Thread::Current()->AssertPendingException();
- return nullptr;
}
+
+ // If we had a method, or if we can find one with another lookup type,
+ // it's an incompatible-class-change error.
+ if (resolved == nullptr) {
+ resolved = FindIncompatibleMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
+ }
+ if (resolved != nullptr) {
+ ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
+ } else {
+ // We failed to find the method (using all lookup types), so throw a NoSuchMethodError.
+ const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ ThrowNoSuchMethodError(type, klass, name, signature);
+ }
+ Thread::Current()->AssertPendingException();
+ return nullptr;
}
inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx,
@@ -584,6 +501,12 @@
return nullptr;
}
+ // Look for the field again in case the type resolution updated the cache.
+ resolved = dex_cache->GetResolvedField(field_idx);
+ if (resolved != nullptr) {
+ return resolved;
+ }
+
resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static);
if (resolved == nullptr) {
const char* name = dex_file.GetFieldName(field_id);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 444cc63..c772a9c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -37,6 +37,7 @@
#include "art_method-inl.h"
#include "barrier.h"
#include "base/arena_allocator.h"
+#include "base/arena_bit_vector.h"
#include "base/casts.h"
#include "base/file_utils.h"
#include "base/hash_map.h"
@@ -271,7 +272,6 @@
}
void Run(Thread* self) override {
- self->ClearMakeVisiblyInitializedCounter();
AdjustThreadVisibilityCounter(self, -1);
}
@@ -299,7 +299,13 @@
}
}
- static constexpr size_t kMaxClasses = 16;
+ // Making classes initialized in bigger batches helps with app startup for
+ // apps that initialize a lot of classes by running fewer checkpoints.
+ // (On the other hand, bigger batches make class initialization checks more
+ // likely to take a slow path but that is mitigated by making partially
+ // filled buffers visibly initialized if we take the slow path many times.
+ // See `Thread::kMakeVisiblyInitializedCounterTriggerCount`.)
+ static constexpr size_t kMaxClasses = 48;
ClassLinker* const class_linker_;
size_t num_classes_;
@@ -322,6 +328,7 @@
}
std::optional<Barrier> maybe_barrier; // Avoid constructing the Barrier for `wait == false`.
if (wait) {
+ Locks::mutator_lock_->AssertNotHeld(self);
maybe_barrier.emplace(0);
}
int wait_count = 0;
@@ -535,10 +542,9 @@
static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
Thread* self = Thread::Current();
- JNIEnv* env = self->GetJniEnv();
- ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
- CHECK(cause.get() != nullptr);
+ ObjPtr<mirror::Throwable> cause = self->GetException();
+ CHECK(cause != nullptr);
// Boot classpath classes should not fail initialization. This is a consistency debug check.
// This cannot in general be guaranteed, but in all likelihood leads to breakage down the line.
@@ -553,12 +559,8 @@
<< self->GetException()->Dump();
}
- env->ExceptionClear();
- bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error);
- env->Throw(cause.get());
-
// We only wrap non-Error exceptions; an Error can just be used as-is.
- if (!is_error) {
+ if (!cause->IsError()) {
self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr);
}
VlogClassInitializationFailure(klass);
@@ -1085,22 +1087,116 @@
VLOG(startup) << "ClassLinker::FinishInit exiting";
}
-void ClassLinker::RunRootClinits(Thread* self) {
- for (size_t i = 0; i < static_cast<size_t>(ClassRoot::kMax); ++i) {
- ObjPtr<mirror::Class> c = GetClassRoot(ClassRoot(i), this);
- if (!c->IsArrayClass() && !c->IsPrimitive()) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(c));
- if (!EnsureInitialized(self, h_class, true, true)) {
- LOG(FATAL) << "Exception when initializing " << h_class->PrettyClass()
- << ": " << self->GetException()->Dump();
- }
- } else {
- DCHECK(c->IsInitialized());
+static void EnsureRootInitialized(ClassLinker* class_linker,
+ Thread* self,
+ ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!klass->IsVisiblyInitialized()) {
+ DCHECK(!klass->IsArrayClass());
+ DCHECK(!klass->IsPrimitive());
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(klass));
+ if (!class_linker->EnsureInitialized(
+ self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true)) {
+ LOG(FATAL) << "Exception when initializing " << h_class->PrettyClass()
+ << ": " << self->GetException()->Dump();
}
}
}
+void ClassLinker::RunEarlyRootClinits(Thread* self) {
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots = hs.NewHandle(GetClassRoots());
+ EnsureRootInitialized(this, self, GetClassRoot<mirror::Class>(class_roots.Get()));
+ EnsureRootInitialized(this, self, GetClassRoot<mirror::String>(class_roots.Get()));
+ // `Field` class is needed for register_java_net_InetAddress in libcore, b/28153851.
+ EnsureRootInitialized(this, self, GetClassRoot<mirror::Field>(class_roots.Get()));
+
+ WellKnownClasses::Init(self->GetJniEnv());
+
+ // `FinalizerReference` class is needed for initialization of `java.net.InetAddress`.
+ // (Indirectly by constructing a `ObjectStreamField` which uses a `StringBuilder`
+ // and, when resizing, initializes the `System` class for `System.arraycopy()`
+ // and `System.<clinit> creates a finalizable object.)
+ EnsureRootInitialized(
+ this, self, WellKnownClasses::java_lang_ref_FinalizerReference_add->GetDeclaringClass());
+}
+
+void ClassLinker::RunRootClinits(Thread* self) {
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots = hs.NewHandle(GetClassRoots());
+ for (size_t i = 0; i < static_cast<size_t>(ClassRoot::kMax); ++i) {
+ EnsureRootInitialized(this, self, GetClassRoot(ClassRoot(i), class_roots.Get()));
+ }
+
+ // Make sure certain well-known classes are initialized. Note that well-known
+ // classes are always in the boot image, so this code is primarily intended
+ // for running without boot image but may be needed for boot image if the
+ // AOT-initialization fails due to introduction of new code to `<clinit>`.
+ ArtMethod* static_methods_of_classes_to_initialize[] = {
+ // Initialize primitive boxing classes (avoid check at runtime).
+ WellKnownClasses::java_lang_Boolean_valueOf,
+ WellKnownClasses::java_lang_Byte_valueOf,
+ WellKnownClasses::java_lang_Character_valueOf,
+ WellKnownClasses::java_lang_Double_valueOf,
+ WellKnownClasses::java_lang_Float_valueOf,
+ WellKnownClasses::java_lang_Integer_valueOf,
+ WellKnownClasses::java_lang_Long_valueOf,
+ WellKnownClasses::java_lang_Short_valueOf,
+ // Initialize `StackOverflowError`.
+ WellKnownClasses::java_lang_StackOverflowError_init,
+ // Ensure class loader classes are initialized (avoid check at runtime).
+ // Superclass `ClassLoader` is a class root and already initialized above.
+ // Superclass `BaseDexClassLoader` is initialized implicitly.
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader_init,
+ WellKnownClasses::dalvik_system_DexClassLoader_init,
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader_init,
+ WellKnownClasses::dalvik_system_PathClassLoader_init,
+ WellKnownClasses::java_lang_BootClassLoader_init,
+ // Ensure `Daemons` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_Daemons_start,
+ // Ensure `Thread` and `ThreadGroup` classes are initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_Thread_init,
+ WellKnownClasses::java_lang_ThreadGroup_add,
+ // Ensure reference classes are initialized (avoid check at runtime).
+ // The `FinalizerReference` class was initialized in `RunEarlyRootClinits()`.
+ WellKnownClasses::java_lang_ref_ReferenceQueue_add,
+ // Ensure `InvocationTargetException` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_reflect_InvocationTargetException_init,
+ // Ensure `Parameter` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_reflect_Parameter_init,
+ // Ensure `MethodHandles` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_invoke_MethodHandles_lookup,
+ // Ensure `DirectByteBuffer` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_nio_DirectByteBuffer_init,
+ // Ensure `FloatingDecimal` class is initialized (avoid check at runtime).
+ WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D,
+ // Ensure reflection annotation classes are initialized (avoid check at runtime).
+ WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation,
+ WellKnownClasses::libcore_reflect_AnnotationMember_init,
+ // We're suppressing exceptions from `DdmServer` and we do not want to repeatedly
+ // suppress class initialization error (say, due to OOM), so initialize it early.
+ WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch,
+ };
+ for (ArtMethod* method : static_methods_of_classes_to_initialize) {
+ EnsureRootInitialized(this, self, method->GetDeclaringClass());
+ }
+ ArtField* static_fields_of_classes_to_initialize[] = {
+ // Ensure classes used by class loaders are initialized (avoid check at runtime).
+ WellKnownClasses::dalvik_system_DexFile_cookie,
+ WellKnownClasses::dalvik_system_DexPathList_dexElements,
+ WellKnownClasses::dalvik_system_DexPathList__Element_dexFile,
+ // Ensure `VMRuntime` is initialized (avoid check at runtime).
+ WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer,
+ // Initialize empty arrays needed by `StackOverflowError`.
+ WellKnownClasses::java_util_Collections_EMPTY_LIST,
+ WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT,
+ };
+ for (ArtField* field : static_fields_of_classes_to_initialize) {
+ EnsureRootInitialized(this, self, field->GetDeclaringClass());
+ }
+}
+
ALWAYS_INLINE
static uint32_t ComputeMethodHash(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!method->IsRuntimeMethod());
@@ -1304,6 +1400,7 @@
std::vector<std::unique_ptr<const DexFile>> dex_files;
if (!AddImageSpace(spaces[i],
ScopedNullHandle<mirror::ClassLoader>(),
+ /* class_loader_context= */ nullptr,
/*out*/&dex_files,
error_msg)) {
return false;
@@ -1339,11 +1436,9 @@
}
}
-bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::ClassLoader> class_loader) {
+bool ClassLinker::IsBootClassLoader(ObjPtr<mirror::Object> class_loader) {
return class_loader == nullptr ||
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader) ==
- class_loader->GetClass();
+ WellKnownClasses::java_lang_BootClassLoader == class_loader->GetClass();
}
class CHAOnDeleteUpdateClassVisitor {
@@ -1733,112 +1828,113 @@
// Helper class for ArtMethod checks when adding an image. Keeps all required functionality
// together and caches some intermediate results.
+template <PointerSize kPointerSize>
class ImageChecker final {
public:
- static void CheckObjects(gc::Heap* heap, ClassLinker* class_linker)
+ static void CheckObjects(gc::Heap* heap, gc::space::ImageSpace* space)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ImageChecker ic(heap, class_linker);
+ // There can be no GC during boot image initialization, so we do not need read barriers.
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
+
+ CHECK_EQ(kPointerSize, space->GetImageHeader().GetPointerSize());
+ const ImageSection& objects_section = space->GetImageHeader().GetObjectsSection();
+ uintptr_t space_begin = reinterpret_cast<uintptr_t>(space->Begin());
+ uintptr_t objects_begin = space_begin + objects_section.Offset();
+ uintptr_t objects_end = objects_begin + objects_section.Size();
+ ImageChecker ic(heap);
auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
- CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj;
- CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj;
- if (obj->IsClass()) {
+ mirror::Class* obj_klass = obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ CHECK(obj_klass != nullptr) << "Null class in object " << obj;
+ mirror::Class* class_class = obj_klass->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ CHECK(class_class != nullptr) << "Null class class " << obj;
+ if (obj_klass == class_class) {
auto klass = obj->AsClass();
for (ArtField& field : klass->GetIFields()) {
- CHECK_EQ(field.GetDeclaringClass(), klass);
+ CHECK_EQ(field.GetDeclaringClass<kWithoutReadBarrier>(), klass);
}
for (ArtField& field : klass->GetSFields()) {
- CHECK_EQ(field.GetDeclaringClass(), klass);
+ CHECK_EQ(field.GetDeclaringClass<kWithoutReadBarrier>(), klass);
}
- const PointerSize pointer_size = ic.pointer_size_;
- for (ArtMethod& m : klass->GetMethods(pointer_size)) {
+ for (ArtMethod& m : klass->GetMethods(kPointerSize)) {
ic.CheckArtMethod(&m, klass);
}
- ObjPtr<mirror::PointerArray> vtable = klass->GetVTable();
+ ObjPtr<mirror::PointerArray> vtable =
+ klass->GetVTable<kDefaultVerifyFlags, kWithoutReadBarrier>();
if (vtable != nullptr) {
- ic.CheckArtMethodPointerArray(vtable, nullptr);
+ ic.CheckArtMethodPointerArray(vtable);
}
if (klass->ShouldHaveImt()) {
- ImTable* imt = klass->GetImt(pointer_size);
+ ImTable* imt = klass->GetImt(kPointerSize);
for (size_t i = 0; i < ImTable::kSize; ++i) {
- ic.CheckArtMethod(imt->Get(i, pointer_size), nullptr);
+ ic.CheckArtMethod(imt->Get(i, kPointerSize), /*expected_class=*/ nullptr);
}
}
if (klass->ShouldHaveEmbeddedVTable()) {
for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
- ic.CheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr);
+ ic.CheckArtMethod(klass->GetEmbeddedVTableEntry(i, kPointerSize),
+ /*expected_class=*/ nullptr);
}
}
- ObjPtr<mirror::IfTable> iftable = klass->GetIfTable();
- for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
- if (iftable->GetMethodArrayCount(i) > 0) {
- ic.CheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr);
+ ObjPtr<mirror::IfTable> iftable =
+ klass->GetIfTable<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ int32_t iftable_count = (iftable != nullptr) ? iftable->Count() : 0;
+ for (int32_t i = 0; i < iftable_count; ++i) {
+ ObjPtr<mirror::PointerArray> method_array =
+ iftable->GetMethodArrayOrNull<kDefaultVerifyFlags, kWithoutReadBarrier>(i);
+ if (method_array != nullptr) {
+ ic.CheckArtMethodPointerArray(method_array);
}
}
}
};
- heap->VisitObjects(visitor);
+ space->GetLiveBitmap()->VisitMarkedRange(objects_begin, objects_end, visitor);
}
private:
- ImageChecker(gc::Heap* heap, ClassLinker* class_linker)
- : spaces_(heap->GetBootImageSpaces()),
- pointer_size_(class_linker->GetImagePointerSize()) {
- space_begin_.reserve(spaces_.size());
- method_sections_.reserve(spaces_.size());
- runtime_method_sections_.reserve(spaces_.size());
- for (gc::space::ImageSpace* space : spaces_) {
+ explicit ImageChecker(gc::Heap* heap) {
+ ArrayRef<gc::space::ImageSpace* const> spaces(heap->GetBootImageSpaces());
+ space_begin_.reserve(spaces.size());
+ for (gc::space::ImageSpace* space : spaces) {
+ CHECK_EQ(static_cast<const void*>(space->Begin()), &space->GetImageHeader());
space_begin_.push_back(space->Begin());
- auto& header = space->GetImageHeader();
- method_sections_.push_back(&header.GetMethodsSection());
- runtime_method_sections_.push_back(&header.GetRuntimeMethodsSection());
}
}
void CheckArtMethod(ArtMethod* m, ObjPtr<mirror::Class> expected_class)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
if (m->IsRuntimeMethod()) {
- ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClassUnchecked();
CHECK(declaring_class == nullptr) << declaring_class << " " << m->PrettyMethod();
} else if (m->IsCopied()) {
- CHECK(m->GetDeclaringClass() != nullptr) << m->PrettyMethod();
+ CHECK(declaring_class != nullptr) << m->PrettyMethod();
} else if (expected_class != nullptr) {
- CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << m->PrettyMethod();
+ CHECK_EQ(declaring_class, expected_class) << m->PrettyMethod();
}
- if (!spaces_.empty()) {
- bool contains = false;
- for (size_t i = 0; !contains && i != space_begin_.size(); ++i) {
- const size_t offset = reinterpret_cast<uint8_t*>(m) - space_begin_[i];
- contains = method_sections_[i]->Contains(offset) ||
- runtime_method_sections_[i]->Contains(offset);
+ bool contains = false;
+ for (const uint8_t* begin : space_begin_) {
+ const size_t offset = reinterpret_cast<uint8_t*>(m) - begin;
+ const ImageHeader* header = reinterpret_cast<const ImageHeader*>(begin);
+ if (header->GetMethodsSection().Contains(offset) ||
+ header->GetRuntimeMethodsSection().Contains(offset)) {
+ contains = true;
+ break;
}
- CHECK(contains) << m << " not found";
}
+ CHECK(contains) << m << " not found";
}
- void CheckArtMethodPointerArray(ObjPtr<mirror::PointerArray> arr,
- ObjPtr<mirror::Class> expected_class)
+ void CheckArtMethodPointerArray(ObjPtr<mirror::PointerArray> arr)
REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
- auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size_);
- // expected_class == null means we are a dex cache.
- if (expected_class != nullptr) {
- CHECK(method != nullptr);
- }
- if (method != nullptr) {
- CheckArtMethod(method, expected_class);
- }
+ auto* method = arr->GetElementPtrSize<ArtMethod*>(j, kPointerSize);
+ CHECK(method != nullptr);
+ CheckArtMethod(method, /*expected_class=*/ nullptr);
}
}
- const std::vector<gc::space::ImageSpace*>& spaces_;
- const PointerSize pointer_size_;
-
- // Cached sections from the spaces.
std::vector<const uint8_t*> space_begin_;
- std::vector<const ImageSection*> method_sections_;
- std::vector<const ImageSection*> runtime_method_sections_;
};
static void VerifyAppImage(const ImageHeader& header,
@@ -1876,6 +1972,7 @@
bool ClassLinker::AddImageSpace(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
+ ClassLoaderContext* context,
std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
std::string* error_msg) {
DCHECK(out_dex_files != nullptr);
@@ -1907,9 +2004,8 @@
hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
- MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
- app_image ? header.GetImageRoot(ImageHeader::kAppImageClassLoader)->AsClassLoader()
- : nullptr));
+ MutableHandle<mirror::Object> special_root(hs.NewHandle(
+ app_image ? header.GetImageRoot(ImageHeader::kSpecialRoots) : nullptr));
DCHECK(class_roots != nullptr);
if (class_roots->GetLength() != static_cast<int32_t>(ClassRoot::kMax)) {
*error_msg = StringPrintf("Expected %d class roots but got %d",
@@ -1955,17 +2051,52 @@
}
if (app_image) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- ScopedAssertNoThreadSuspension sants("Checking app image", soa.Self());
- if (IsBootClassLoader(soa, image_class_loader.Get())) {
+ ScopedAssertNoThreadSuspension sants("Checking app image");
+ if (special_root == nullptr) {
+ *error_msg = "Unexpected null special root in app image";
+ return false;
+ } else if (special_root->IsObjectArray()) {
+ ObjPtr<mirror::IntArray> checksums =
+ special_root->AsObjectArray<mirror::Object>()->Get(1)->AsIntArray();
+ size_t count = checksums->GetLength();
+ if (oat_file->GetVdexFile()->GetNumberOfDexFiles() != count) {
+ *error_msg = "Checksums count does not match";
+ return false;
+ }
+ static_assert(sizeof(VdexFile::VdexChecksum) == sizeof(int32_t));
+ const VdexFile::VdexChecksum* art_checksums =
+ reinterpret_cast<VdexFile::VdexChecksum*>(checksums->GetData());
+ const VdexFile::VdexChecksum* vdex_checksums =
+ oat_file->GetVdexFile()->GetDexChecksumsArray();
+ if (memcmp(art_checksums, vdex_checksums, sizeof(VdexFile::VdexChecksum) * count) != 0) {
+ *error_msg = "Image and vdex checksums did not match";
+ return false;
+ }
+ ObjPtr<mirror::String> encoded_context =
+ special_root->AsObjectArray<mirror::Object>()->Get(0)->AsString();
+ std::string encoded_context_str = encoded_context->ToModifiedUtf8();
+ if (context->VerifyClassLoaderContextMatch(encoded_context_str.c_str()) ==
+ ClassLoaderContext::VerificationResult::kMismatch) {
+ *error_msg =
+ StringPrintf("Class loader contexts don't match: %s", encoded_context_str.c_str());
+ return false;
+ }
+ } else if (IsBootClassLoader(special_root.Get())) {
*error_msg = "Unexpected BootClassLoader in app image";
return false;
+ } else if (!special_root->IsClassLoader()) {
+ *error_msg = "Unexpected special root in app image";
+ return false;
}
}
if (kCheckImageObjects) {
if (!app_image) {
- ImageChecker::CheckObjects(heap, this);
+ if (image_pointer_size_ == PointerSize::k64) {
+ ImageChecker<PointerSize::k64>::CheckObjects(heap, space);
+ } else {
+ ImageChecker<PointerSize::k32>::CheckObjects(heap, space);
+ }
}
}
@@ -2013,8 +2144,7 @@
// Set image methods' entry point that point to the nterp trampoline to the
// nterp entry point. This allows taking the fast path when doing a
// nterp->nterp call.
- DCHECK_IMPLIES(NeedsClinitCheckBeforeCall(&method),
- method.GetDeclaringClass()->IsVisiblyInitialized());
+ DCHECK(!method.StillNeedsClinitCheck());
method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpEntryPoint());
} else {
method.SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
@@ -2025,7 +2155,7 @@
if (runtime->IsVerificationSoftFail()) {
header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!method.IsNative() && method.IsInvokable()) {
+ if (method.IsManagedAndInvokable()) {
method.ClearSkipAccessChecks();
}
}, space->Begin(), image_pointer_size_);
@@ -2063,9 +2193,11 @@
ObjPtr<mirror::Class> klass(root.Read());
// Do not update class loader for boot image classes where the app image
// class loader is only the initiating loader but not the defining loader.
- // Avoid read barrier since we are comparing against null.
- if (klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+ if (space->HasAddress(klass.Ptr())) {
klass->SetClassLoader(loader);
+ } else {
+ DCHECK(klass->IsBootStrapClassLoaded());
+ DCHECK(Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass.Ptr()));
}
}
}
@@ -2376,7 +2508,7 @@
}
} else if (cha_ != nullptr) {
// If we don't have a JIT, we need to manually remove the CHA dependencies manually.
- cha_->RemoveDependenciesForLinearAlloc(data.allocator);
+ cha_->RemoveDependenciesForLinearAlloc(self, data.allocator);
}
// Cleanup references to single implementation ArtMethods that will be deleted.
if (cleanup_cha) {
@@ -2662,19 +2794,16 @@
} \
} while (0)
-bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInSharedLibraries(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
- return FindClassInSharedLibrariesHelper(soa, self, descriptor, hash, class_loader, field, result);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
+ return FindClassInSharedLibrariesHelper(self, descriptor, hash, class_loader, field, result);
}
-bool ClassLinker::FindClassInSharedLibrariesHelper(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInSharedLibrariesHelper(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -2692,38 +2821,35 @@
for (auto loader : shared_libraries.Iterate<mirror::ClassLoader>()) {
temp_loader.Assign(loader);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result),
+ FindClassInBaseDexClassLoader(self, descriptor, hash, temp_loader, result),
*result,
self);
}
return true;
}
-bool ClassLinker::FindClassInSharedLibrariesAfter(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInSharedLibrariesAfter(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
- ArtField* field = jni::DecodeArtField(
- WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter);
- return FindClassInSharedLibrariesHelper(soa, self, descriptor, hash, class_loader, field, result);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
+ return FindClassInSharedLibrariesHelper(self, descriptor, hash, class_loader, field, result);
}
-bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInBaseDexClassLoader(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
// Termination case: boot class loader.
- if (IsBootClassLoader(soa, class_loader.Get())) {
+ if (IsBootClassLoader(class_loader.Get())) {
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
FindClassInBootClassLoaderClassPath(self, descriptor, hash, result), *result, self);
return true;
}
- if (IsPathOrDexClassLoader(soa, class_loader) || IsInMemoryDexClassLoader(soa, class_loader)) {
+ if (IsPathOrDexClassLoader(class_loader) || IsInMemoryDexClassLoader(class_loader)) {
// For regular path or dex class loader the search order is:
// - parent
// - shared libraries
@@ -2733,19 +2859,19 @@
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result),
+ FindClassInBaseDexClassLoader(self, descriptor, hash, h_parent, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibraries(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader, result),
+ FindClassInBaseDexClassLoaderClassPath(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibrariesAfter(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibrariesAfter(self, descriptor, hash, class_loader, result),
*result,
self);
// We did not find a class, but the class loader chain was recognized, so we
@@ -2753,7 +2879,7 @@
return true;
}
- if (IsDelegateLastClassLoader(soa, class_loader)) {
+ if (IsDelegateLastClassLoader(class_loader)) {
// For delegate last, the search order is:
// - boot class path
// - shared libraries
@@ -2762,15 +2888,15 @@
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
FindClassInBootClassLoaderClassPath(self, descriptor, hash, result), *result, self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibraries(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader, result),
+ FindClassInBaseDexClassLoaderClassPath(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibrariesAfter(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibrariesAfter(self, descriptor, hash, class_loader, result),
*result,
self);
@@ -2778,7 +2904,7 @@
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result),
+ FindClassInBaseDexClassLoader(self, descriptor, hash, h_parent, result),
*result,
self);
// We did not find a class, but the class loader chain was recognized, so we
@@ -2847,14 +2973,14 @@
}
bool ClassLinker::FindClassInBaseDexClassLoaderClassPath(
- ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
- DCHECK(IsPathOrDexClassLoader(soa, class_loader) ||
- IsInMemoryDexClassLoader(soa, class_loader) ||
- IsDelegateLastClassLoader(soa, class_loader))
+ DCHECK(IsPathOrDexClassLoader(class_loader) ||
+ IsInMemoryDexClassLoader(class_loader) ||
+ IsDelegateLastClassLoader(class_loader))
<< "Unexpected class loader for descriptor " << descriptor;
const DexFile* dex_file = nullptr;
@@ -2869,15 +2995,15 @@
}
return true; // Continue with the next DexFile.
};
- VisitClassLoaderDexFiles(soa, class_loader, find_class_def);
+ VisitClassLoaderDexFiles(self, class_loader, find_class_def);
if (class_def != nullptr) {
- *result = DefineClass(soa.Self(), descriptor, hash, class_loader, *dex_file, *class_def);
+ *result = DefineClass(self, descriptor, hash, class_loader, *dex_file, *class_def);
if (UNLIKELY(*result == nullptr)) {
- CHECK(soa.Self()->IsExceptionPending()) << descriptor;
- FilterDexFileCaughtExceptions(soa.Self(), this);
+ CHECK(self->IsExceptionPending()) << descriptor;
+ FilterDexFileCaughtExceptions(self, this);
} else {
- DCHECK(!soa.Self()->IsExceptionPending());
+ DCHECK(!self->IsExceptionPending());
}
}
// A BaseDexClassLoader is always a known lookup.
@@ -2933,7 +3059,7 @@
} else {
ScopedObjectAccessUnchecked soa(self);
bool known_hierarchy =
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
+ FindClassInBaseDexClassLoader(self, descriptor, hash, class_loader, &result_ptr);
if (result_ptr != nullptr) {
// The chain was understood and we found the class. We still need to add the class to
// the class table to protect from racy programs that can try and redefine the path list
@@ -2988,29 +3114,23 @@
"%s",
class_name_string.c_str());
} else {
- ScopedLocalRef<jobject> class_loader_object(
- soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
- ScopedLocalRef<jobject> result(soa.Env(), nullptr);
- {
- ScopedThreadStateChange tsc(self, ThreadState::kNative);
- ScopedLocalRef<jobject> class_name_object(
- soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
- if (class_name_object.get() == nullptr) {
- DCHECK(self->IsExceptionPending()); // OOME.
- return nullptr;
- }
- CHECK(class_loader_object.get() != nullptr);
- result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
- WellKnownClasses::java_lang_ClassLoader_loadClass,
- class_name_object.get()));
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::String> class_name_object = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(self, class_name_string.c_str()));
+ if (class_name_object == nullptr) {
+ DCHECK(self->IsExceptionPending()); // OOME.
+ return nullptr;
}
- if (result.get() == nullptr && !self->IsExceptionPending()) {
+ DCHECK(class_loader != nullptr);
+ result_ptr = ObjPtr<mirror::Class>::DownCast(
+ WellKnownClasses::java_lang_ClassLoader_loadClass->InvokeVirtual<'L', 'L'>(
+ self, class_loader.Get(), class_name_object.Get()));
+ if (result_ptr == nullptr && !self->IsExceptionPending()) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
return nullptr;
}
- result_ptr = soa.Decode<mirror::Class>(result.get());
// Check the name of the returned class.
descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
}
@@ -3282,7 +3402,7 @@
// classes. However it could not update methods of this class while we
// were loading it. Now the class is resolved, we can update entrypoints
// as required by instrumentation.
- if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
+ if (Runtime::Current()->GetInstrumentation()->EntryExitStubsInstalled()) {
// We must be in the kRunnable state to prevent instrumentation from
// suspending all threads to update entrypoints while we are doing it
// for this class.
@@ -3400,14 +3520,11 @@
}
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
- // Link the code of methods skipped by LinkCode.
for (size_t method_index = 0; method_index < num_direct_methods; ++method_index) {
ArtMethod* method = klass->GetDirectMethod(method_index, pointer_size);
- if (!method->IsStatic()) {
- // Only update static methods.
- continue;
+ if (method->NeedsClinitCheckBeforeCall()) {
+ instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method));
}
- instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method));
}
// Ignore virtual methods on the iterator.
}
@@ -3821,7 +3938,8 @@
std::string dex_file_location = dex_file.GetLocation();
// The following paths checks don't work on preopt when using boot dex files, where the dex
// cache location is the one on device, and the dex_file's location is the one on host.
- if (!(Runtime::Current()->IsAotCompiler() && class_loader == nullptr && !kIsTargetBuild)) {
+ Runtime* runtime = Runtime::Current();
+ if (!(runtime->IsAotCompiler() && class_loader == nullptr && !kIsTargetBuild)) {
CHECK_GE(dex_file_location.length(), dex_cache_length)
<< dex_cache_location << " " << dex_file.GetLocation();
const std::string dex_file_suffix = dex_file_location.substr(
@@ -3831,34 +3949,37 @@
// dex file location is /system/priv-app/SettingsProvider/SettingsProvider.apk
CHECK_EQ(dex_cache_location, dex_file_suffix);
}
+
+ // Check if we need to initialize OatFile data (.data.bimg.rel.ro and .bss
+ // sections) needed for code execution and register the oat code range.
const OatFile* oat_file =
(dex_file.GetOatDexFile() != nullptr) ? dex_file.GetOatDexFile()->GetOatFile() : nullptr;
- // Clean up pass to remove null dex caches; null dex caches can occur due to class unloading
- // and we are lazily removing null entries. Also check if we need to initialize OatFile data
- // (.data.bimg.rel.ro and .bss sections) needed for code execution.
bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable();
- JavaVMExt* const vm = self->GetJniEnv()->GetVm();
- for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) {
- const DexCacheData& data = it->second;
- if (self->IsJWeakCleared(data.weak_root)) {
- vm->DeleteWeakGlobalRef(self, data.weak_root);
- it = dex_caches_.erase(it);
- } else {
- if (initialize_oat_file_data &&
- it->first->GetOatDexFile() != nullptr &&
- it->first->GetOatDexFile()->GetOatFile() == oat_file) {
+ if (initialize_oat_file_data) {
+ for (const auto& entry : dex_caches_) {
+ if (!self->IsJWeakCleared(entry.second.weak_root) &&
+ entry.first->GetOatDexFile() != nullptr &&
+ entry.first->GetOatDexFile()->GetOatFile() == oat_file) {
initialize_oat_file_data = false; // Already initialized.
+ break;
}
- ++it;
}
}
if (initialize_oat_file_data) {
oat_file->InitializeRelocations();
+ // Notify the fault handler about the new executable code range if needed.
+ size_t exec_offset = oat_file->GetOatHeader().GetExecutableOffset();
+ DCHECK_LE(exec_offset, oat_file->Size());
+ size_t exec_size = oat_file->Size() - exec_offset;
+ if (exec_size != 0u) {
+ runtime->AddGeneratedCodeRange(oat_file->Begin() + exec_offset, exec_size);
+ }
}
+
// Let hiddenapi assign a domain to the newly registered dex file.
hiddenapi::InitializeDexFileDomain(dex_file, class_loader);
- jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache);
+ jweak dex_cache_jweak = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, dex_cache);
DexCacheData data;
data.weak_root = dex_cache_jweak;
data.class_table = ClassTableForClassLoader(class_loader);
@@ -4778,6 +4899,12 @@
return; // nothing to process
}
const uint8_t* handlers_ptr = accessor.GetCatchHandlerData(0);
+ CHECK(method->GetDexFile()->IsInDataSection(handlers_ptr))
+ << method->PrettyMethod()
+ << "@" << method->GetDexFile()->GetLocation()
+ << "@" << reinterpret_cast<const void*>(handlers_ptr)
+ << " is_compact_dex=" << method->GetDexFile()->IsCompactDexFile();
+
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
for (uint32_t idx = 0; idx < handlers_size; idx++) {
CatchHandlerIterator iterator(handlers_ptr);
@@ -5034,8 +5161,7 @@
// Find the <init>(InvocationHandler)V method. The exact method offset varies depending
// on which front-end compiler was used to build the libcore DEX files.
- ArtMethod* proxy_constructor =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init);
+ ArtMethod* proxy_constructor = WellKnownClasses::java_lang_reflect_Proxy_init;
DCHECK(proxy_constructor != nullptr)
<< "Could not find <init> method in java.lang.reflect.Proxy";
@@ -7792,7 +7918,8 @@
LengthPrefixedArray<ArtMethod>* const new_methods = klass->GetMethodsPtr();
if (new_methods != nullptr) {
DCHECK_NE(new_methods->size(), 0u);
- imt_methods_begin = reinterpret_cast<uintptr_t>(&new_methods->At(0));
+ imt_methods_begin =
+ reinterpret_cast<uintptr_t>(&new_methods->At(0, kMethodSize, kMethodAlignment));
imt_methods_size = new_methods->size() * kMethodSize;
}
}
@@ -7895,20 +8022,6 @@
return true;
}
-NO_INLINE
-static void ThrowIllegalAccessErrorForImplementingMethod(ObjPtr<mirror::Class> klass,
- ArtMethod* vtable_method,
- ArtMethod* interface_method)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(!vtable_method->IsAbstract());
- DCHECK(!vtable_method->IsPublic());
- ThrowIllegalAccessError(
- klass,
- "Method '%s' implementing interface method '%s' is not public",
- vtable_method->PrettyMethod().c_str(),
- interface_method->PrettyMethod().c_str());
-}
-
template <PointerSize kPointerSize>
ObjPtr<mirror::PointerArray> ClassLinker::LinkMethodsHelper<kPointerSize>::AllocPointerArray(
Thread* self, size_t length) {
@@ -7997,55 +8110,17 @@
static constexpr double kMinLoadFactor = 0.3;
static constexpr double kMaxLoadFactor = 0.5;
static constexpr size_t kMaxStackBuferSize = 256;
- const size_t super_vtable_buffer_size = super_vtable_length * 3;
const size_t declared_virtuals_buffer_size = num_virtual_methods * 3;
- const size_t total_buffer_size = super_vtable_buffer_size + declared_virtuals_buffer_size;
- uint32_t* super_vtable_buffer_ptr = (total_buffer_size <= kMaxStackBuferSize)
- ? reinterpret_cast<uint32_t*>(alloca(total_buffer_size * sizeof(uint32_t)))
- : allocator_.AllocArray<uint32_t>(total_buffer_size);
- uint32_t* declared_virtuals_buffer_ptr = super_vtable_buffer_ptr + super_vtable_buffer_size;
- VTableSignatureSet super_vtable_signatures(
- kMinLoadFactor,
- kMaxLoadFactor,
- VTableSignatureHash(super_vtable_accessor),
- VTableSignatureEqual(super_vtable_accessor),
- super_vtable_buffer_ptr,
- super_vtable_buffer_size,
- allocator_.Adapter());
- ArrayRef<uint32_t> same_signature_vtable_lists;
- // Insert the first `mirror::Object::kVTableLength` indexes with pre-calculated hashes.
- DCHECK_GE(super_vtable_length, mirror::Object::kVTableLength);
- for (uint32_t i = 0; i != mirror::Object::kVTableLength; ++i) {
- size_t hash = class_linker_->object_virtual_method_hashes_[i];
- // There are no duplicate signatures in `java.lang.Object`, so use `HashSet<>::PutWithHash()`.
- // This avoids equality comparison for the three `java.lang.Object.wait()` overloads.
- super_vtable_signatures.PutWithHash(i, hash);
- }
- // Insert the remaining indexes, check for duplicate signatures.
- if (super_vtable_length > mirror::Object::kVTableLength) {
- for (size_t i = mirror::Object::kVTableLength; i < super_vtable_length; ++i) {
- // Use `super_vtable_accessor` for getting the method for hash calculation.
- // Letting `HashSet<>::insert()` use the internal accessor copy in the hash
- // function prevents the compiler from optimizing this properly because the
- // compiler cannot prove that the accessor copy is immutable.
- size_t hash = ComputeMethodHash(super_vtable_accessor.GetVTableEntry(i));
- auto [it, inserted] = super_vtable_signatures.InsertWithHash(i, hash);
- if (UNLIKELY(!inserted)) {
- if (same_signature_vtable_lists.empty()) {
- same_signature_vtable_lists = ArrayRef<uint32_t>(
- allocator_.AllocArray<uint32_t>(super_vtable_length), super_vtable_length);
- std::fill_n(same_signature_vtable_lists.data(), super_vtable_length, dex::kDexNoIndex);
- same_signature_vtable_lists_ = same_signature_vtable_lists;
- }
- DCHECK_LT(*it, i);
- same_signature_vtable_lists[i] = *it;
- *it = i;
- }
- }
- }
+ const size_t super_vtable_buffer_size = super_vtable_length * 3;
+ const size_t bit_vector_size = BitVector::BitsToWords(num_virtual_methods);
+ const size_t total_size =
+ declared_virtuals_buffer_size + super_vtable_buffer_size + bit_vector_size;
- // For each declared virtual method, look for a superclass virtual method
- // to override and assign a new vtable index if no method was overridden.
+ uint32_t* declared_virtuals_buffer_ptr = (total_size <= kMaxStackBuferSize)
+ ? reinterpret_cast<uint32_t*>(alloca(total_size * sizeof(uint32_t)))
+ : allocator_.AllocArray<uint32_t>(total_size);
+ uint32_t* bit_vector_buffer_ptr = declared_virtuals_buffer_ptr + declared_virtuals_buffer_size;
+
DeclaredVirtualSignatureSet declared_virtual_signatures(
kMinLoadFactor,
kMaxLoadFactor,
@@ -8054,8 +8129,24 @@
declared_virtuals_buffer_ptr,
declared_virtuals_buffer_size,
allocator_.Adapter());
+
+ ArrayRef<uint32_t> same_signature_vtable_lists;
const bool is_proxy_class = klass->IsProxyClass();
size_t vtable_length = super_vtable_length;
+
+ // Record which declared methods are overriding a super method.
+ BitVector initialized_methods(/* expandable= */ false,
+ Allocator::GetNoopAllocator(),
+ bit_vector_size,
+ bit_vector_buffer_ptr);
+
+ // Note: our sets hash on the method name, and therefore we pay a high
+ // performance price when a class has many overloads.
+ //
+ // We populate a set of declared signatures instead of signatures from the
+ // super vtable (which is only lazy populated in case of interface overriding,
+ // see below). This makes sure that we pay the performance price only on that
+ // class, and not on its subclasses (except in the case of interface overriding, see below).
for (size_t i = 0; i != num_virtual_methods; ++i) {
ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i, kPointerSize);
DCHECK(!virtual_method->IsStatic()) << virtual_method->PrettyMethod();
@@ -8064,59 +8155,79 @@
: virtual_method;
size_t hash = ComputeMethodHash(signature_method);
declared_virtual_signatures.PutWithHash(i, hash);
- auto it = super_vtable_signatures.FindWithHash(signature_method, hash);
- if (it != super_vtable_signatures.end()) {
- size_t super_index = *it;
- DCHECK_LT(super_index, super_vtable_length);
- ArtMethod* super_method = super_vtable_accessor.GetVTableEntry(super_index);
- // Historical note: Before Android 4.1, an inaccessible package-private
- // superclass method would have been incorrectly overridden.
- bool overrides = klass->CanAccessMember(super_method->GetDeclaringClass(),
- super_method->GetAccessFlags());
- if (overrides && super_method->IsFinal()) {
- sants.reset();
- ThrowLinkageError(klass, "Method %s overrides final method in class %s",
- virtual_method->PrettyMethod().c_str(),
- super_method->GetDeclaringClassDescriptor());
- return 0u;
- }
- if (UNLIKELY(!same_signature_vtable_lists.empty())) {
- // We may override more than one method according to JLS, see b/211854716 .
- // We record the highest overridden vtable index here so that we can walk
- // the list to find other overridden methods when constructing the vtable.
- // However, we walk all the methods to check for final method overriding.
- size_t current_index = super_index;
- while (same_signature_vtable_lists[current_index] != dex::kDexNoIndex) {
- DCHECK_LT(same_signature_vtable_lists[current_index], current_index);
- current_index = same_signature_vtable_lists[current_index];
- ArtMethod* current_method = super_vtable_accessor.GetVTableEntry(current_index);
- if (klass->CanAccessMember(current_method->GetDeclaringClass(),
- current_method->GetAccessFlags())) {
- if (current_method->IsFinal()) {
- sants.reset();
- ThrowLinkageError(klass, "Method %s overrides final method in class %s",
- virtual_method->PrettyMethod().c_str(),
- current_method->GetDeclaringClassDescriptor());
- return 0u;
- }
- if (!overrides) {
- overrides = true;
- super_index = current_index;
- super_method = current_method;
- }
- }
- }
- }
- if (overrides) {
- virtual_method->SetMethodIndex(super_index);
- continue;
- }
- }
- // The method does not override any method from superclass, so it needs a new vtable index.
- virtual_method->SetMethodIndex(vtable_length);
- ++vtable_length;
}
+ // Loop through each super vtable method and see if they are overridden by a method we added to
+ // the hash table.
+ for (size_t j = 0; j < super_vtable_length; ++j) {
+ // Search the hash table to see if we are overridden by any method.
+ ArtMethod* super_method = super_vtable_accessor.GetVTableEntry(j);
+ if (!klass->CanAccessMember(super_method->GetDeclaringClass(),
+ super_method->GetAccessFlags())) {
+ // Continue on to the next method since this one is package private and cannot be overridden.
+ // Before Android 4.1, the package-private method super_method might have been incorrectly
+ // overridden.
+ continue;
+ }
+ size_t hash = (j < mirror::Object::kVTableLength)
+ ? class_linker_->object_virtual_method_hashes_[j]
+ : ComputeMethodHash(super_method);
+ auto it = declared_virtual_signatures.FindWithHash(super_method, hash);
+ if (it == declared_virtual_signatures.end()) {
+ continue;
+ }
+ ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(*it, kPointerSize);
+ if (super_method->IsFinal()) {
+ sants.reset();
+ ThrowLinkageError(klass, "Method %s overrides final method in class %s",
+ virtual_method->PrettyMethod().c_str(),
+ super_method->GetDeclaringClassDescriptor());
+ return 0u;
+ }
+ if (initialized_methods.IsBitSet(*it)) {
+ // The method is overriding more than one method.
+ // We record that information in a linked list to later set the method in the vtable
+ // locations that are not the method index.
+ if (same_signature_vtable_lists.empty()) {
+ same_signature_vtable_lists = ArrayRef<uint32_t>(
+ allocator_.AllocArray<uint32_t>(super_vtable_length), super_vtable_length);
+ std::fill_n(same_signature_vtable_lists.data(), super_vtable_length, dex::kDexNoIndex);
+ same_signature_vtable_lists_ = same_signature_vtable_lists;
+ }
+ same_signature_vtable_lists[j] = virtual_method->GetMethodIndexDuringLinking();
+ } else {
+ initialized_methods.SetBit(*it);
+ }
+
+ // We arbitrarily set to the largest index. This is also expected when
+ // iterating over the `same_signature_vtable_lists_`.
+ virtual_method->SetMethodIndex(j);
+ }
+
+ // Add the non-overridden methods at the end.
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ if (!initialized_methods.IsBitSet(i)) {
+ ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, kPointerSize);
+ local_method->SetMethodIndex(vtable_length);
+ vtable_length++;
+ }
+ }
+
+ // A lazily constructed super vtable set, which we only populate in the less
+ // common sittuation of a superclass implementing a method declared in an
+ // interface this class inherits.
+ // We still try to allocate the set on the stack as using the arena will have
+ // a larger cost.
+ uint32_t* super_vtable_buffer_ptr = bit_vector_buffer_ptr + bit_vector_size;
+ VTableSignatureSet super_vtable_signatures(
+ kMinLoadFactor,
+ kMaxLoadFactor,
+ VTableSignatureHash(super_vtable_accessor),
+ VTableSignatureEqual(super_vtable_accessor),
+ super_vtable_buffer_ptr,
+ super_vtable_buffer_size,
+ allocator_.Adapter());
+
// Assign vtable indexes for interface methods in new interfaces and store them
// in implementation method arrays. These shall be replaced by actual method
// pointers later. We do not need to do this for superclass interfaces as we can
@@ -8135,42 +8246,39 @@
ArtMethod* interface_method = iface->GetVirtualMethod(j, kPointerSize);
size_t hash = ComputeMethodHash(interface_method);
ArtMethod* vtable_method = nullptr;
- bool found = false;
auto it1 = declared_virtual_signatures.FindWithHash(interface_method, hash);
if (it1 != declared_virtual_signatures.end()) {
- vtable_method = klass->GetVirtualMethodDuringLinking(*it1, kPointerSize);
- found = true;
+ ArtMethod* found_method = klass->GetVirtualMethodDuringLinking(*it1, kPointerSize);
+ // For interface overriding, we only look at public methods.
+ if (found_method->IsPublic()) {
+ vtable_method = found_method;
+ }
} else {
+ // This situation should be rare (a superclass implements a method
+ // declared in an interface this class is inheriting). Only in this case
+ // do we lazily populate the super_vtable_signatures.
+ if (super_vtable_signatures.empty()) {
+ for (size_t k = 0; k < super_vtable_length; ++k) {
+ ArtMethod* super_method = super_vtable_accessor.GetVTableEntry(k);
+ if (!super_method->IsPublic()) {
+ // For interface overriding, we only look at public methods.
+ continue;
+ }
+ size_t super_hash = (k < mirror::Object::kVTableLength)
+ ? class_linker_->object_virtual_method_hashes_[k]
+ : ComputeMethodHash(super_method);
+ auto [it, inserted] = super_vtable_signatures.InsertWithHash(k, super_hash);
+ DCHECK(inserted || super_vtable_accessor.GetVTableEntry(*it) == super_method);
+ }
+ }
auto it2 = super_vtable_signatures.FindWithHash(interface_method, hash);
if (it2 != super_vtable_signatures.end()) {
- // If there are multiple vtable methods with the same signature, the one with
- // the highest vtable index is not nessarily the one in most-derived class.
- // Find the most-derived method. See b/211854716 .
vtable_method = super_vtable_accessor.GetVTableEntry(*it2);
- if (UNLIKELY(!same_signature_vtable_lists.empty())) {
- size_t current_index = *it2;
- while (same_signature_vtable_lists[current_index] != dex::kDexNoIndex) {
- DCHECK_LT(same_signature_vtable_lists[current_index], current_index);
- current_index = same_signature_vtable_lists[current_index];
- ArtMethod* current_method = super_vtable_accessor.GetVTableEntry(current_index);
- ObjPtr<mirror::Class> current_class = current_method->GetDeclaringClass();
- if (current_class->IsSubClass(vtable_method->GetDeclaringClass())) {
- vtable_method = current_method;
- }
- }
- }
- found = true;
}
}
+
uint32_t vtable_index = vtable_length;
- if (found) {
- DCHECK(vtable_method != nullptr);
- if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
- // FIXME: Delay the exception until we actually try to call the method. b/211854716
- sants.reset();
- ThrowIllegalAccessErrorForImplementingMethod(klass, vtable_method, interface_method);
- return 0u;
- }
+ if (vtable_method != nullptr) {
vtable_index = vtable_method->GetMethodIndexDuringLinking();
if (!vtable_method->IsOverridableByDefaultMethod()) {
method_array->SetElementPtrSize(j, vtable_index, kPointerSize);
@@ -8180,7 +8288,7 @@
auto [it, inserted] = copied_method_records_.InsertWithHash(
CopiedMethodRecord(interface_method, vtable_index), hash);
- if (found) {
+ if (vtable_method != nullptr) {
DCHECK_EQ(vtable_index, it->GetMethodIndex());
} else if (inserted) {
DCHECK_EQ(vtable_index, it->GetMethodIndex());
@@ -8346,26 +8454,29 @@
ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zu", num_virtual_methods);
return false;
}
+ // Assign each method an interface table index and set the default flag.
bool has_defaults = false;
- // Assign each method an IMT index and set the default flag.
for (size_t i = 0; i < num_virtual_methods; ++i) {
ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, kPointerSize);
m->SetMethodIndex(i);
- if (!m->IsAbstract()) {
+ uint32_t access_flags = m->GetAccessFlags();
+ DCHECK(!ArtMethod::IsDefault(access_flags));
+ DCHECK_EQ(!ArtMethod::IsAbstract(access_flags), ArtMethod::IsInvokable(access_flags));
+ if (ArtMethod::IsInvokable(access_flags)) {
// If the dex file does not support default methods, throw ClassFormatError.
// This check is necessary to protect from odd cases, such as native default
// methods, that the dex file verifier permits for old dex file versions. b/157170505
// FIXME: This should be `if (!m->GetDexFile()->SupportsDefaultMethods())` but we're
// currently running CTS tests for default methods with dex file version 035 which
// does not support default methods. So, we limit this to native methods. b/157718952
- if (m->IsNative()) {
+ if (ArtMethod::IsNative(access_flags)) {
DCHECK(!m->GetDexFile()->SupportsDefaultMethods());
ThrowClassFormatError(klass.Get(),
"Dex file does not support default method '%s'",
m->PrettyMethod().c_str());
return false;
}
- if (!m->IsPublic()) {
+ if (!ArtMethod::IsPublic(access_flags)) {
// The verifier should have caught the non-public method for dex version 37.
// Just warn and skip it since this is from before default-methods so we don't
// really need to care that it has code.
@@ -8373,7 +8484,7 @@
<< "This will be a fatal error in subsequent versions of android. "
<< "Continuing anyway.";
}
- m->SetAccessFlags(m->GetAccessFlags() | kAccDefault);
+ m->SetAccessFlags(access_flags | kAccDefault);
has_defaults = true;
}
}
@@ -8520,17 +8631,16 @@
uint32_t vtable_index = virtual_method.GetMethodIndexDuringLinking();
vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize);
if (UNLIKELY(vtable_index < same_signature_vtable_lists.size())) {
- // We may override more than one method according to JLS, see b/211854716 .
- // If we do, arbitrarily update the method index to the lowest overridden vtable index.
+ // We may override more than one method according to JLS, see b/211854716.
while (same_signature_vtable_lists[vtable_index] != dex::kDexNoIndex) {
DCHECK_LT(same_signature_vtable_lists[vtable_index], vtable_index);
vtable_index = same_signature_vtable_lists[vtable_index];
- ArtMethod* current_method = super_class->GetVTableEntry(vtable_index, kPointerSize);
- if (klass->CanAccessMember(current_method->GetDeclaringClass(),
- current_method->GetAccessFlags())) {
+ vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize);
+ if (kIsDebugBuild) {
+ ArtMethod* current_method = super_class->GetVTableEntry(vtable_index, kPointerSize);
+ DCHECK(klass->CanAccessMember(current_method->GetDeclaringClass(),
+ current_method->GetAccessFlags()));
DCHECK(!current_method->IsFinal());
- vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize);
- virtual_method.SetMethodIndex(vtable_index);
}
}
}
@@ -8642,7 +8752,8 @@
};
// We use the following order of field types for assigning offsets.
-// Some fields can be shuffled forward to fill gaps, see `ClassLinker::LinkFields()`.
+// Some fields can be shuffled forward to fill gaps, see
+// `ClassLinker::LinkFieldsHelper::LinkFields()`.
enum class ClassLinker::LinkFieldsHelper::FieldTypeOrder : uint16_t {
kReference = 0u,
kLong,
@@ -9376,19 +9487,7 @@
Thread::Current()->AssertPendingException();
return nullptr;
}
- if (klass->IsInterface()) {
- resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- } else {
- resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- }
- if (resolved != nullptr &&
- hiddenapi::ShouldDenyAccessToMember(
- resolved,
- hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
- hiddenapi::AccessMethod::kLinking)) {
- resolved = nullptr;
- }
- return resolved;
+ return FindResolvedMethod(klass, dex_cache.Get(), class_loader.Get(), method_idx);
}
ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx,
@@ -9540,6 +9639,9 @@
Handle<mirror::MethodType> type = hs.NewHandle(
mirror::MethodType::Create(self, return_type, method_params));
if (type != nullptr) {
+ // Ensure all stores for the newly created MethodType are visible, before we attempt to place
+ // it in the DexCache (b/224733324).
+ std::atomic_thread_fence(std::memory_order_release);
dex_cache->SetResolvedMethodType(proto_idx, type.Get());
}
@@ -10036,11 +10138,13 @@
Handle<mirror::ClassLoader> parent_loader,
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries,
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_after) {
+ CHECK(loader_class.Get() == WellKnownClasses::dalvik_system_PathClassLoader ||
+ loader_class.Get() == WellKnownClasses::dalvik_system_DelegateLastClassLoader ||
+ loader_class.Get() == WellKnownClasses::dalvik_system_InMemoryDexClassLoader);
StackHandleScope<5> hs(self);
- ArtField* dex_elements_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
+ ArtField* dex_elements_field = WellKnownClasses::dalvik_system_DexPathList_dexElements;
Handle<mirror::Class> dex_elements_class(hs.NewHandle(dex_elements_field->ResolveType()));
DCHECK(dex_elements_class != nullptr);
@@ -10052,14 +10156,13 @@
Handle<mirror::Class> h_dex_element_class =
hs.NewHandle(dex_elements_class->GetComponentType());
- ArtField* element_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ ArtField* element_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
DCHECK_EQ(h_dex_element_class.Get(), element_file_field->GetDeclaringClass());
- ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ ArtField* cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->LookupResolvedType());
- ArtField* file_name_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_fileName);
+ ArtField* file_name_field = WellKnownClasses::dalvik_system_DexFile_fileName;
DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->LookupResolvedType());
// Fill the elements array.
@@ -10131,88 +10234,45 @@
ObjPtr<mirror::ClassLoader>::DownCast(loader_class->AllocObject(self)));
DCHECK(h_class_loader != nullptr);
// Set DexPathList.
- ArtField* path_list_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
+ ArtField* path_list_field = WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
DCHECK(path_list_field != nullptr);
path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
// Make a pretend boot-classpath.
// TODO: Should we scan the image?
- ArtField* const parent_field =
- jni::DecodeArtField(WellKnownClasses::java_lang_ClassLoader_parent);
+ ArtField* const parent_field = WellKnownClasses::java_lang_ClassLoader_parent;
DCHECK(parent_field != nullptr);
if (parent_loader.Get() == nullptr) {
- ScopedObjectAccessUnchecked soa(self);
- ObjPtr<mirror::Object> boot_loader(soa.Decode<mirror::Class>(
- WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self));
+ ObjPtr<mirror::Object> boot_loader(
+ WellKnownClasses::java_lang_BootClassLoader->AllocObject(self));
parent_field->SetObject<false>(h_class_loader.Get(), boot_loader);
} else {
parent_field->SetObject<false>(h_class_loader.Get(), parent_loader.Get());
}
ArtField* shared_libraries_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
DCHECK(shared_libraries_field != nullptr);
shared_libraries_field->SetObject<false>(h_class_loader.Get(), shared_libraries.Get());
ArtField* shared_libraries_after_field =
- jni::DecodeArtField(
- WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter);
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
DCHECK(shared_libraries_after_field != nullptr);
shared_libraries_after_field->SetObject<false>(h_class_loader.Get(),
shared_libraries_after.Get());
return h_class_loader.Get();
}
-jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files,
- jclass loader_class,
- jobject parent_loader,
- jobject shared_libraries,
- jobject shared_libraries_after) {
- CHECK(self->GetJniEnv()->IsSameObject(loader_class,
- WellKnownClasses::dalvik_system_PathClassLoader) ||
- self->GetJniEnv()->IsSameObject(loader_class,
- WellKnownClasses::dalvik_system_DelegateLastClassLoader) ||
- self->GetJniEnv()->IsSameObject(loader_class,
- WellKnownClasses::dalvik_system_InMemoryDexClassLoader));
-
- // SOAAlreadyRunnable is protected, and we need something to add a global reference.
- // We could move the jobject to the callers, but all call-sites do this...
- ScopedObjectAccessUnchecked soa(self);
-
- // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex.
- StackHandleScope<5> hs(self);
-
- Handle<mirror::Class> h_loader_class =
- hs.NewHandle<mirror::Class>(soa.Decode<mirror::Class>(loader_class));
- Handle<mirror::ClassLoader> h_parent =
- hs.NewHandle<mirror::ClassLoader>(soa.Decode<mirror::ClassLoader>(parent_loader));
- Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries =
- hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries));
- Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries_after =
- hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries_after));
-
- ObjPtr<mirror::ClassLoader> loader = CreateWellKnownClassLoader(
- self,
- dex_files,
- h_loader_class,
- h_parent,
- h_shared_libraries,
- h_shared_libraries_after);
-
- // Make it a global ref and return.
- ScopedLocalRef<jobject> local_ref(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(loader));
- return soa.Env()->NewGlobalRef(local_ref.get());
-}
-
jobject ClassLinker::CreatePathClassLoader(Thread* self,
const std::vector<const DexFile*>& dex_files) {
- return CreateWellKnownClassLoader(self,
- dex_files,
- WellKnownClasses::dalvik_system_PathClassLoader,
- nullptr);
+ StackHandleScope<3u> hs(self);
+ Handle<mirror::Class> d_s_pcl =
+ hs.NewHandle(WellKnownClasses::dalvik_system_PathClassLoader.Get());
+ auto null_parent = hs.NewHandle<mirror::ClassLoader>(nullptr);
+ auto null_libs = hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr);
+ ObjPtr<mirror::ClassLoader> class_loader =
+ CreateWellKnownClassLoader(self, dex_files, d_s_pcl, null_parent, null_libs, null_libs);
+ return Runtime::Current()->GetJavaVM()->AddGlobalRef(self, class_loader);
}
void ClassLinker::DropFindArrayClassCache() {
@@ -10269,27 +10329,65 @@
void ClassLinker::CleanupClassLoaders() {
Thread* const self = Thread::Current();
- std::vector<ClassLoaderData> to_delete;
+ std::list<ClassLoaderData> to_delete;
// Do the delete outside the lock to avoid lock violation in jit code cache.
{
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
for (auto it = class_loaders_.begin(); it != class_loaders_.end(); ) {
- const ClassLoaderData& data = *it;
+ auto this_it = it;
+ ++it;
+ const ClassLoaderData& data = *this_it;
// Need to use DecodeJObject so that we get null for cleared JNI weak globals.
ObjPtr<mirror::ClassLoader> class_loader =
ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(data.weak_root));
- if (class_loader != nullptr) {
- ++it;
- } else {
+ if (class_loader == nullptr) {
VLOG(class_linker) << "Freeing class loader";
- to_delete.push_back(data);
- it = class_loaders_.erase(it);
+ to_delete.splice(to_delete.end(), class_loaders_, this_it);
}
}
}
- for (ClassLoaderData& data : to_delete) {
- // CHA unloading analysis and SingleImplementaion cleanups are required.
- DeleteClassLoader(self, data, /*cleanup_cha=*/ true);
+ std::set<const OatFile*> unregistered_oat_files;
+ if (!to_delete.empty()) {
+ JavaVMExt* vm = self->GetJniEnv()->GetVm();
+ WriterMutexLock mu(self, *Locks::dex_lock_);
+ for (auto it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ) {
+ const DexFile* dex_file = it->first;
+ const DexCacheData& data = it->second;
+ if (self->DecodeJObject(data.weak_root) == nullptr) {
+ DCHECK(to_delete.end() != std::find_if(
+ to_delete.begin(),
+ to_delete.end(),
+ [&](const ClassLoaderData& cld) { return cld.class_table == data.class_table; }));
+ if (dex_file->GetOatDexFile() != nullptr &&
+ dex_file->GetOatDexFile()->GetOatFile() != nullptr &&
+ dex_file->GetOatDexFile()->GetOatFile()->IsExecutable()) {
+ unregistered_oat_files.insert(dex_file->GetOatDexFile()->GetOatFile());
+ }
+ vm->DeleteWeakGlobalRef(self, data.weak_root);
+ it = dex_caches_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ {
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ for (ClassLoaderData& data : to_delete) {
+ // CHA unloading analysis and SingleImplementaion cleanups are required.
+ DeleteClassLoader(self, data, /*cleanup_cha=*/ true);
+ }
+ }
+ if (!unregistered_oat_files.empty()) {
+ for (const OatFile* oat_file : unregistered_oat_files) {
+ // Notify the fault handler about removal of the executable code range if needed.
+ DCHECK(oat_file->IsExecutable());
+ size_t exec_offset = oat_file->GetOatHeader().GetExecutableOffset();
+ DCHECK_LE(exec_offset, oat_file->Size());
+ size_t exec_size = oat_file->Size() - exec_offset;
+ if (exec_size != 0u) {
+ Runtime::Current()->RemoveGeneratedCodeRange(oat_file->Begin() + exec_offset, exec_size);
+ }
+ }
}
}
@@ -10317,9 +10415,73 @@
CHECK(method->IsCopied());
FindVirtualMethodHolderVisitor visitor(method, image_pointer_size_);
VisitClasses(&visitor);
+ DCHECK(visitor.holder_ != nullptr);
return visitor.holder_;
}
+ObjPtr<mirror::ClassLoader> ClassLinker::GetHoldingClassLoaderOfCopiedMethod(Thread* self,
+ ArtMethod* method) {
+ // Note: `GetHoldingClassOfCopiedMethod(method)` is a lot more expensive than finding
+ // the class loader, so we're using it only to verify the result in debug mode.
+ CHECK(method->IsCopied());
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ // Check if the copied method is in the boot class path.
+ if (heap->IsBootImageAddress(method) || GetAllocatorForClassLoader(nullptr)->Contains(method)) {
+ DCHECK(GetHoldingClassOfCopiedMethod(method)->GetClassLoader() == nullptr);
+ return nullptr;
+ }
+ // Check if the copied method is in an app image.
+ // Note: Continuous spaces contain boot image spaces and app image spaces.
+ // However, they are sorted by address, so boot images are not trivial to skip.
+ ArrayRef<gc::space::ContinuousSpace* const> spaces(heap->GetContinuousSpaces());
+ DCHECK_GE(spaces.size(), heap->GetBootImageSpaces().size());
+ for (gc::space::ContinuousSpace* space : spaces) {
+ if (space->IsImageSpace()) {
+ gc::space::ImageSpace* image_space = space->AsImageSpace();
+ size_t offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin();
+ const ImageSection& methods_section = image_space->GetImageHeader().GetMethodsSection();
+ if (offset - methods_section.Offset() < methods_section.Size()) {
+ // Grab the class loader from the first non-BCP class in the app image class table.
+ // Note: If we allow classes from arbitrary parent or library class loaders in app
+ // images, this shall need to be updated to actually search for the exact class.
+ const ImageSection& class_table_section =
+ image_space->GetImageHeader().GetClassTableSection();
+ CHECK_NE(class_table_section.Size(), 0u);
+ const uint8_t* ptr = image_space->Begin() + class_table_section.Offset();
+ size_t read_count = 0;
+ ClassTable::ClassSet class_set(ptr, /*make_copy_of_data=*/ false, &read_count);
+ CHECK(!class_set.empty());
+ auto it = class_set.begin();
+ // No read barrier needed for references to non-movable image classes.
+ while ((*it).Read<kWithoutReadBarrier>()->IsBootStrapClassLoaded()) {
+ ++it;
+ CHECK(it != class_set.end());
+ }
+ ObjPtr<mirror::ClassLoader> class_loader =
+ (*it).Read<kWithoutReadBarrier>()->GetClassLoader();
+ DCHECK(GetHoldingClassOfCopiedMethod(method)->GetClassLoader() == class_loader);
+ return class_loader;
+ }
+ }
+ }
+ // Otherwise, the method must be in one of the `LinearAlloc` memory areas.
+ jweak result = nullptr;
+ {
+ ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ for (const ClassLoaderData& data : class_loaders_) {
+ if (data.allocator->Contains(method)) {
+ result = data.weak_root;
+ break;
+ }
+ }
+ }
+ CHECK(result != nullptr) << "Did not find allocator holding the copied method: " << method
+ << " " << method->PrettyMethod();
+ // The `method` is alive, so the class loader must also be alive.
+ return ObjPtr<mirror::ClassLoader>::DownCast(
+ Runtime::Current()->GetJavaVM()->DecodeWeakGlobalAsStrong(result));
+}
+
bool ClassLinker::DenyAccessBasedOnPublicSdk(ArtMethod* art_method ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Should not be called on ClassLinker, only on AotClassLinker that overrides this.
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5981adc..d6d3396 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -36,6 +36,7 @@
#include "dex/dex_file_types.h"
#include "gc_root.h"
#include "handle.h"
+#include "interpreter/mterp/nterp.h"
#include "jni.h"
#include "mirror/class.h"
#include "mirror/object.h"
@@ -47,6 +48,7 @@
class ArtField;
class ArtMethod;
class ClassHierarchyAnalysis;
+class ClassLoaderContext;
enum class ClassRoot : uint32_t;
class ClassTable;
class DexFile;
@@ -175,6 +177,7 @@
// Add boot class path dex files that were not included in the boot image.
// ClassLinker takes ownership of these dex files.
+ // DO NOT use directly. Use `Runtime::AddExtraBootDexFiles`.
void AddExtraBootDexFiles(Thread* self,
std::vector<std::unique_ptr<const DexFile>>&& additional_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -186,6 +189,7 @@
// properly handle read barriers and object marking.
bool AddImageSpace(gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
+ ClassLoaderContext* context,
std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
std::string* error_msg)
REQUIRES(!Locks::dex_lock_)
@@ -365,14 +369,11 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
- template <InvokeType type, ResolveMode kResolveMode>
- ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
template <ResolveMode kResolveMode>
ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+
ArtMethod* ResolveMethodWithoutInvokeType(uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader)
@@ -459,6 +460,12 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+ // Initializes a few essential classes, namely `java.lang.Class`,
+ // `java.lang.Object` and `java.lang.reflect.Field`.
+ void RunEarlyRootClinits(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+
// Initializes classes that have instances in the image but that have
// <clinit> methods so they could not be initialized by the compiler.
void RunRootClinits(Thread* self)
@@ -620,6 +627,11 @@
return nterp_trampoline_ == entry_point;
}
+ bool IsNterpEntryPoint(const void* entry_point) const {
+ return entry_point == interpreter::GetNterpEntryPoint() ||
+ entry_point == interpreter::GetNterpWithClinitEntryPoint();
+ }
+
const void* GetQuickToInterpreterBridgeTrampoline() const {
return quick_to_interpreter_bridge_trampoline_;
}
@@ -657,31 +669,19 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
- // that can be used to load classes from the given dex files. The parent of the class loader
- // will be set to `parent_loader`. If `parent_loader` is null the parent will be
- // the boot class loader.
- // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
- // this method will abort.
- // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
- jobject CreateWellKnownClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files,
- jclass loader_class,
- jobject parent_loader,
- jobject shared_libraries = nullptr,
- jobject shared_libraries_after = nullptr)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
-
- // Calls CreateWellKnownClassLoader(self,
- // dex_files,
- // WellKnownClasses::dalvik_system_PathClassLoader,
- // nullptr)
+ // Calls `CreateWellKnownClassLoader()` with `WellKnownClasses::dalvik_system_PathClassLoader`,
+ // and null parent and libraries. Wraps the result in a JNI global reference.
jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- // Non-GlobalRef version of CreateWellKnownClassLoader
+ // Creates a `PathClassLoader`, `DelegateLastClassLoader` or `InMemoryDexClassLoader`
+ // (specified by loader_class) that can be used to load classes from the given dex files.
+ // The parent of the class loader will be set to `parent_loader`. If `parent_loader` is
+ // null the parent will be the boot class loader.
+ // If `loader_class` points to a different class than `PathClassLoader`,
+ // `DelegateLastClassLoader` or `InMemoryDexClassLoader` this method will abort.
+ // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
ObjPtr<mirror::ClassLoader> CreateWellKnownClassLoader(
Thread* self,
const std::vector<const DexFile*>& dex_files,
@@ -722,8 +722,7 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::ClassLoader> class_loader)
+ static bool IsBootClassLoader(ObjPtr<mirror::Object> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtMethod* AddMethodToConflictTable(ObjPtr<mirror::Class> klass,
@@ -767,6 +766,11 @@
ObjPtr<mirror::Class> GetHoldingClassOfCopiedMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Get the class loader holding class for a copied method.
+ ObjPtr<mirror::ClassLoader> GetHoldingClassLoaderOfCopiedMethod(Thread* self, ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
+
// Returns null if not found.
// This returns a pointer to the class-table, without requiring any locking - including the
// boot class-table. It is the caller's responsibility to access this under lock, if required.
@@ -774,10 +778,12 @@
REQUIRES_SHARED(Locks::mutator_lock_)
NO_THREAD_SAFETY_ANALYSIS;
+ // DO NOT use directly. Use `Runtime::AppendToBootClassPath`.
void AppendToBootClassPath(Thread* self, const DexFile* dex_file)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ // DO NOT use directly. Use `Runtime::AppendToBootClassPath`.
void AppendToBootClassPath(const DexFile* dex_file, ObjPtr<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
@@ -798,7 +804,7 @@
return cha_.get();
}
- void MakeInitializedClassesVisiblyInitialized(Thread* self, bool wait);
+ void MakeInitializedClassesVisiblyInitialized(Thread* self, bool wait /* ==> no locks held */);
// Registers the native method and returns the new entry point. NB The returned entry point
// might be different from the native_method argument if some MethodCallback modifies it.
@@ -1004,8 +1010,7 @@
// class-loader chain could be handled, false otherwise, i.e., a non-supported class-loader
// was encountered while walking the parent chain (currently only BootClassLoader and
// PathClassLoader are supported).
- bool FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInBaseDexClassLoader(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -1013,8 +1018,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- bool FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInSharedLibraries(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -1022,8 +1026,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- bool FindClassInSharedLibrariesHelper(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInSharedLibrariesHelper(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -1032,8 +1035,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- bool FindClassInSharedLibrariesAfter(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInSharedLibrariesAfter(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -1049,7 +1051,7 @@
// The method always returns true, to notify to the caller a
// BaseDexClassLoader has a known lookup.
bool FindClassInBaseDexClassLoaderClassPath(
- ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 010b384..05f31b2 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -52,6 +52,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference.h"
+#include "mirror/stack_frame_info.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
#include "mirror/var_handle.h"
@@ -130,7 +131,14 @@
EXPECT_TRUE(JavaLangObject->GetSuperClass() == nullptr);
EXPECT_FALSE(JavaLangObject->HasSuperClass());
EXPECT_TRUE(JavaLangObject->GetClassLoader() == nullptr);
- class_linker_->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
+ {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(&JavaLangObject));
+ ScopedThreadSuspension sts(self, ThreadState::kNative);
+ class_linker_->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
+ // HandleWrapperObjPtr restores JavaLangObject here after becoming runnable again.
+ }
EXPECT_EQ(ClassStatus::kVisiblyInitialized, JavaLangObject->GetStatus());
EXPECT_FALSE(JavaLangObject->IsErroneous());
EXPECT_TRUE(JavaLangObject->IsLoaded());
@@ -598,6 +606,7 @@
struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> {
ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") {
+ addOffset(OFFSETOF_MEMBER(mirror::ClassExt, class_value_map_), "classValueMap");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, erroneous_state_error_), "erroneousStateError");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, instance_jfield_ids_), "instanceJfieldIDs");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, jmethod_ids_), "jmethodIDs");
@@ -640,6 +649,20 @@
}
};
+struct StackFrameInfoOffsets : public CheckOffsets<mirror::StackFrameInfo> {
+ StackFrameInfoOffsets() : CheckOffsets<mirror::StackFrameInfo>(
+ false, "Ljava/lang/StackFrameInfo;") {
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, bci_), "bci");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, declaring_class_), "declaringClass");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, file_name_), "fileName");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, line_number_), "lineNumber");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, method_name_), "methodName");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, method_type_), "methodType");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, retain_class_ref_), "retainClassRef");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, ste_), "ste");
+ }
+};
+
struct ClassLoaderOffsets : public CheckOffsets<mirror::ClassLoader> {
ClassLoaderOffsets() : CheckOffsets<mirror::ClassLoader>(false, "Ljava/lang/ClassLoader;") {
addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, allocator_), "allocator");
@@ -661,20 +684,18 @@
addOffset(OFFSETOF_MEMBER(mirror::DexCache, class_loader_), "classLoader");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_preresolved_strings_), "numPreResolvedStrings");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, preresolved_strings_), "preResolvedStrings");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_array_), "resolvedFieldsArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_array_),
+ "resolvedMethodTypesArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_array_), "resolvedMethodsArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_array_), "resolvedTypesArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_), "strings");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_array_), "stringsArray");
}
};
@@ -830,7 +851,7 @@
// C++ fields must exactly match the fields in the Java classes. If this fails,
// reorder the fields in the C++ class. Managed class fields are ordered by
-// ClassLinker::LinkFields.
+// ClassLinker::LinkFieldsHelper::LinkFields.
TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) {
ScopedObjectAccess soa(Thread::Current());
EXPECT_TRUE(ObjectOffsets().Check());
@@ -858,6 +879,7 @@
EXPECT_TRUE(ArrayElementVarHandleOffsets().Check());
EXPECT_TRUE(ByteArrayViewVarHandleOffsets().Check());
EXPECT_TRUE(ByteBufferViewVarHandleOffsets().Check());
+ EXPECT_TRUE(StackFrameInfoOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
@@ -1689,7 +1711,7 @@
}
ASSERT_TRUE(klass == nullptr);
} else if (expected_class_loader_obj == nullptr) {
- ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader()));
+ ASSERT_TRUE(ClassLinker::IsBootClassLoader(klass->GetClassLoader()));
} else {
ASSERT_TRUE(klass != nullptr) << descriptor;
Handle<mirror::ClassLoader> expected_class_loader(
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index eb32f2b..722b8f7 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -44,7 +44,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -778,14 +778,15 @@
}
// Returns the WellKnownClass for the given class loader type.
-static jclass GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type) {
+static ObjPtr<mirror::Class> GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
switch (type) {
case ClassLoaderContext::kPathClassLoader:
- return WellKnownClasses::dalvik_system_PathClassLoader;
+ return WellKnownClasses::dalvik_system_PathClassLoader.Get();
case ClassLoaderContext::kDelegateLastClassLoader:
- return WellKnownClasses::dalvik_system_DelegateLastClassLoader;
+ return WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get();
case ClassLoaderContext::kInMemoryDexClassLoader:
- return WellKnownClasses::dalvik_system_InMemoryDexClassLoader;
+ return WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get();
case ClassLoaderContext::kInvalidClassLoader: break; // will fail after the switch.
}
LOG(FATAL) << "Invalid class loader type " << type;
@@ -883,8 +884,7 @@
compilation_sources.begin(),
compilation_sources.end());
}
- Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>(
- soa.Decode<mirror::Class>(GetClassLoaderClass(info.type)));
+ Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>(GetClassLoaderClass(info.type));
ObjPtr<mirror::ClassLoader> loader =
Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
self,
@@ -950,12 +950,13 @@
return result;
}
-std::string ClassLoaderContext::FlattenDexPaths() const {
+std::vector<std::string> ClassLoaderContext::FlattenDexPaths() const {
+ std::vector<std::string> result;
+
if (class_loader_chain_ == nullptr) {
- return "";
+ return result;
}
- std::vector<std::string> result;
std::vector<ClassLoaderInfo*> work_list;
work_list.push_back(class_loader_chain_.get());
while (!work_list.empty()) {
@@ -966,7 +967,7 @@
}
AddToWorkList(info, work_list);
}
- return FlattenClasspath(result);
+ return result;
}
const char* ClassLoaderContext::GetClassLoaderTypeName(ClassLoaderType type) {
@@ -1019,21 +1020,18 @@
// Collects all the dex files loaded by the given class loader.
// Returns true for success or false if an unexpected state is discovered (e.g. a null dex cookie,
// a null list of dex elements or a null dex element).
-static bool CollectDexFilesFromSupportedClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+static bool CollectDexFilesFromSupportedClassLoader(Thread* self,
Handle<mirror::ClassLoader> class_loader,
std::vector<const DexFile*>* out_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK(IsInstanceOfBaseDexClassLoader(soa, class_loader));
+ CHECK(IsInstanceOfBaseDexClassLoader(class_loader));
// All supported class loaders inherit from BaseDexClassLoader.
// We need to get the DexPathList and loop through it.
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ ArtField* const cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
+ ArtField* const dex_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(class_loader.Get());
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList->GetObject(class_loader.Get());
CHECK(cookie_field != nullptr);
CHECK(dex_file_field != nullptr);
if (dex_path_list == nullptr) {
@@ -1043,8 +1041,7 @@
}
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
+ WellKnownClasses::dalvik_system_DexPathList_dexElements->GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj == nullptr) {
@@ -1052,7 +1049,7 @@
// and assume we have no elements.
return true;
} else {
- StackHandleScope<1> hs(soa.Self());
+ StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Object>> dex_elements(
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()));
for (auto element : dex_elements.Iterate<mirror::Object>()) {
@@ -1075,19 +1072,15 @@
}
static bool GetDexFilesFromDexElementsArray(
- ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
std::vector<const DexFile*>* out_dex_files) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(dex_elements != nullptr);
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
- const ObjPtr<mirror::Class> element_class = soa.Decode<mirror::Class>(
- WellKnownClasses::dalvik_system_DexPathList__Element);
- const ObjPtr<mirror::Class> dexfile_class = soa.Decode<mirror::Class>(
- WellKnownClasses::dalvik_system_DexFile);
+ ArtField* const cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
+ ArtField* const dex_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
+ const ObjPtr<mirror::Class> element_class =
+ WellKnownClasses::dalvik_system_DexPathList__Element.Get();
+ const ObjPtr<mirror::Class> dexfile_class = WellKnownClasses::dalvik_system_DexFile.Get();
for (auto element : dex_elements.Iterate<mirror::Object>()) {
// We can hit a null element here because this is invoked with a partially filled dex_elements
@@ -1131,17 +1124,17 @@
bool is_shared_library,
bool is_after)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (ClassLinker::IsBootClassLoader(soa, class_loader.Get())) {
+ if (ClassLinker::IsBootClassLoader(class_loader.Get())) {
// Nothing to do for the boot class loader as we don't add its dex files to the context.
return true;
}
ClassLoaderContext::ClassLoaderType type;
- if (IsPathOrDexClassLoader(soa, class_loader)) {
+ if (IsPathOrDexClassLoader(class_loader)) {
type = kPathClassLoader;
- } else if (IsDelegateLastClassLoader(soa, class_loader)) {
+ } else if (IsDelegateLastClassLoader(class_loader)) {
type = kDelegateLastClassLoader;
- } else if (IsInMemoryDexClassLoader(soa, class_loader)) {
+ } else if (IsInMemoryDexClassLoader(class_loader)) {
type = kInMemoryDexClassLoader;
} else {
LOG(WARNING) << "Unsupported class loader";
@@ -1150,7 +1143,7 @@
// Inspect the class loader for its dex files.
std::vector<const DexFile*> dex_files_loaded;
- CollectDexFilesFromSupportedClassLoader(soa, class_loader, &dex_files_loaded);
+ CollectDexFilesFromSupportedClassLoader(soa.Self(), class_loader, &dex_files_loaded);
// If we have a dex_elements array extract its dex elements now.
// This is used in two situations:
@@ -1162,7 +1155,7 @@
// BaseDexClassLoader, the paths already present in the class loader will be passed
// in the dex_elements array.
if (dex_elements != nullptr) {
- GetDexFilesFromDexElementsArray(soa, dex_elements, &dex_files_loaded);
+ GetDexFilesFromDexElementsArray(dex_elements, &dex_files_loaded);
}
ClassLoaderInfo* info = new ClassLoaderContext::ClassLoaderInfo(type);
@@ -1197,8 +1190,7 @@
// Add the shared libraries.
StackHandleScope<5> hs(Thread::Current());
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader.Get());
if (raw_shared_libraries != nullptr) {
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries =
@@ -1216,8 +1208,7 @@
}
}
}
- ArtField* field2 = jni::DecodeArtField(
- WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter);
+ ArtField* field2 = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
ObjPtr<mirror::Object> raw_shared_libraries_after = field2->GetObject(class_loader.Get());
if (raw_shared_libraries_after != nullptr) {
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_after =
@@ -1287,12 +1278,12 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> h_class_loader =
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader));
- if (!IsInstanceOfBaseDexClassLoader(soa, h_class_loader)) {
+ if (!IsInstanceOfBaseDexClassLoader(h_class_loader)) {
return std::map<std::string, std::string>{};
}
std::vector<const DexFile*> dex_files_loaded;
- CollectDexFilesFromSupportedClassLoader(soa, h_class_loader, &dex_files_loaded);
+ CollectDexFilesFromSupportedClassLoader(soa.Self(), h_class_loader, &dex_files_loaded);
std::map<std::string, std::string> results;
for (const DexFile* dex_file : dex_files_loaded) {
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index eceea00..806ab5e 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -51,7 +51,10 @@
// Special encoding used to denote a foreign ClassLoader was found when trying to encode class
// loader contexts for each classpath element in a ClassLoader. See
- // EncodeClassPathContextsForClassLoader. Keep in sync with PackageDexUsage in the framework.
+ // EncodeClassPathContextsForClassLoader. Keep in sync with PackageDexUsage in the framework
+ // (frameworks/base/services/core/java/com/android/server/pm/dex/PackageDexUsage.java) and
+ // DexUseManager in ART Services
+ // (art/libartservice/service/java/com/android/server/art/DexUseManager.java).
static constexpr const char* kUnsupportedClassLoaderContextEncoding =
"=UnsupportedClassLoaderContext=";
@@ -155,9 +158,8 @@
// Should only be called if OpenDexFiles() returned true.
std::vector<const DexFile*> FlattenOpenedDexFiles() const;
- // Return a colon-separated list of dex file locations from this class loader
- // context after flattening.
- std::string FlattenDexPaths() const;
+ // Return a list of dex file locations from this class loader context after flattening.
+ std::vector<std::string> FlattenDexPaths() const;
// Verifies that the current context is identical to the context encoded as `context_spec`.
// Identical means:
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index fd14476..ce9780a 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -24,6 +24,7 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_field-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/dchecked_vector.h"
#include "base/stl_util.h"
#include "class_linker.h"
@@ -40,12 +41,16 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
class ClassLoaderContextTest : public CommonRuntimeTest {
public:
+ ClassLoaderContextTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void SetUp() override {
CommonRuntimeTest::SetUp();
scratch_dir_ = std::make_unique<ScratchDir>();
@@ -236,14 +241,14 @@
ASSERT_FALSE(context->owns_the_dex_files_);
}
- void VerifyClassLoaderDexFiles(ScopedObjectAccess& soa,
+ void VerifyClassLoaderDexFiles(Thread* self,
Handle<mirror::ClassLoader> class_loader,
- jclass type,
+ ObjPtr<mirror::Class> type,
std::vector<const DexFile*>& expected_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ASSERT_TRUE(class_loader->GetClass() == soa.Decode<mirror::Class>(type));
+ ASSERT_TRUE(class_loader->GetClass() == type);
- std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(soa, class_loader);
+ std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(self, class_loader);
ASSERT_EQ(expected_dex_files.size(), class_loader_dex_files.size());
for (size_t i = 0; i < expected_dex_files.size(); i++) {
@@ -673,10 +678,9 @@
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader));
- ASSERT_TRUE(class_loader->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
+ ASSERT_TRUE(class_loader->GetClass() == WellKnownClasses::dalvik_system_PathClassLoader);
ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
// For the first class loader the class path dex files must come first and then the
// compilation sources.
@@ -685,9 +689,9 @@
expected_classpath.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
expected_classpath);
}
@@ -710,12 +714,12 @@
soa.Decode<mirror::ClassLoader>(jclass_loader));
// An empty context should create a single PathClassLoader with only the compilation sources.
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
compilation_sources_raw);
ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) {
@@ -761,31 +765,31 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify the second class loader
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
class_loader_2_dex_files);
// Verify the third class loader
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_2->GetParent());
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
// The last class loader should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) {
@@ -829,14 +833,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify the shared libraries.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -848,9 +851,9 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
class_loader_2_dex_files);
raw_shared_libraries = field->GetObject(class_loader_2.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
@@ -859,20 +862,20 @@
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries->Get(1));
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
raw_shared_libraries = field->GetObject(class_loader_3.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
// All class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_1->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) {
@@ -914,14 +917,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify its shared library.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -932,9 +934,9 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_b);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_2_dex_files);
raw_shared_libraries = field->GetObject(class_loader_2.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
@@ -943,9 +945,9 @@
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
// Verify its shared library.
@@ -959,20 +961,20 @@
Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0));
std::vector<const DexFile*> class_loader_4_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_4,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_4_dex_files);
raw_shared_libraries = field->GetObject(class_loader_4.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
// Class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) {
@@ -1014,14 +1016,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify its shared library.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -1032,9 +1033,9 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_b);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_2_dex_files);
// Verify the shared library dependency of the shared library.
@@ -1048,9 +1049,9 @@
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0));
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
raw_shared_libraries = field->GetObject(class_loader_3.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
@@ -1059,20 +1060,20 @@
Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_4_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_4,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_4_dex_files);
raw_shared_libraries = field->GetObject(class_loader_4.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
// Class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, RemoveSourceLocations) {
@@ -1129,14 +1130,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify its shared library.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -1147,18 +1147,18 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_b);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_2_dex_files);
// Verify the parent.
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
// Verify its shared library is the same as the child.
@@ -1171,9 +1171,9 @@
// Class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, EncodeInOatFile) {
@@ -1363,13 +1363,14 @@
static jobject CreateForeignClassLoader() {
ScopedObjectAccess soa(Thread::Current());
- JNIEnv* env = soa.Env();
// We cannot instantiate a ClassLoader directly, so instead we allocate an Object to represent
// our foreign ClassLoader (this works because the runtime does proper instanceof checks before
// operating on this object.
- jmethodID ctor = env->GetMethodID(WellKnownClasses::java_lang_Object, "<init>", "()V");
- return env->NewObject(WellKnownClasses::java_lang_Object, ctor);
+ ArtMethod* ctor =
+ GetClassRoot<mirror::Object>()->FindClassMethod("<init>", "()V", kRuntimePointerSize);
+ CHECK(ctor != nullptr);
+ return soa.AddLocalReference<jobject>(ctor->NewObject<>(soa.Self()));
}
TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedBase) {
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 934c92b..6868b3f 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -26,46 +26,36 @@
#include "mirror/object.h"
#include "native/dalvik_system_DexFile.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
// Returns true if the given class loader derives from BaseDexClassLoader.
-inline bool IsInstanceOfBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsInstanceOfBaseDexClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return class_loader->InstanceOf(
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_BaseDexClassLoader));
+ return class_loader->InstanceOf(WellKnownClasses::dalvik_system_BaseDexClassLoader.Get());
}
// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
// (they both have the same behaviour with respect to class lookup order)
-inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsPathOrDexClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass();
- return
- (class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
- (class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+ return (class_loader_class == WellKnownClasses::dalvik_system_PathClassLoader) ||
+ (class_loader_class == WellKnownClasses::dalvik_system_DexClassLoader);
}
// Returns true if the given class loader is an InMemoryDexClassLoader.
-inline bool IsInMemoryDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsInMemoryDexClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass();
- return (class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_InMemoryDexClassLoader));
+ return (class_loader_class == WellKnownClasses::dalvik_system_InMemoryDexClassLoader);
}
-inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsDelegateLastClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass();
- return class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+ return class_loader_class == WellKnownClasses::dalvik_system_DelegateLastClassLoader;
}
// Visit the DexPathList$Element instances in the given classloader with the given visitor.
@@ -75,20 +65,17 @@
// when the visitor ends the visit (by returning false).
// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
template <typename Visitor, typename RetType>
-inline RetType VisitClassLoaderDexElements(ScopedObjectAccessAlreadyRunnable& soa,
+inline RetType VisitClassLoaderDexElements(Thread* self,
Handle<mirror::ClassLoader> class_loader,
Visitor fn,
RetType defaultReturn)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Thread* self = soa.Self();
ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(class_loader.Get());
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList->GetObject(class_loader.Get());
if (dex_path_list != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
+ WellKnownClasses::dalvik_system_DexPathList_dexElements->GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
@@ -117,15 +104,13 @@
// when the visitor ends the visit (by returning false).
// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
template <typename Visitor, typename RetType>
-inline RetType VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+inline RetType VisitClassLoaderDexFiles(Thread* self,
Handle<mirror::ClassLoader> class_loader,
Visitor fn,
RetType defaultReturn)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ ArtField* const cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
+ ArtField* const dex_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
if (dex_file_field == nullptr || cookie_field == nullptr) {
return defaultReturn;
}
@@ -133,7 +118,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
if (dex_file != nullptr) {
- ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::LongArray> long_array =
+ hs.NewHandle(cookie_field->GetObject(dex_file)->AsLongArray());
if (long_array == nullptr) {
// This should never happen so log a warning.
LOG(WARNING) << "Null DexFile::mCookie";
@@ -155,12 +142,12 @@
return true;
};
- return VisitClassLoaderDexElements(soa, class_loader, visit_dex_files, defaultReturn);
+ return VisitClassLoaderDexElements(self, class_loader, visit_dex_files, defaultReturn);
}
// Simplified version of the above, w/o out argument.
template <typename Visitor>
-inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+inline void VisitClassLoaderDexFiles(Thread* self,
Handle<mirror::ClassLoader> class_loader,
Visitor fn)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -174,10 +161,10 @@
return fn(dex_file);
};
- VisitClassLoaderDexFiles<decltype(helper), void*>(soa,
+ VisitClassLoaderDexFiles<decltype(helper), void*>(self,
class_loader,
helper,
- /* default= */ nullptr);
+ /* defaultReturn= */ nullptr);
}
} // namespace art
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index 67eeb55..ecc8a0a 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -213,6 +213,11 @@
DCHECK_EQ(descriptor_hash, klass->DescriptorHash());
}
+inline ClassTable::TableSlot::TableSlot(uint32_t ptr, uint32_t descriptor_hash)
+ : data_(ptr | MaskHash(descriptor_hash)) {
+ DCHECK_ALIGNED(ptr, kObjectAlignment);
+}
+
template <typename Filter>
inline void ClassTable::RemoveStrongRoots(const Filter& filter) {
WriterMutexLock mu(Thread::Current(), lock_);
@@ -227,6 +232,11 @@
return Lookup(descriptor, hash);
}
+inline size_t ClassTable::Size() const {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ return classes_.size();
+}
+
} // namespace art
#endif // ART_RUNTIME_CLASS_TABLE_INL_H_
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 123c069..7e26373 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -58,18 +58,31 @@
explicit TableSlot(ObjPtr<mirror::Class> klass);
TableSlot(ObjPtr<mirror::Class> klass, uint32_t descriptor_hash);
+ TableSlot(uint32_t ptr, uint32_t descriptor_hash);
TableSlot& operator=(const TableSlot& copy) {
data_.store(copy.data_.load(std::memory_order_relaxed), std::memory_order_relaxed);
return *this;
}
+ uint32_t Data() const {
+ return data_.load(std::memory_order_relaxed);
+ }
+
bool IsNull() const REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t Hash() const {
return MaskHash(data_.load(std::memory_order_relaxed));
}
+ uint32_t NonHashData() const {
+ return RemoveHash(Data());
+ }
+
+ static uint32_t RemoveHash(uint32_t hash) {
+ return hash & ~kHashMask;
+ }
+
static uint32_t MaskHash(uint32_t hash) {
return hash & kHashMask;
}
@@ -168,6 +181,11 @@
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns the number of classes in the class table.
+ size_t Size() const
+ REQUIRES(!lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Update a class in the table with the new class. Returns the existing class which was replaced.
ObjPtr<mirror::Class> UpdateClass(const char* descriptor,
ObjPtr<mirror::Class> new_klass,
diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc
index 7dbeba5..14c5d70 100644
--- a/runtime/class_table_test.cc
+++ b/runtime/class_table_test.cc
@@ -66,7 +66,12 @@
};
-class ClassTableTest : public CommonRuntimeTest {};
+class ClassTableTest : public CommonRuntimeTest {
+ protected:
+ ClassTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
TEST_F(ClassTableTest, ClassTable) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 882e3ce..ce2090c 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -26,6 +26,7 @@
#include "dex/code_item_accessors.h"
#include "dex/dex_file_structs.h"
#include "dex/primitive.h"
+#include "entrypoints/entrypoint_utils.h"
#include "handle_scope-inl.h"
#include "instrumentation.h"
#include "interpreter/interpreter.h"
@@ -55,8 +56,28 @@
ShadowFrame* shadow_frame,
uint16_t arg_offset,
JValue* result);
+
} // namespace interpreter
+inline bool EnsureInitialized(Thread* self, ShadowFrame* shadow_frame)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (LIKELY(!shadow_frame->GetMethod()->StillNeedsClinitCheck())) {
+ return true;
+ }
+
+ // Save the shadow frame.
+ ScopedStackedShadowFramePusher pusher(self, shadow_frame);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class = hs.NewHandle(shadow_frame->GetMethod()->GetDeclaringClass());
+ if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
+ self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ DCHECK(h_class->IsInitializing());
+ return true;
+}
+
inline void PerformCall(Thread* self,
const CodeItemDataAccessor& accessor,
ArtMethod* caller_method,
@@ -65,15 +86,20 @@
JValue* result,
bool use_interpreter_entrypoint)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (LIKELY(Runtime::Current()->IsStarted())) {
- if (use_interpreter_entrypoint) {
- interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result);
- } else {
- interpreter::ArtInterpreterToCompiledCodeBridge(
- self, caller_method, callee_frame, first_dest_reg, result);
- }
- } else {
+ if (UNLIKELY(!Runtime::Current()->IsStarted())) {
interpreter::UnstartedRuntime::Invoke(self, accessor, callee_frame, result, first_dest_reg);
+ return;
+ }
+
+ if (!EnsureInitialized(self, callee_frame)) {
+ return;
+ }
+
+ if (use_interpreter_entrypoint) {
+ interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result);
+ } else {
+ interpreter::ArtInterpreterToCompiledCodeBridge(
+ self, caller_method, callee_frame, first_dest_reg, result);
}
}
@@ -149,7 +175,7 @@
return true;
}
-template<Primitive::Type field_type, bool do_assignability_check, bool transaction_active>
+template<Primitive::Type field_type, bool transaction_active>
ALWAYS_INLINE bool DoFieldPutCommon(Thread* self,
const ShadowFrame& shadow_frame,
ObjPtr<mirror::Object> obj,
@@ -210,7 +236,7 @@
break;
case Primitive::kPrimNot: {
ObjPtr<mirror::Object> reg = value.GetL();
- if (do_assignability_check && reg != nullptr) {
+ if (reg != nullptr && !shadow_frame.GetMethod()->SkipAccessChecks()) {
// FieldHelper::GetType can resolve classes, use a handle wrapper which will restore the
// object in the destructor.
ObjPtr<mirror::Class> field_class;
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index cd39686..131d0d8 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -63,7 +63,7 @@
#include "runtime_intrinsics.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -121,9 +121,12 @@
}
PreRuntimeCreate();
- if (!Runtime::Create(options, false)) {
- LOG(FATAL) << "Failed to create runtime";
- UNREACHABLE();
+ {
+ ScopedLogSeverity sls(LogSeverity::WARNING);
+ if (!Runtime::Create(options, false)) {
+ LOG(FATAL) << "Failed to create runtime";
+ UNREACHABLE();
+ }
}
PostRuntimeCreate();
runtime_.reset(Runtime::Current());
@@ -158,14 +161,11 @@
{
ScopedObjectAccess soa(Thread::Current());
+ runtime_->GetClassLinker()->RunEarlyRootClinits(soa.Self());
+ InitializeIntrinsics();
runtime_->RunRootClinits(soa.Self());
}
- // We're back in native, take the opportunity to initialize well known classes and ensure
- // intrinsics are initialized.
- WellKnownClasses::Init(Thread::Current()->GetJniEnv());
- InitializeIntrinsics();
-
runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test
// Reduce timinig-dependent flakiness in OOME behavior (eg StubTest.AllocObject).
runtime_->GetHeap()->SetMinIntervalHomogeneousSpaceCompactionByOom(0U);
@@ -195,20 +195,17 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader));
- return GetDexFiles(soa, class_loader);
+ return GetDexFiles(soa.Self(), class_loader);
}
std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(
- ScopedObjectAccess& soa,
+ Thread* self,
Handle<mirror::ClassLoader> class_loader) {
- DCHECK(
- (class_loader->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
- (class_loader->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader)));
+ DCHECK((class_loader->GetClass() == WellKnownClasses::dalvik_system_PathClassLoader) ||
+ (class_loader->GetClass() == WellKnownClasses::dalvik_system_DelegateLastClassLoader));
std::vector<const DexFile*> ret;
- VisitClassLoaderDexFiles(soa,
+ VisitClassLoaderDexFiles(self,
class_loader,
[&](const DexFile* cp_dex_file) {
if (cp_dex_file == nullptr) {
@@ -259,8 +256,9 @@
}
jobject
-CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::vector<std::string>& dex_names,
- jclass loader_class,
+CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(ScopedObjectAccess& soa,
+ const std::vector<std::string>& dex_names,
+ ObjPtr<mirror::Class> loader_class,
jobject parent_loader,
jobject shared_libraries,
jobject shared_libraries_after) {
@@ -273,39 +271,44 @@
loaded_dex_files_.push_back(std::move(dex_file));
}
}
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::Class> h_loader_class = hs.NewHandle(loader_class);
+ Handle<mirror::ClassLoader> h_parent_loader =
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(parent_loader));
+ Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries =
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries));
+ Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries_after =
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries_after));
- jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
- self,
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::ClassLoader> result = class_linker->CreateWellKnownClassLoader(
+ soa.Self(),
class_path,
- loader_class,
- parent_loader,
- shared_libraries,
- shared_libraries_after);
+ h_loader_class,
+ h_parent_loader,
+ h_shared_libraries,
+ h_shared_libraries_after);
{
// Verify we build the correct chain.
- ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
// Verify that the result has the correct class.
- CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+ CHECK_EQ(h_loader_class.Get(), result->GetClass());
// Verify that the parent is not null. The boot class loader will be set up as a
// proper object.
- ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+ ObjPtr<mirror::ClassLoader> actual_parent(result->GetParent());
CHECK(actual_parent != nullptr);
if (parent_loader != nullptr) {
// We were given a parent. Verify that it's what we expect.
- ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
- CHECK_EQ(expected_parent, actual_parent);
+ CHECK_EQ(h_parent_loader.Get(), actual_parent);
} else {
// No parent given. The parent must be the BootClassLoader.
- CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+ CHECK(class_linker->IsBootClassLoader(actual_parent));
}
}
- return result;
+ return soa.Env()->GetVm()->AddGlobalRef(soa.Self(), result);
}
jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
@@ -322,8 +325,10 @@
jobject parent_loader,
jobject shared_libraries,
jobject shared_libraries_after) {
- return LoadDexInWellKnownClassLoader(names,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ ScopedObjectAccess soa(Thread::Current());
+ return LoadDexInWellKnownClassLoader(soa,
+ names,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
parent_loader,
shared_libraries,
shared_libraries_after);
@@ -331,16 +336,22 @@
jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
jobject parent_loader) {
- return LoadDexInWellKnownClassLoader({ dex_name },
- WellKnownClasses::dalvik_system_DelegateLastClassLoader,
- parent_loader);
+ ScopedObjectAccess soa(Thread::Current());
+ return LoadDexInWellKnownClassLoader(
+ soa,
+ { dex_name },
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
+ parent_loader);
}
jobject CommonRuntimeTestImpl::LoadDexInInMemoryDexClassLoader(const std::string& dex_name,
jobject parent_loader) {
- return LoadDexInWellKnownClassLoader({ dex_name },
- WellKnownClasses::dalvik_system_InMemoryDexClassLoader,
- parent_loader);
+ ScopedObjectAccess soa(Thread::Current());
+ return LoadDexInWellKnownClassLoader(
+ soa,
+ { dex_name },
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get(),
+ parent_loader);
}
void CommonRuntimeTestImpl::FillHeap(Thread* self,
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index e136073..7904aeb 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -152,6 +152,7 @@
jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // The following helper functions return global JNI references to the class loader.
jobject LoadDexInPathClassLoader(const std::string& dex_name,
jobject parent_loader,
jobject shared_libraries = nullptr,
@@ -162,11 +163,13 @@
jobject shared_libraries_after = nullptr);
jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
jobject LoadDexInInMemoryDexClassLoader(const std::string& dex_name, jobject parent_loader);
- jobject LoadDexInWellKnownClassLoader(const std::vector<std::string>& dex_names,
- jclass loader_class,
+ jobject LoadDexInWellKnownClassLoader(ScopedObjectAccess& soa,
+ const std::vector<std::string>& dex_names,
+ ObjPtr<mirror::Class> loader_class,
jobject parent_loader,
jobject shared_libraries = nullptr,
- jobject shared_libraries_after = nullptr);
+ jobject shared_libraries_after = nullptr)
+ REQUIRES_SHARED(Locks::mutator_lock_);
void VisitDexes(ArrayRef<const std::string> dexes,
const std::function<void(MethodReference)>& method_visitor,
@@ -198,8 +201,7 @@
// Get the dex files from a PathClassLoader or DelegateLastClassLoader.
// This only looks into the current class loader and does not recurse into the parents.
std::vector<const DexFile*> GetDexFiles(jobject jclass_loader);
- std::vector<const DexFile*> GetDexFiles(ScopedObjectAccess& soa,
- Handle<mirror::ClassLoader> class_loader)
+ std::vector<const DexFile*> GetDexFiles(Thread* self, Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
// Get the first dex file from a PathClassLoader. Will abort if it is null.
@@ -221,7 +223,7 @@
static std::string GetImageLocation();
static std::string GetSystemImageFile();
- static void EnterTransactionMode();
+ static void EnterTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_);
static void ExitTransactionMode();
static void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_);
static bool IsTransactionAborted();
@@ -298,12 +300,6 @@
return; \
}
-#define TEST_DISABLED_FOR_STRING_COMPRESSION() \
- if (mirror::kUseStringCompression) { \
- printf("WARNING: TEST DISABLED FOR STRING COMPRESSION\n"); \
- return; \
- }
-
#define TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS() \
if (!gUseReadBarrier || !kUseBakerReadBarrier) { \
printf("WARNING: TEST DISABLED FOR GC WITHOUT BAKER READ BARRIER\n"); \
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index d8dea17..5182689 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -29,14 +29,14 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_instruction-inl.h"
#include "dex/invoke_type.h"
-#include "mirror/class-inl.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/method_type.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "nativehelper/scoped_local_ref.h"
#include "obj_ptr-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -237,6 +237,19 @@
va_end(args);
}
+void ThrowIllegalAccessErrorForImplementingMethod(ObjPtr<mirror::Class> klass,
+ ArtMethod* implementation_method,
+ ArtMethod* interface_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!implementation_method->IsAbstract());
+ DCHECK(!implementation_method->IsPublic());
+ ThrowIllegalAccessError(
+ klass,
+ "Method '%s' implementing interface method '%s' is not public",
+ implementation_method->PrettyMethod().c_str(),
+ interface_method->PrettyMethod().c_str());
+}
+
// IllegalAccessException
void ThrowIllegalAccessException(const char* msg) {
@@ -688,20 +701,25 @@
}
self->SetStackEndForStackOverflow(); // Allow space on the stack for constructor to execute.
- JNIEnvExt* env = self->GetJniEnv();
- std::string msg("stack size ");
- msg += PrettySize(self->GetStackSize());
// Avoid running Java code for exception initialization.
// TODO: Checks to make this a bit less brittle.
//
- // Note: this lambda ensures that the destruction of the ScopedLocalRefs will run in the extended
- // stack, which is important for modes with larger stack sizes (e.g., ASAN). Using a lambda
- // instead of a block simplifies the control flow.
- auto create_and_throw = [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Note: This lambda is used to make sure the `StackOverflowError` intitialization code
+ // does not increase the frame size of `ThrowStackOverflowError()` itself. It runs
+ // with its own frame in the extended stack, which is especially important for modes
+ // with larger stack sizes (e.g., ASAN).
+ auto create_and_throw = [self]() REQUIRES_SHARED(Locks::mutator_lock_) NO_INLINE {
+ std::string msg("stack size ");
+ msg += PrettySize(self->GetStackSize());
+
+ ScopedObjectAccessUnchecked soa(self);
+ StackHandleScope<1u> hs(self);
+
// Allocate an uninitialized object.
- ScopedLocalRef<jobject> exc(env,
- env->AllocObject(WellKnownClasses::java_lang_StackOverflowError));
+ DCHECK(WellKnownClasses::java_lang_StackOverflowError->IsInitialized());
+ Handle<mirror::Object> exc = hs.NewHandle(
+ WellKnownClasses::java_lang_StackOverflowError->AllocObject(self));
if (exc == nullptr) {
LOG(WARNING) << "Could not allocate StackOverflowError object.";
return;
@@ -720,53 +738,54 @@
// fillInStackTrace();
// detailMessage.
- // TODO: Use String::FromModifiedUTF...?
- ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg.c_str()));
- if (s == nullptr) {
- LOG(WARNING) << "Could not throw new StackOverflowError because JNI NewStringUTF failed.";
- return;
+ {
+ ObjPtr<mirror::String> s = mirror::String::AllocFromModifiedUtf8(self, msg.c_str());
+ if (s == nullptr) {
+ LOG(WARNING) << "Could not throw new StackOverflowError because message allocation failed.";
+ return;
+ }
+ WellKnownClasses::java_lang_Throwable_detailMessage
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), s);
}
- env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_detailMessage, s.get());
-
// cause.
- env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_cause, exc.get());
+ WellKnownClasses::java_lang_Throwable_cause
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), exc.Get());
// suppressedExceptions.
- ScopedLocalRef<jobject> emptylist(env, env->GetStaticObjectField(
- WellKnownClasses::java_util_Collections,
- WellKnownClasses::java_util_Collections_EMPTY_LIST));
- CHECK(emptylist != nullptr);
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_suppressedExceptions,
- emptylist.get());
+ {
+ ObjPtr<mirror::Class> j_u_c = WellKnownClasses::java_util_Collections.Get();
+ DCHECK(j_u_c->IsInitialized());
+ ObjPtr<mirror::Object> empty_list =
+ WellKnownClasses::java_util_Collections_EMPTY_LIST->GetObject(j_u_c);
+ CHECK(empty_list != nullptr);
+ WellKnownClasses::java_lang_Throwable_suppressedExceptions
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), empty_list);
+ }
// stackState is set as result of fillInStackTrace. fillInStackTrace calls
// nativeFillInStackTrace.
- ScopedLocalRef<jobject> stack_state_val(env, nullptr);
- {
- ScopedObjectAccessUnchecked soa(env); // TODO: Is this necessary?
- stack_state_val.reset(soa.Self()->CreateInternalStackTrace(soa));
- }
+ ObjPtr<mirror::Object> stack_state_val =
+ soa.Decode<mirror::Object>(self->CreateInternalStackTrace(soa));
if (stack_state_val != nullptr) {
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_stackState,
- stack_state_val.get());
+ WellKnownClasses::java_lang_Throwable_stackState
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), stack_state_val);
// stackTrace.
- ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField(
- WellKnownClasses::libcore_util_EmptyArray,
- WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT));
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_stackTrace,
- stack_trace_elem.get());
+ ObjPtr<mirror::Class> l_u_ea = WellKnownClasses::libcore_util_EmptyArray.Get();
+ DCHECK(l_u_ea->IsInitialized());
+ ObjPtr<mirror::Object> empty_ste =
+ WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT->GetObject(l_u_ea);
+ CHECK(empty_ste != nullptr);
+ WellKnownClasses::java_lang_Throwable_stackTrace
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), empty_ste);
} else {
LOG(WARNING) << "Could not create stack trace.";
// Note: we'll create an exception without stack state, which is valid.
}
// Throw the exception.
- self->SetException(self->DecodeJObject(exc.get())->AsThrowable());
+ self->SetException(exc->AsThrowable());
};
create_and_throw();
CHECK(self->IsExceptionPending());
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 843c455..d9620df 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -111,6 +111,11 @@
__attribute__((__format__(__printf__, 2, 3)))
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+void ThrowIllegalAccessErrorForImplementingMethod(ObjPtr<mirror::Class> klass,
+ ArtMethod* implementation_method,
+ ArtMethod* interface_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// IllegalAccessException
void ThrowIllegalAccessException(const char* msg)
diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc
index 9c38cce..fd9e050 100644
--- a/runtime/debug_print.cc
+++ b/runtime/debug_print.cc
@@ -29,7 +29,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -63,12 +63,10 @@
std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) {
std::ostringstream oss;
uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
- ObjPtr<mirror::Class> path_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
- ObjPtr<mirror::Class> dex_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
+ ObjPtr<mirror::Class> path_class_loader = WellKnownClasses::dalvik_system_PathClassLoader.Get();
+ ObjPtr<mirror::Class> dex_class_loader = WellKnownClasses::dalvik_system_DexClassLoader.Get();
ObjPtr<mirror::Class> delegate_last_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get();
// Print the class loader chain.
bool found_class = false;
@@ -97,13 +95,13 @@
loader->GetClass() == dex_class_loader ||
loader->GetClass() == delegate_last_class_loader) {
oss << "(";
- ScopedObjectAccessUnchecked soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
const char* path_separator = "";
const DexFile* base_dex_file = nullptr;
VisitClassLoaderDexFiles(
- soa,
+ self,
handle,
[&](const DexFile* dex_file) {
oss << path_separator;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index af36531..a7b818e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -187,33 +187,28 @@
const ArrayRef<const jbyte>& data,
/*out*/uint32_t* out_type,
/*out*/std::vector<uint8_t>* out_data) {
- ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(data.size()));
- if (dataArray.get() == nullptr) {
+ ScopedObjectAccess soa(env);
+ StackHandleScope<1u> hs(soa.Self());
+ Handle<mirror::ByteArray> data_array =
+ hs.NewHandle(mirror::ByteArray::Alloc(soa.Self(), data.size()));
+ if (data_array == nullptr) {
LOG(WARNING) << "byte[] allocation failed: " << data.size();
env->ExceptionClear();
return false;
}
- env->SetByteArrayRegion(dataArray.get(),
- 0,
- data.size(),
- reinterpret_cast<const jbyte*>(data.data()));
+ memcpy(data_array->GetData(), data.data(), data.size());
// Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
- ScopedLocalRef<jobject> chunk(
- env,
- env->CallStaticObjectMethod(
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer,
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch,
- type, dataArray.get(), 0, data.size()));
- if (env->ExceptionCheck()) {
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
+ ArtMethod* dispatch = WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ ObjPtr<mirror::Object> chunk = dispatch->InvokeStatic<'L', 'I', 'L', 'I', 'I'>(
+ soa.Self(), type, data_array.Get(), 0, static_cast<jint>(data.size()));
+ if (soa.Self()->IsExceptionPending()) {
LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl
- << self->GetException()->Dump();
- self->ClearException();
+ << soa.Self()->GetException()->Dump();
+ soa.Self()->ClearException();
return false;
}
- if (chunk.get() == nullptr) {
+ if (chunk == nullptr) {
return false;
}
@@ -229,38 +224,33 @@
*
* So we're pretty much stuck with copying data around multiple times.
*/
- ScopedLocalRef<jbyteArray> replyData(
- env,
- reinterpret_cast<jbyteArray>(
- env->GetObjectField(
- chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data)));
- jint offset = env->GetIntField(chunk.get(),
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset);
- jint length = env->GetIntField(chunk.get(),
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length);
- *out_type = env->GetIntField(chunk.get(),
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type);
+ ObjPtr<mirror::ByteArray> reply_data = ObjPtr<mirror::ByteArray>::DownCast(
+ WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data->GetObject(chunk));
+ jint offset = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset->GetInt(chunk);
+ jint length = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length->GetInt(chunk);
+ *out_type = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type->GetInt(chunk);
VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d",
type,
- replyData.get(),
+ reply_data.Ptr(),
offset,
length);
- out_data->resize(length);
- env->GetByteArrayRegion(replyData.get(),
- offset,
- length,
- reinterpret_cast<jbyte*>(out_data->data()));
- if (env->ExceptionCheck()) {
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
- LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x",
- type) << std::endl << self->GetException()->Dump();
- self->ClearException();
+ if (reply_data == nullptr) {
+ LOG(INFO) << "Null reply data";
return false;
}
+ jint reply_length = reply_data->GetLength();
+ if (offset < 0 || offset > reply_length || length < 0 || length > reply_length - offset) {
+ LOG(INFO) << "Invalid reply data range: offset=" << offset << ", length=" << length
+ << " reply_length=" << reply_length;
+ return false;
+ }
+
+ out_data->resize(length);
+ memcpy(out_data->data(), reply_data->GetData() + offset, length);
+
return true;
}
@@ -273,12 +263,13 @@
/* try anyway? */
}
+ // TODO: Can we really get here while not `Runnable`? If not, we do not need the `soa`.
+ ScopedObjectAccessUnchecked soa(self);
JNIEnv* env = self->GetJniEnv();
jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
- env->CallStaticVoidMethod(WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer,
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast,
- event);
- if (env->ExceptionCheck()) {
+ ArtMethod* broadcast = WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
+ broadcast->InvokeStatic<'V', 'I'>(self, event);
+ if (self->IsExceptionPending()) {
LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
env->ExceptionDescribe();
env->ExceptionClear();
diff --git a/runtime/deoptimization_kind.h b/runtime/deoptimization_kind.h
index c2e6a65..65a1cf1 100644
--- a/runtime/deoptimization_kind.h
+++ b/runtime/deoptimization_kind.h
@@ -56,9 +56,10 @@
// for functions that are already on stack. The value in the slot specifies the
// reason we need to deoptimize.
enum class DeoptimizeFlagValue: uint8_t {
- kCHA = 0b01,
- kDebug = 0b10,
- kAll = kCHA | kDebug
+ kCHA = 0b001,
+ kForceDeoptForRedefinition = 0b010,
+ kCheckCallerForDeopt = 0b100,
+ kAll = kCHA | kForceDeoptForRedefinition | kCheckCallerForDeopt
};
} // namespace art
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 5a409f0..7c461fd 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -21,7 +21,7 @@
#include "android-base/stringprintf.h"
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/sdk_version.h"
#include "class_linker-inl.h"
#include "class_root-inl.h"
@@ -356,7 +356,6 @@
uint32_t size = DecodeUnsignedLeb128(annotation);
Thread* self = Thread::Current();
- ScopedObjectAccessUnchecked soa(self);
StackHandleScope<4> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> annotation_class(hs.NewHandle(
@@ -371,10 +370,8 @@
return nullptr;
}
- ObjPtr<mirror::Class> annotation_member_class =
- soa.Decode<mirror::Class>(WellKnownClasses::libcore_reflect_AnnotationMember);
ObjPtr<mirror::Class> annotation_member_array_class =
- class_linker->FindArrayClass(self, annotation_member_class);
+ WellKnownClasses::ToClass(WellKnownClasses::libcore_reflect_AnnotationMember__array);
if (annotation_member_array_class == nullptr) {
return nullptr;
}
@@ -397,18 +394,16 @@
h_element_array->SetWithoutChecks<false>(i, new_member);
}
- JValue result;
ArtMethod* create_annotation_method =
- jni::DecodeArtMethod(WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation);
- uint32_t args[2] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(annotation_class.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h_element_array.Get())) };
- create_annotation_method->Invoke(self, args, sizeof(args), &result, "LLL");
+ WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
+ ObjPtr<mirror::Object> result = create_annotation_method->InvokeStatic<'L', 'L', 'L'>(
+ self, annotation_class.Get(), h_element_array.Get());
if (self->IsExceptionPending()) {
LOG(INFO) << "Exception in AnnotationFactory.createAnnotation";
return nullptr;
}
- return result.GetL();
+ return result;
}
template <bool kTransactionActive>
@@ -704,8 +699,6 @@
StackHandleScope<5> hs(self);
uint32_t element_name_index = DecodeUnsignedLeb128(annotation);
const char* name = dex_file.StringDataByIdx(dex::StringIndex(element_name_index));
- Handle<mirror::String> string_name(
- hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, name)));
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
ArtMethod* annotation_method =
@@ -713,7 +706,19 @@
if (annotation_method == nullptr) {
return nullptr;
}
- Handle<mirror::Class> method_return(hs.NewHandle(annotation_method->ResolveReturnType()));
+
+ Handle<mirror::String> string_name =
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, name));
+ if (UNLIKELY(string_name == nullptr)) {
+ LOG(ERROR) << "Failed to allocate name for annotation member";
+ return nullptr;
+ }
+
+ Handle<mirror::Class> method_return = hs.NewHandle(annotation_method->ResolveReturnType());
+ if (UNLIKELY(method_return == nullptr)) {
+ LOG(ERROR) << "Failed to resolve method return type for annotation member";
+ return nullptr;
+ }
DexFile::AnnotationValue annotation_value;
if (!ProcessAnnotationValue<false>(klass,
@@ -721,37 +726,26 @@
&annotation_value,
method_return,
DexFile::kAllObjects)) {
+ // TODO: Logging the error breaks run-test 005-annotations.
+ // LOG(ERROR) << "Failed to process annotation value for annotation member";
return nullptr;
}
- Handle<mirror::Object> value_object(hs.NewHandle(annotation_value.value_.GetL()));
+ Handle<mirror::Object> value_object = hs.NewHandle(annotation_value.value_.GetL());
- ObjPtr<mirror::Class> annotation_member_class =
- WellKnownClasses::ToClass(WellKnownClasses::libcore_reflect_AnnotationMember);
- Handle<mirror::Object> new_member(hs.NewHandle(annotation_member_class->AllocObject(self)));
- ObjPtr<mirror::Method> method_obj_ptr = (pointer_size == PointerSize::k64)
+ Handle<mirror::Method> method_object = hs.NewHandle((pointer_size == PointerSize::k64)
? mirror::Method::CreateFromArtMethod<PointerSize::k64>(self, annotation_method)
- : mirror::Method::CreateFromArtMethod<PointerSize::k32>(self, annotation_method);
- Handle<mirror::Method> method_object(hs.NewHandle(method_obj_ptr));
-
- if (new_member == nullptr || string_name == nullptr ||
- method_object == nullptr || method_return == nullptr) {
- LOG(ERROR) << StringPrintf("Failed creating annotation element (m=%p n=%p a=%p r=%p",
- new_member.Get(), string_name.Get(), method_object.Get(), method_return.Get());
+ : mirror::Method::CreateFromArtMethod<PointerSize::k32>(self, annotation_method));
+ if (UNLIKELY(method_object == nullptr)) {
+ LOG(ERROR) << "Failed to create method object for annotation member";
return nullptr;
}
- JValue result;
- ArtMethod* annotation_member_init =
- jni::DecodeArtMethod(WellKnownClasses::libcore_reflect_AnnotationMember_init);
- uint32_t args[5] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(new_member.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(string_name.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(value_object.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_return.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_object.Get()))
- };
- annotation_member_init->Invoke(self, args, sizeof(args), &result, "VLLLL");
- if (self->IsExceptionPending()) {
- LOG(INFO) << "Exception in AnnotationMember.<init>";
+ Handle<mirror::Object> new_member =
+ WellKnownClasses::libcore_reflect_AnnotationMember_init->NewObject<'L', 'L', 'L', 'L'>(
+ hs, self, string_name, value_object, method_return, method_object);
+ if (new_member == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ LOG(ERROR) << "Failed to create annotation member";
return nullptr;
}
@@ -841,6 +835,38 @@
return annotation_value.value_.GetL();
}
+template<typename T>
+static inline ObjPtr<mirror::ObjectArray<T>> GetAnnotationArrayValue(
+ Handle<mirror::Class> klass,
+ const char* annotation_name,
+ const char* value_name)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassData data(klass);
+ const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
+ if (annotation_set == nullptr) {
+ return nullptr;
+ }
+ const AnnotationItem* annotation_item =
+ SearchAnnotationSet(data.GetDexFile(), annotation_set, annotation_name,
+ DexFile::kDexVisibilitySystem);
+ if (annotation_item == nullptr) {
+ return nullptr;
+ }
+ StackHandleScope<1> hs(Thread::Current());
+ Handle<mirror::Class> class_array_class =
+ hs.NewHandle(GetClassRoot<mirror::ObjectArray<T>>());
+ DCHECK(class_array_class != nullptr);
+ ObjPtr<mirror::Object> obj = GetAnnotationValue(data,
+ annotation_item,
+ value_name,
+ class_array_class,
+ DexFile::kDexAnnotationArray);
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ return obj->AsObjectArray<T>();
+}
+
static ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureValue(
const ClassData& klass,
const AnnotationSetItem* annotation_set)
@@ -895,10 +921,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
- ScopedObjectAccessUnchecked soa(self);
StackHandleScope<2> hs(self);
Handle<mirror::Class> annotation_array_class(hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array)));
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array)));
if (annotation_set == nullptr) {
return mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), 0);
}
@@ -953,10 +978,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
- ScopedObjectAccessUnchecked soa(self);
StackHandleScope<1> hs(self);
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::Class> annotation_array_array_class =
Runtime::Current()->GetClassLinker()->FindArrayClass(self, annotation_array_class);
if (annotation_array_array_class == nullptr) {
@@ -1310,6 +1334,20 @@
WellKnownClasses::dalvik_annotation_optimization_NeverCompile);
}
+bool MethodIsNeverInline(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index) {
+ const dex::AnnotationSetItem* annotation_set =
+ FindAnnotationSetForMethod(dex_file, class_def, method_index);
+ if (annotation_set == nullptr) {
+ return false;
+ }
+ return IsMethodBuildAnnotationPresent(
+ dex_file,
+ *annotation_set,
+ "Ldalvik/annotation/optimization/NeverInline;",
+ WellKnownClasses::dalvik_annotation_optimization_NeverInline);
+}
bool FieldIsReachabilitySensitive(const DexFile& dex_file,
const dex::ClassDef& class_def,
@@ -1478,28 +1516,9 @@
}
ObjPtr<mirror::ObjectArray<mirror::Class>> GetDeclaredClasses(Handle<mirror::Class> klass) {
- ClassData data(klass);
- const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
- if (annotation_set == nullptr) {
- return nullptr;
- }
- const AnnotationItem* annotation_item =
- SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/MemberClasses;",
- DexFile::kDexVisibilitySystem);
- if (annotation_item == nullptr) {
- return nullptr;
- }
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> class_array_class =
- hs.NewHandle(GetClassRoot<mirror::ObjectArray<mirror::Class>>());
- DCHECK(class_array_class != nullptr);
- ObjPtr<mirror::Object> obj =
- GetAnnotationValue(data, annotation_item, "value", class_array_class,
- DexFile::kDexAnnotationArray);
- if (obj == nullptr) {
- return nullptr;
- }
- return obj->AsObjectArray<mirror::Class>();
+ return GetAnnotationArrayValue<mirror::Class>(klass,
+ "Ldalvik/annotation/MemberClasses;",
+ "value");
}
ObjPtr<mirror::Class> GetDeclaringClass(Handle<mirror::Class> klass) {
@@ -1714,6 +1733,46 @@
return data.GetDexFile().StringDataByIdx(index);
}
+ObjPtr<mirror::Class> GetNestHost(Handle<mirror::Class> klass) {
+ ClassData data(klass);
+ const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
+ if (annotation_set == nullptr) {
+ return nullptr;
+ }
+ const AnnotationItem* annotation_item =
+ SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/NestHost;",
+ DexFile::kDexVisibilitySystem);
+ if (annotation_item == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::Object> obj = GetAnnotationValue(data,
+ annotation_item,
+ "host",
+ ScopedNullHandle<mirror::Class>(),
+ DexFile::kDexAnnotationType);
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ if (!obj->IsClass()) {
+ // TypeNotPresentException, throw the NoClassDefFoundError.
+ Thread::Current()->SetException(obj->AsThrowable()->GetCause());
+ return nullptr;
+ }
+ return obj->AsClass();
+}
+
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetNestMembers(Handle<mirror::Class> klass) {
+ return GetAnnotationArrayValue<mirror::Class>(klass,
+ "Ldalvik/annotation/NestMembers;",
+ "classes");
+}
+
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetPermittedSubclasses(Handle<mirror::Class> klass) {
+ return GetAnnotationArrayValue<mirror::Class>(klass,
+ "Ldalvik/annotation/PermittedSubclasses;",
+ "value");
+}
+
bool IsClassAnnotationPresent(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) {
ClassData data(klass);
const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index 3ef67e5..ea11e10 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -91,6 +91,11 @@
bool MethodIsNeverCompile(const DexFile& dex_file,
const dex::ClassDef& class_def,
uint32_t method_index);
+// Is the method from the `dex_file` with the given `field_index`
+// annotated with @dalvik.annotation.optimization.NeverInline?
+bool MethodIsNeverInline(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index);
// Is the field from the `dex_file` with the given `field_index`
// annotated with @dalvik.annotation.optimization.ReachabilitySensitive?
bool FieldIsReachabilitySensitive(const DexFile& dex_file,
@@ -136,6 +141,12 @@
Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
const char* GetSourceDebugExtension(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::Class> GetNestHost(Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetNestMembers(Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetPermittedSubclasses(Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
bool IsClassAnnotationPresent(Handle<mirror::Class> klass,
Handle<mirror::Class> annotation_class)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index e867c4f..c2d37c2 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -21,8 +21,6 @@
#include <string>
#include <vector>
-#include <gtest/gtest.h>
-
#include "base/file_utils.h"
#include "base/os.h"
#include "base/stl_util.h"
@@ -34,8 +32,10 @@
#include "exec_utils.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
+#include "gtest/gtest.h"
#include "oat_file_assistant.h"
#include "runtime.h"
+#include "ziparchive/zip_writer.h"
namespace art {
@@ -109,10 +109,9 @@
<< "Expected dex file to be at: " << GetDexSrc1();
ASSERT_TRUE(OS::FileExists(GetResourceOnlySrc1().c_str()))
<< "Expected stripped dex file to be at: " << GetResourceOnlySrc1();
- ASSERT_FALSE(
- dex_file_loader.GetMultiDexChecksums(
- GetResourceOnlySrc1().c_str(), &checksums, &dex_locations, &error_msg))
- << "Expected stripped dex file to be stripped: " << GetResourceOnlySrc1();
+ ASSERT_TRUE(dex_file_loader.GetMultiDexChecksums(
+ GetResourceOnlySrc1().c_str(), &checksums, &dex_locations, &error_msg))
+ << "Expected stripped dex file to be stripped: " << GetResourceOnlySrc1();
ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
<< "Expected dex file to be at: " << GetDexSrc2();
@@ -178,6 +177,10 @@
return GetTestDexFileName("MultiDex");
}
+ std::string GetMultiDexUncompressedAlignedSrc1() const {
+ return GetTestDexFileName("MultiDexUncompressedAligned");
+ }
+
// Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
// with the contents of the secondary dex file changed.
std::string GetMultiDexSrc2() const {
@@ -243,6 +246,23 @@
return res.status_code;
}
+
+ void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) {
+ // Read the vdex bytes.
+ std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str()));
+ std::vector<uint8_t> data(vdex_file->GetLength());
+ ASSERT_TRUE(vdex_file->ReadFully(data.data(), data.size()));
+
+ // Zip the content.
+ FILE* file = fopen(out_dm.c_str(), "wb");
+ ZipWriter writer(file);
+ writer.StartEntry("primary.vdex", ZipWriter::kAlign32);
+ writer.WriteBytes(data.data(), data.size());
+ writer.FinishEntry();
+ writer.Finish();
+ fflush(file);
+ fclose(file);
+ }
};
} // namespace art
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index eb5b149..1f44a67 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#include <string>
-#include <vector>
+#include "dexopt_test.h"
#include <gtest/gtest.h>
#include <procinfo/process_map.h>
+#include <string>
+#include <vector>
+
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "arch/instruction_set.h"
#include "base/file_utils.h"
#include "base/mem_map.h"
#include "common_runtime_test.h"
@@ -29,10 +32,10 @@
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file_loader.h"
#include "dex2oat_environment_test.h"
-#include "dexopt_test.h"
#include "gc/space/image_space.h"
#include "hidden_api.h"
#include "oat.h"
+#include "oat_file_assistant.h"
#include "profile/profile_compilation_info.h"
namespace art {
@@ -163,22 +166,13 @@
EXPECT_EQ(filter, odex_file->GetCompilerFilter());
if (CompilerFilter::DependsOnImageChecksum(filter)) {
- const OatHeader& oat_header = odex_file->GetOatHeader();
- const char* oat_bcp = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
- ASSERT_TRUE(oat_bcp != nullptr);
- ASSERT_EQ(oat_bcp, android::base::Join(Runtime::Current()->GetBootClassPathLocations(), ':'));
- const char* checksums = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
- ASSERT_TRUE(checksums != nullptr);
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(/*spec=*/"");
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ context.get(),
+ /*load_executable=*/false);
- bool match = gc::space::ImageSpace::VerifyBootClassPathChecksums(
- checksums,
- oat_bcp,
- ArrayRef<const std::string>(&image_location, 1),
- ArrayRef<const std::string>(Runtime::Current()->GetBootClassPathLocations()),
- ArrayRef<const std::string>(Runtime::Current()->GetBootClassPath()),
- ArrayRef<const int>(Runtime::Current()->GetBootClassPathFds()),
- kRuntimeISA,
- &error_msg);
+ bool match = oat_file_assistant.ValidateBootClassPathChecksums(*odex_file);
ASSERT_EQ(!with_alternate_image, match) << error_msg;
}
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 4ee1013..4a1dfba 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -34,7 +34,6 @@
#include "imt_conflict_table.h"
#include "imtable-inl.h"
#include "indirect_reference_table.h"
-#include "jni/jni_internal.h"
#include "mirror/array-alloc-inl.h"
#include "mirror/class-alloc-inl.h"
#include "mirror/class-inl.h"
@@ -122,7 +121,7 @@
uint32_t method_index = code_info.GetMethodIndexOf(inline_info);
if (inline_info.GetDexPc() == static_cast<uint32_t>(-1)) {
// "charAt" special case. It is the only non-leaf method we inline across dex files.
- ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+ ArtMethod* inlined_method = WellKnownClasses::java_lang_String_charAt;
DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index);
return inlined_method;
}
@@ -290,7 +289,6 @@
}
-template <bool kAccessCheck>
ALWAYS_INLINE
inline ObjPtr<mirror::Class> CheckArrayAlloc(dex::TypeIndex type_idx,
int32_t component_count,
@@ -312,7 +310,7 @@
}
CHECK(klass->IsArrayClass()) << klass->PrettyClass();
}
- if (kAccessCheck) {
+ if (!method->SkipAccessChecks()) {
ObjPtr<mirror::Class> referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
ThrowIllegalAccessErrorClass(referrer, klass);
@@ -327,7 +325,7 @@
// it cannot be resolved, throw an error. If it can, use it to create an array.
// When verification/compiler hasn't been able to verify access, optionally perform an access
// check.
-template <bool kAccessCheck, bool kInstrumented>
+template <bool kInstrumented>
ALWAYS_INLINE
inline ObjPtr<mirror::Array> AllocArrayFromCode(dex::TypeIndex type_idx,
int32_t component_count,
@@ -335,8 +333,7 @@
Thread* self,
gc::AllocatorType allocator_type) {
bool slow_path = false;
- ObjPtr<mirror::Class> klass =
- CheckArrayAlloc<kAccessCheck>(type_idx, component_count, method, &slow_path);
+ ObjPtr<mirror::Class> klass = CheckArrayAlloc(type_idx, component_count, method, &slow_path);
if (UNLIKELY(slow_path)) {
if (klass == nullptr) {
return nullptr;
@@ -376,76 +373,77 @@
allocator_type);
}
-template<FindFieldType type, bool access_check>
+FLATTEN
+inline ArtField* ResolveFieldWithAccessChecks(Thread* self,
+ ClassLinker* class_linker,
+ uint16_t field_index,
+ ArtMethod* caller,
+ bool is_static,
+ bool is_put,
+ size_t resolve_field_type) // Resolve if not zero
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (caller->SkipAccessChecks()) {
+ return class_linker->ResolveField(field_index, caller, is_static);
+ }
+
+ caller = caller->GetInterfaceMethodIfProxy(class_linker->GetImagePointerSize());
+
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(caller->GetDexCache()));
+ Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(caller->GetClassLoader()));
+
+ ArtField* resolved_field = class_linker->ResolveFieldJLS(field_index,
+ h_dex_cache,
+ h_class_loader);
+ if (resolved_field == nullptr) {
+ return nullptr;
+ }
+
+ ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
+ if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
+ ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, caller);
+ return nullptr;
+ }
+ ObjPtr<mirror::Class> referring_class = caller->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
+ resolved_field,
+ caller->GetDexCache(),
+ field_index))) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ if (UNLIKELY(is_put && !resolved_field->CanBeChangedBy(caller))) {
+ ThrowIllegalAccessErrorFinalField(caller, resolved_field);
+ return nullptr;
+ }
+
+ if (resolve_field_type != 0u) {
+ StackArtFieldHandleScope<1> rhs(self);
+ ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(resolved_field));
+ if (resolved_field->ResolveType().IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ resolved_field = field_handle.Get();
+ }
+ return resolved_field;
+}
+
+template<FindFieldType type>
inline ArtField* FindFieldFromCode(uint32_t field_idx,
ArtMethod* referrer,
Thread* self,
- size_t expected_size) {
- constexpr bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0;
+ bool should_resolve_type = false) {
constexpr bool is_set = (type & FindFieldFlags::WriteBit) != 0;
constexpr bool is_static = (type & FindFieldFlags::StaticBit) != 0;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-
- ArtField* resolved_field;
- if (access_check) {
- // Slow path: According to JLS 13.4.8, a linkage error may occur if a compile-time
- // qualifying type of a field and the resolved run-time qualifying type of a field differed
- // in their static-ness.
- //
- // In particular, don't assume the dex instruction already correctly knows if the
- // real field is static or not. The resolution must not be aware of this.
- ArtMethod* method = referrer->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(method->GetDexCache()));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(method->GetClassLoader()));
-
- resolved_field = class_linker->ResolveFieldJLS(field_idx,
- h_dex_cache,
- h_class_loader);
- } else {
- // Fast path: Verifier already would've called ResolveFieldJLS and we wouldn't
- // be executing here if there was a static/non-static mismatch.
- resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
- }
-
- if (UNLIKELY(resolved_field == nullptr)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
- if (access_check) {
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
- return nullptr;
- }
- ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
- resolved_field,
- referrer->GetDexCache(),
- field_idx))) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- if (UNLIKELY(is_set && !resolved_field->CanBeChangedBy(referrer))) {
- ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
- return nullptr; // Failure.
- } else {
- if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
- resolved_field->FieldSize() != expected_size)) {
- self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
- "Attempted read of %zd-bit %s on field '%s'",
- expected_size * (32 / sizeof(int32_t)),
- is_primitive ? "primitive" : "non-primitive",
- resolved_field->PrettyField(true).c_str());
- return nullptr; // Failure.
- }
- }
- }
- if (!is_static) {
+ ArtField* resolved_field = ResolveFieldWithAccessChecks(
+ self, class_linker, field_idx, referrer, is_static, is_set, should_resolve_type ? 1u : 0u);
+ if (!is_static || resolved_field == nullptr) {
// instance fields must be being accessed on an initialized class
return resolved_field;
} else {
+ ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
// If the class is initialized we're done.
if (LIKELY(fields_class->IsVisiblyInitialized())) {
return resolved_field;
@@ -463,28 +461,147 @@
}
}
+// NOLINTBEGIN(bugprone-macro-parentheses)
// Explicit template declarations of FindFieldFromCode for all field access types.
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type) \
template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \
-ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \
- ArtMethod* referrer, \
- Thread* self, size_t expected_size) \
+ArtField* FindFieldFromCode<_type>(uint32_t field_idx, \
+ ArtMethod* referrer, \
+ Thread* self, \
+ bool should_resolve_type = false) \
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
- EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \
- EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true)
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstanceObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstanceObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstancePrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstancePrimitiveWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticPrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticPrimitiveWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite);
-
-#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+// NOLINTEND(bugprone-macro-parentheses)
+
+static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const dex::MethodId& method_id = dex_file->GetMethodId(method_idx);
+ const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_);
+ const char* method_name = dex_file->GetMethodName(method_id);
+ // Instead of calling ResolveMethod() which has suspend point and can trigger
+ // GC, look up the method symbolically.
+ // Compare method's class name and method name against string init.
+ // It's ok since it's not allowed to create your own java/lang/String.
+ // TODO: verify that assumption.
+ if ((strcmp(class_name, "Ljava/lang/String;") == 0) &&
+ (strcmp(method_name, "<init>") == 0)) {
+ return true;
+ }
+ return false;
+}
+
+static inline bool IsStringInit(const Instruction& instr, ArtMethod* caller)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (instr.Opcode() == Instruction::INVOKE_DIRECT ||
+ instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) {
+ uint16_t callee_method_idx = (instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) ?
+ instr.VRegB_3rc() : instr.VRegB_35c();
+ return IsStringInit(caller->GetDexFile(), callee_method_idx);
+ }
+ return false;
+}
+
+extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr);
+
+template <InvokeType type>
+ArtMethod* FindMethodToCall(Thread* self,
+ ArtMethod* caller,
+ ObjPtr<mirror::Object>* this_object,
+ const Instruction& inst,
+ bool only_lookup_tls_cache,
+ /*out*/ bool* string_init)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+
+ // Try to find the method in thread-local cache.
+ size_t tls_value = 0u;
+ if (!self->GetInterpreterCache()->Get(self, &inst, &tls_value)) {
+ if (only_lookup_tls_cache) {
+ return nullptr;
+ }
+ DCHECK(!self->IsExceptionPending());
+ // NterpGetMethod can suspend, so save this_object.
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
+ tls_value = NterpGetMethod(self, caller, reinterpret_cast<const uint16_t*>(&inst));
+ if (self->IsExceptionPending()) {
+ return nullptr;
+ }
+ }
+
+ if (type != kStatic && UNLIKELY((*this_object) == nullptr)) {
+ if (UNLIKELY(IsStringInit(inst, caller))) {
+ // Hack for String init:
+ //
+ // We assume that the input of String.<init> in verified code is always
+ // an uninitialized reference. If it is a null constant, it must have been
+ // optimized out by the compiler and we arrive here after deoptimization.
+ // Do not throw NullPointerException.
+ } else {
+ // Maintain interpreter-like semantics where NullPointerException is thrown
+ // after potential NoSuchMethodError from class linker.
+ const uint32_t method_idx = inst.VRegB();
+ ThrowNullPointerExceptionForMethodAccess(method_idx, type);
+ return nullptr;
+ }
+ }
+
+ static constexpr size_t kStringInitMethodFlag = 0b1;
+ static constexpr size_t kInvokeInterfaceOnObjectMethodFlag = 0b1;
+ static constexpr size_t kMethodMask = ~0b11;
+
+ ArtMethod* called_method = nullptr;
+ switch (type) {
+ case kDirect:
+ case kSuper:
+ case kStatic:
+ // Note: for the interpreter, the String.<init> special casing for invocation is handled
+ // in DoCallCommon.
+ *string_init = ((tls_value & kStringInitMethodFlag) != 0);
+ DCHECK_EQ(*string_init, IsStringInit(inst, caller));
+ called_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask);
+ break;
+ case kInterface:
+ if ((tls_value & kInvokeInterfaceOnObjectMethodFlag) != 0) {
+ // invokeinterface on a j.l.Object method.
+ uint16_t method_index = tls_value >> 16;
+ called_method = (*this_object)->GetClass()->GetVTableEntry(method_index, pointer_size);
+ } else {
+ ArtMethod* interface_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask);
+ called_method = (*this_object)->GetClass()->GetImt(pointer_size)->Get(
+ interface_method->GetImtIndex(), pointer_size);
+ if (called_method->IsRuntimeMethod()) {
+ called_method = (*this_object)->GetClass()->FindVirtualMethodForInterface(
+ interface_method, pointer_size);
+ if (UNLIKELY(called_method == nullptr)) {
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
+ interface_method, *this_object, caller);
+ return nullptr;
+ }
+ }
+ }
+ break;
+ case kVirtual:
+ called_method = (*this_object)->GetClass()->GetVTableEntry(tls_value, pointer_size);
+ break;
+ }
+
+ if (UNLIKELY(!called_method->IsInvokable())) {
+ called_method->ThrowInvocationTimeError((type == kStatic) ? nullptr : *this_object);
+ return nullptr;
+ }
+ DCHECK(!called_method->IsRuntimeMethod()) << called_method->PrettyMethod();
+ return called_method;
+}
template<bool access_check>
ALWAYS_INLINE ArtMethod* FindSuperMethodToCall(uint32_t method_idx,
@@ -546,130 +663,6 @@
return super_class->GetVTableEntry(vtable_index, linker->GetImagePointerSize());
}
-// Follow virtual/interface indirections if applicable.
-// Will throw null-pointer exception the if the object is null.
-template<InvokeType type, bool access_check>
-ALWAYS_INLINE ArtMethod* FindMethodToCall(uint32_t method_idx,
- ArtMethod* resolved_method,
- ObjPtr<mirror::Object>* this_object,
- ArtMethod* referrer,
- Thread* self)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- // Null pointer check.
- if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
- if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() &&
- resolved_method->IsConstructor())) {
- // Hack for String init:
- //
- // We assume that the input of String.<init> in verified code is always
- // an unitialized reference. If it is a null constant, it must have been
- // optimized out by the compiler. Do not throw NullPointerException.
- } else {
- // Maintain interpreter-like semantics where NullPointerException is thrown
- // after potential NoSuchMethodError from class linker.
- ThrowNullPointerExceptionForMethodAccess(method_idx, type);
- return nullptr; // Failure.
- }
- }
- switch (type) {
- case kStatic:
- case kDirect:
- return resolved_method;
- case kVirtual: {
- ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (access_check &&
- (!klass->HasVTable() ||
- vtable_index >= static_cast<uint32_t>(klass->GetVTableLength()))) {
- // Behavior to agree with that of the verifier.
- ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
- resolved_method->GetName(), resolved_method->GetSignature());
- return nullptr; // Failure.
- }
- DCHECK(klass->HasVTable()) << klass->PrettyClass();
- return klass->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize());
- }
- case kSuper: {
- return FindSuperMethodToCall<access_check>(method_idx, resolved_method, referrer, self);
- }
- case kInterface: {
- size_t imt_index = resolved_method->GetImtIndex();
- PointerSize pointer_size = class_linker->GetImagePointerSize();
- ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
- ArtMethod* imt_method = klass->GetImt(pointer_size)->Get(imt_index, pointer_size);
- if (!imt_method->IsRuntimeMethod()) {
- if (kIsDebugBuild) {
- ArtMethod* method = klass->FindVirtualMethodForInterface(
- resolved_method, class_linker->GetImagePointerSize());
- CHECK_EQ(imt_method, method) << ArtMethod::PrettyMethod(resolved_method) << " / "
- << imt_method->PrettyMethod() << " / "
- << ArtMethod::PrettyMethod(method) << " / "
- << klass->PrettyClass();
- }
- return imt_method;
- } else {
- ArtMethod* interface_method = klass->FindVirtualMethodForInterface(
- resolved_method, class_linker->GetImagePointerSize());
- if (UNLIKELY(interface_method == nullptr)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method,
- *this_object, referrer);
- return nullptr; // Failure.
- }
- return interface_method;
- }
- }
- default:
- LOG(FATAL) << "Unknown invoke type " << type;
- return nullptr; // Failure.
- }
-}
-
-template<InvokeType type, bool access_check>
-inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
- ObjPtr<mirror::Object>* this_object,
- ArtMethod* referrer,
- Thread* self) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- constexpr ClassLinker::ResolveMode resolve_mode =
- access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
- : ClassLinker::ResolveMode::kNoChecks;
- ArtMethod* resolved_method;
- if (type == kStatic) {
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
- } else {
- StackHandleScope<1> hs(self);
- HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
- }
- if (UNLIKELY(resolved_method == nullptr)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- return FindMethodToCall<type, access_check>(
- method_idx, resolved_method, this_object, referrer, self);
-}
-
-// Explicit template declarations of FindMethodFromCode for all invoke types.
-#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
- template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \
- ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \
- ObjPtr<mirror::Object>* this_object, \
- ArtMethod* referrer, \
- Thread* self)
-#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
- EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \
- EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true)
-
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface);
-
-#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL
-#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL
-
inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx,
ArtMethod* referrer,
Thread* self,
@@ -724,13 +717,6 @@
}
}
-inline bool NeedsClinitCheckBeforeCall(ArtMethod* method) {
- // The class needs to be visibly initialized before we can use entrypoints to
- // compiled code for static methods. See b/18161648 . The class initializer is
- // special as it is invoked during initialization and does not need the check.
- return method->IsStatic() && !method->IsConstructor();
-}
-
inline ObjPtr<mirror::Object> GetGenericJniSynchronizationObject(Thread* self, ArtMethod* called)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!called->IsCriticalNative());
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 63d2aa4..e6d0adb 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -22,6 +22,7 @@
#include "base/mutex.h"
#include "base/sdk_version.h"
#include "class_linker-inl.h"
+#include "class_root-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/method_reference.h"
#include "entrypoints/entrypoint_utils-inl.h"
@@ -33,14 +34,14 @@
#include "mirror/class-inl.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "nth_caller_visitor.h"
#include "oat_file.h"
#include "oat_file-inl.h"
#include "oat_quick_method_header.h"
#include "reflection.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -65,60 +66,70 @@
jobject rcvr_jobj,
jobject interface_method_jobj,
std::vector<jvalue>& args) {
- DCHECK(soa.Env()->IsInstanceOf(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy));
+ StackHandleScope<4u> hs(soa.Self());
+ DCHECK(rcvr_jobj != nullptr);
+ Handle<mirror::Object> h_receiver = hs.NewHandle(soa.Decode<mirror::Object>(rcvr_jobj));
+ DCHECK(h_receiver->InstanceOf(GetClassRoot(ClassRoot::kJavaLangReflectProxy)));
+ Handle<mirror::Method> h_interface_method =
+ hs.NewHandle(soa.Decode<mirror::Method>(interface_method_jobj));
// Build argument array possibly triggering GC.
soa.Self()->AssertThreadSuspensionIsAllowable();
- jobjectArray args_jobj = nullptr;
+ auto h_args = hs.NewHandle<mirror::ObjectArray<mirror::Object>>(nullptr);
const JValue zero;
- uint32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion();
+ Runtime* runtime = Runtime::Current();
+ uint32_t target_sdk_version = runtime->GetTargetSdkVersion();
// Do not create empty arrays unless needed to maintain Dalvik bug compatibility.
if (args.size() > 0 || IsSdkVersionSetAndAtMost(target_sdk_version, SdkVersion::kL)) {
- args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, nullptr);
- if (args_jobj == nullptr) {
+ h_args.Assign(mirror::ObjectArray<mirror::Object>::Alloc(
+ soa.Self(), GetClassRoot<mirror::ObjectArray<mirror::Object>>(), args.size()));
+ if (h_args == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
return zero;
}
for (size_t i = 0; i < args.size(); ++i) {
+ ObjPtr<mirror::Object> value;
if (shorty[i + 1] == 'L') {
- jobject val = args[i].l;
- soa.Env()->SetObjectArrayElement(args_jobj, i, val);
+ value = soa.Decode<mirror::Object>(args[i].l);
} else {
JValue jv;
jv.SetJ(args[i].j);
- ObjPtr<mirror::Object> val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
- if (val == nullptr) {
+ value = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
+ if (value == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
return zero;
}
- soa.Decode<mirror::ObjectArray<mirror::Object>>(args_jobj)->Set<false>(i, val);
}
+ // We do not support `Proxy.invoke()` in a transaction.
+ h_args->SetWithoutChecks</*kActiveTransaction=*/ false>(i, value);
}
}
// Call Proxy.invoke(Proxy proxy, Method method, Object[] args).
- jvalue invocation_args[3];
- invocation_args[0].l = rcvr_jobj;
- invocation_args[1].l = interface_method_jobj;
- invocation_args[2].l = args_jobj;
- jobject result =
- soa.Env()->CallStaticObjectMethodA(WellKnownClasses::java_lang_reflect_Proxy,
- WellKnownClasses::java_lang_reflect_Proxy_invoke,
- invocation_args);
+ Handle<mirror::Object> h_result = hs.NewHandle(
+ WellKnownClasses::java_lang_reflect_Proxy_invoke->InvokeStatic<'L', 'L', 'L', 'L'>(
+ soa.Self(), h_receiver.Get(), h_interface_method.Get(), h_args.Get()));
// Unbox result and handle error conditions.
if (LIKELY(!soa.Self()->IsExceptionPending())) {
- if (shorty[0] == 'V' || (shorty[0] == 'L' && result == nullptr)) {
+ if (shorty[0] == 'V' || (shorty[0] == 'L' && h_result == nullptr)) {
// Do nothing.
return zero;
} else {
- ArtMethod* interface_method =
- soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
- // This can cause thread suspension.
- ObjPtr<mirror::Class> result_type = interface_method->ResolveReturnType();
- ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
+ ObjPtr<mirror::Class> result_type;
+ if (shorty[0] == 'L') {
+ // This can cause thread suspension.
+ result_type = h_interface_method->GetArtMethod()->ResolveReturnType();
+ if (result_type == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ return zero;
+ }
+ } else {
+ result_type = runtime->GetClassLinker()->LookupPrimitiveClass(shorty[0]);
+ DCHECK(result_type != nullptr);
+ }
JValue result_unboxed;
- if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) {
+ if (!UnboxPrimitiveForResult(h_result.Get(), result_type, &result_unboxed)) {
DCHECK(soa.Self()->IsExceptionPending());
return zero;
}
@@ -200,9 +211,8 @@
static inline ArtMethod* DoGetCalleeSaveMethodCaller(ArtMethod* outer_method,
uintptr_t caller_pc,
bool do_caller_check)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtMethod* caller = outer_method;
- if (LIKELY(caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()))) {
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* caller = outer_method;
if (outer_method != nullptr) {
const OatQuickMethodHeader* current_code = outer_method->GetOatQuickMethodHeader(caller_pc);
DCHECK(current_code != nullptr);
@@ -225,14 +235,7 @@
visitor.WalkStack();
CHECK_EQ(caller, visitor.caller);
}
- } else {
- // We're instrumenting, just use the StackVisitor which knows how to
- // handle instrumented frames.
- NthCallerVisitor visitor(Thread::Current(), 1, true);
- visitor.WalkStack();
- caller = visitor.caller;
- }
- return caller;
+ return caller;
}
ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, CalleeSaveType type, bool do_caller_check)
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b6fc69..ae80352 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -77,7 +77,6 @@
REQUIRES(!Roles::uninterruptible_);
-template <bool kAccessCheck>
ALWAYS_INLINE inline ObjPtr<mirror::Class> CheckArrayAlloc(dex::TypeIndex type_idx,
int32_t component_count,
ArtMethod* method,
@@ -89,7 +88,7 @@
// it cannot be resolved, throw an error. If it can, use it to create an array.
// When verification/compiler hasn't been able to verify access, optionally perform an access
// check.
-template <bool kAccessCheck, bool kInstrumented = true>
+template <bool kInstrumented = true>
ALWAYS_INLINE inline ObjPtr<mirror::Array> AllocArrayFromCode(dex::TypeIndex type_idx,
int32_t component_count,
ArtMethod* method,
@@ -143,11 +142,13 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
-template<InvokeType type, bool access_check>
-inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
- ObjPtr<mirror::Object>* this_object,
- ArtMethod* referrer,
- Thread* self)
+template<InvokeType type>
+inline ArtMethod* FindMethodToCall(Thread* self,
+ ArtMethod* referrer,
+ ObjPtr<mirror::Object>* this_object,
+ const Instruction& inst,
+ bool only_lookup_tls_cache,
+ /*out*/ bool* string_init)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
@@ -203,10 +204,6 @@
ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type)
REQUIRES_SHARED(Locks::mutator_lock_);
-// Returns whether we need to do class initialization check before invoking the method.
-// The caller is responsible for performing that check.
-bool NeedsClinitCheckBeforeCall(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
-
// Returns the synchronization object for a native method for a GenericJni frame
// we have just created or are about to exit. The synchronization object is
// the class object for static methods and the `this` object otherwise.
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index c78b604..e606c21 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -75,7 +75,7 @@
// These calls do not have an explicit class initialization check, so do the check now.
// (When going through the stub or GenericJNI, the check was already done.)
- DCHECK(NeedsClinitCheckBeforeCall(target_method));
+ DCHECK(target_method->NeedsClinitCheckBeforeCall());
ObjPtr<mirror::Class> declaring_class = target_method->GetDeclaringClass();
if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
StackHandleScope<1> hs(self);
@@ -87,11 +87,11 @@
}
// Replace the runtime method on the stack with the target method.
- DCHECK(!self->GetManagedStack()->GetTopQuickFrameTag());
+ DCHECK(!self->GetManagedStack()->GetTopQuickFrameGenericJniTag());
ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged();
DCHECK(*sp == Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
*sp = target_method;
- self->SetTopOfStackTagged(sp); // Fake GenericJNI frame.
+ self->SetTopOfStackGenericJniTagged(sp); // Fake GenericJNI frame.
// Continue with the target method.
method = target_method;
diff --git a/runtime/entrypoints/math_entrypoints_test.cc b/runtime/entrypoints/math_entrypoints_test.cc
index f70a2da..fe61f5d 100644
--- a/runtime/entrypoints/math_entrypoints_test.cc
+++ b/runtime/entrypoints/math_entrypoints_test.cc
@@ -18,11 +18,11 @@
#include <limits>
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
namespace art {
-class MathEntrypointsTest : public CommonRuntimeTest {};
+class MathEntrypointsTest : public CommonArtTest {};
TEST_F(MathEntrypointsTest, DoubleToLong) {
EXPECT_EQ(std::numeric_limits<int64_t>::max(), art_d2l(1.85e19));
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index f8856d8..cb3caac 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -122,6 +122,7 @@
extern "C" void art_jni_monitored_method_start();
extern "C" void art_jni_method_end();
extern "C" void art_jni_monitored_method_end();
+extern "C" void art_jni_method_entry_hook();
// JNI lock/unlock entrypoints. Note: Custom calling convention.
extern "C" void art_jni_lock_object(art::mirror::Object*);
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 939feee..ea07788 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -79,6 +79,7 @@
qpoints->SetQuickGenericJniTrampoline(art_quick_generic_jni_trampoline);
qpoints->SetJniDecodeReferenceResult(JniDecodeReferenceResult);
qpoints->SetJniReadBarrier(art_jni_read_barrier);
+ qpoints->SetJniMethodEntryHook(art_jni_method_entry_hook);
// Locks
if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index d06dbcb..277bc7b 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -25,7 +25,10 @@
namespace art {
-NO_RETURN static void artDeoptimizeImpl(Thread* self, DeoptimizationKind kind, bool single_frame)
+NO_RETURN static void artDeoptimizeImpl(Thread* self,
+ DeoptimizationKind kind,
+ bool single_frame,
+ bool skip_method_exit_callbacks)
REQUIRES_SHARED(Locks::mutator_lock_) {
Runtime::Current()->IncrementDeoptimizationCount(kind);
if (VLOG_IS_ON(deopt)) {
@@ -43,13 +46,12 @@
if (single_frame) {
exception_handler.DeoptimizeSingleFrame(kind);
} else {
- exception_handler.DeoptimizeStack();
+ exception_handler.DeoptimizeStack(skip_method_exit_callbacks);
}
- uintptr_t return_pc = exception_handler.UpdateInstrumentationStack();
if (exception_handler.IsFullFragmentDone()) {
exception_handler.DoLongJump(true);
} else {
- exception_handler.DeoptimizePartialFragmentFixup(return_pc);
+ exception_handler.DeoptimizePartialFragmentFixup();
// We cannot smash the caller-saves, as we need the ArtMethod in a parameter register that would
// be caller-saved. This has the downside that we cannot track incorrect register usage down the
// line.
@@ -57,9 +59,10 @@
}
}
-extern "C" NO_RETURN void artDeoptimize(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- artDeoptimizeImpl(self, DeoptimizationKind::kFullFrame, false);
+ artDeoptimizeImpl(self, DeoptimizationKind::kFullFrame, false, skip_method_exit_callbacks);
}
// This is called directly from compiled code by an HDeoptimize.
@@ -74,7 +77,9 @@
self->GetException(),
/* from_code= */ true,
DeoptimizationMethodType::kDefault);
- artDeoptimizeImpl(self, kind, true);
+ // Deopting from compiled code, so method exit haven't run yet. Don't skip method exit callbacks
+ // if required.
+ artDeoptimizeImpl(self, kind, true, /* skip_method_exit_callbacks= */ false);
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 60a5875..76bee21 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -23,6 +23,7 @@
#include "dex/dex_file_types.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/heap.h"
+#include "jvalue-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 7af1a0b..0e73c63 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -67,6 +67,7 @@
// JNI entrypoints when monitoring entry/exit.
extern "C" void artJniMonitoredMethodStart(Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_);
extern "C" void artJniMonitoredMethodEnd(Thread* self) SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
+extern "C" void artJniMethodEntryHook(Thread* self);
// StringAppend pattern entrypoint.
extern "C" mirror::String* artStringBuilderAppend(uint32_t format,
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index dffaa4b..aa3360e 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -78,6 +78,7 @@
V(JniLockObject, void, mirror::Object*) \
V(JniUnlockObject, void, mirror::Object*) \
V(QuickGenericJniTrampoline, void, ArtMethod*) \
+ V(JniMethodEntryHook, void) \
\
V(LockObject, void, mirror::Object*) \
V(UnlockObject, void, mirror::Object*) \
@@ -150,6 +151,7 @@
\
V(NewEmptyString, void, void) \
V(NewStringFromBytes_B, void, void) \
+ V(NewStringFromBytes_BB, void, void) \
V(NewStringFromBytes_BI, void, void) \
V(NewStringFromBytes_BII, void, void) \
V(NewStringFromBytes_BIII, void, void) \
@@ -164,6 +166,7 @@
V(NewStringFromString, void, void) \
V(NewStringFromStringBuffer, void, void) \
V(NewStringFromStringBuilder, void, void) \
+ V(NewStringFromUtf16Bytes_BII, void, void) \
\
V(StringBuilderAppend, void*, uint32_t) \
\
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index c6861c1..e2fc232 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -32,7 +32,7 @@
inline ArtField* FindFieldFast(uint32_t field_idx,
ArtMethod* referrer,
FindFieldType type,
- size_t expected_size)
+ bool should_resolve_type = false)
REQUIRES(!Roles::uninterruptible_)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
@@ -41,7 +41,6 @@
return nullptr;
}
// Check for incompatible class change.
- const bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0;
const bool is_set = (type & FindFieldFlags::WriteBit) != 0;
const bool is_static = (type & FindFieldFlags::StaticBit) != 0;
if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
@@ -63,8 +62,7 @@
// Illegal access.
return nullptr;
}
- if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
- resolved_field->FieldSize() != expected_size)) {
+ if (should_resolve_type && resolved_field->LookupResolvedType() == nullptr) {
return nullptr;
}
return resolved_field;
@@ -73,17 +71,17 @@
// Helper function to do a null check after trying to resolve the field. Not for statics since obj
// does not exist there. There is a suspend check, object is a double pointer to update the value
// in the caller in case it moves.
-template<FindFieldType type, bool kAccessCheck>
+template<FindFieldType type>
ALWAYS_INLINE static inline ArtField* FindInstanceField(uint32_t field_idx,
ArtMethod* referrer,
Thread* self,
- size_t size,
- mirror::Object** obj)
+ mirror::Object** obj,
+ bool should_resolve_type = false)
REQUIRES(!Roles::uninterruptible_)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(obj));
- ArtField* field = FindFieldFromCode<type, kAccessCheck>(field_idx, referrer, self, size);
+ ArtField* field = FindFieldFromCode<type>(field_idx, referrer, self, should_resolve_type);
if (LIKELY(field != nullptr) && UNLIKELY(h == nullptr)) {
ThrowNullPointerExceptionForFieldAccess(field, referrer, (type & FindFieldFlags::ReadBit) != 0);
return nullptr;
@@ -116,13 +114,12 @@
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Static ## PrimitiveOrObject ## Read, \
- sizeof(PrimitiveType)); \
+ field_idx, referrer, Static ## PrimitiveOrObject ## Read); \
if (LIKELY(field != nullptr)) { \
return field->Get ## Kind (field->GetDeclaringClass())Ptr; /* NOLINT */ \
} \
- field = FindFieldFromCode<Static ## PrimitiveOrObject ## Read, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType)); \
+ field = FindFieldFromCode<Static ## PrimitiveOrObject ## Read>( \
+ field_idx, referrer, self); \
if (LIKELY(field != nullptr)) { \
return field->Get ## Kind (field->GetDeclaringClass())Ptr; /* NOLINT */ \
} \
@@ -137,13 +134,12 @@
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Instance ## PrimitiveOrObject ## Read, \
- sizeof(PrimitiveType)); \
+ field_idx, referrer, Instance ## PrimitiveOrObject ## Read); \
if (LIKELY(field != nullptr) && obj != nullptr) { \
return field->Get ## Kind (obj)Ptr; /* NOLINT */ \
} \
- field = FindInstanceField<Instance ## PrimitiveOrObject ## Read, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType), &obj); \
+ field = FindInstanceField<Instance ## PrimitiveOrObject ## Read>( \
+ field_idx, referrer, self, &obj); \
if (LIKELY(field != nullptr)) { \
return field->Get ## Kind (obj)Ptr; /* NOLINT */ \
} \
@@ -157,33 +153,30 @@
Thread* self) \
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
+ bool should_resolve_type = (IsObject) && new_value != 0; \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Static ## PrimitiveOrObject ## Write, \
- sizeof(PrimitiveType)); \
+ field_idx, \
+ referrer, \
+ Static ## PrimitiveOrObject ## Write, \
+ should_resolve_type); \
if (UNLIKELY(field == nullptr)) { \
if (IsObject) { \
StackHandleScope<1> hs(self); \
HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper( \
reinterpret_cast<mirror::Object**>(&new_value))); \
- field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType)); \
+ field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write>( \
+ field_idx, \
+ referrer, \
+ self, \
+ should_resolve_type); \
} else { \
- field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType)); \
+ field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write>( \
+ field_idx, referrer, self); \
} \
if (UNLIKELY(field == nullptr)) { \
return -1; \
} \
} \
- if (!referrer->SkipAccessChecks() && IsObject && new_value != 0) { \
- StackArtFieldHandleScope<1> rhs(self); \
- ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(field)); \
- if (field->ResolveType().IsNull()) { \
- self->AssertPendingException(); \
- return -1; \
- } \
- field = field_handle.Get(); \
- } \
field->Set ## Kind <false>(field->GetDeclaringClass(), new_value); \
return 0; \
} \
@@ -195,43 +188,31 @@
Thread* self) \
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
+ bool should_resolve_type = (IsObject) && new_value != 0; \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Instance ## PrimitiveOrObject ## Write, \
- sizeof(PrimitiveType)); \
+ field_idx, \
+ referrer, \
+ Instance ## PrimitiveOrObject ## Write, \
+ should_resolve_type); \
if (UNLIKELY(field == nullptr || obj == nullptr)) { \
if (IsObject) { \
StackHandleScope<1> hs(self); \
HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper( \
reinterpret_cast<mirror::Object**>(&new_value))); \
- field = \
- FindInstanceField<Instance ## PrimitiveOrObject ## Write, true>( \
- field_idx, \
- referrer, \
- self, \
- sizeof(PrimitiveType), \
- &obj); \
+ field = FindInstanceField<Instance ## PrimitiveOrObject ## Write>( \
+ field_idx, \
+ referrer, \
+ self, \
+ &obj, \
+ should_resolve_type); \
} else { \
- field = \
- FindInstanceField<Instance ## PrimitiveOrObject ## Write, true>( \
- field_idx, \
- referrer, \
- self, \
- sizeof(PrimitiveType), \
- &obj); \
+ field = FindInstanceField<Instance ## PrimitiveOrObject ## Write>( \
+ field_idx, referrer, self, &obj); \
} \
if (UNLIKELY(field == nullptr)) { \
return -1; \
} \
} \
- if (!referrer->SkipAccessChecks() && IsObject && new_value != 0) { \
- StackArtFieldHandleScope<1> rhs(self); \
- ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(field)); \
- if (field->ResolveType().IsNull()) { \
- self->AssertPendingException(); \
- return -1; \
- } \
- field = field_handle.Get(); \
- } \
field->Set ## Kind<false>(obj, new_value); \
return 0; \
} \
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 3083b79..6f69001 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -38,8 +38,13 @@
namespace art {
-static_assert(sizeof(IRTSegmentState) == sizeof(uint32_t), "IRTSegmentState size unexpected");
-static_assert(std::is_trivial<IRTSegmentState>::value, "IRTSegmentState not trivial");
+extern "C" int artMethodExitHook(Thread* self,
+ ArtMethod* method,
+ uint64_t* gpr_result,
+ uint64_t* fpr_result);
+
+static_assert(sizeof(jni::LRTSegmentState) == sizeof(uint32_t), "LRTSegmentState size unexpected");
+static_assert(std::is_trivial<jni::LRTSegmentState>::value, "LRTSegmentState not trivial");
extern "C" void artJniReadBarrier(ArtMethod* method) {
DCHECK(gUseReadBarrier);
@@ -77,7 +82,7 @@
env->CheckNoHeldMonitors();
}
env->SetLocalSegmentState(env->GetLocalRefCookie());
- env->SetLocalRefCookie(bit_cast<IRTSegmentState>(saved_local_ref_cookie));
+ env->SetLocalRefCookie(bit_cast<jni::LRTSegmentState>(saved_local_ref_cookie));
}
// TODO: annotalysis disabled as monitor semantics are maintained in Java code.
@@ -174,11 +179,11 @@
artJniUnlockObject(lock.Ptr(), self);
}
char return_shorty_char = called->GetShorty()[0];
+ uint64_t ret;
if (return_shorty_char == 'L') {
- uint64_t ret = reinterpret_cast<uint64_t>(
+ ret = reinterpret_cast<uint64_t>(
UNLIKELY(self->IsExceptionPending()) ? nullptr : JniDecodeReferenceResult(result.l, self));
PopLocalReferences(saved_local_ref_cookie, self);
- return ret;
} else {
if (LIKELY(!critical_native)) {
PopLocalReferences(saved_local_ref_cookie, self);
@@ -188,32 +193,43 @@
if (kRuntimeISA == InstructionSet::kX86) {
// Convert back the result to float.
double d = bit_cast<double, uint64_t>(result_f);
- return bit_cast<uint32_t, float>(static_cast<float>(d));
+ ret = bit_cast<uint32_t, float>(static_cast<float>(d));
} else {
- return result_f;
+ ret = result_f;
}
}
+ break;
case 'D':
- return result_f;
+ ret = result_f;
+ break;
case 'Z':
- return result.z;
+ ret = result.z;
+ break;
case 'B':
- return result.b;
+ ret = result.b;
+ break;
case 'C':
- return result.c;
+ ret = result.c;
+ break;
case 'S':
- return result.s;
+ ret = result.s;
+ break;
case 'I':
- return result.i;
+ ret = result.i;
+ break;
case 'J':
- return result.j;
+ ret = result.j;
+ break;
case 'V':
- return 0;
+ ret = 0;
+ break;
default:
LOG(FATAL) << "Unexpected return shorty character " << return_shorty_char;
UNREACHABLE();
}
}
+
+ return ret;
}
extern "C" void artJniMonitoredMethodStart(Thread* self) {
diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
index 93422cf..5dca58a 100644
--- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
@@ -21,16 +21,46 @@
namespace art {
+extern "C" void artDeoptimizeIfNeeded(Thread* self, uintptr_t result, bool is_ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ DCHECK(!self->IsExceptionPending());
+
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ DCHECK(sp != nullptr && (*sp)->IsRuntimeMethod());
+
+ DeoptimizationMethodType type = instr->GetDeoptimizationMethodType(*sp);
+ JValue jvalue;
+ jvalue.SetJ(result);
+ instr->DeoptimizeIfNeeded(self, sp, type, jvalue, is_ref);
+}
+
extern "C" void artTestSuspendFromCode(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when there is a pending checkpoint or suspend request.
ScopedQuickEntrypointChecks sqec(self);
self->CheckSuspend();
+
+ // We could have other dex instructions at the same dex pc as suspend and we need to execute
+ // those instructions. So we should start executing from the current dex pc.
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ JValue result;
+ result.SetJ(0);
+ Runtime::Current()->GetInstrumentation()->DeoptimizeIfNeeded(
+ self, sp, DeoptimizationMethodType::kKeepDexPc, result, /* is_ref= */ false);
}
extern "C" void artImplicitSuspendFromCode(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when there is a pending checkpoint or suspend request.
ScopedQuickEntrypointChecks sqec(self);
self->CheckSuspend(/*implicit=*/ true);
+
+ // We could have other dex instructions at the same dex pc as suspend and we need to execute
+ // those instructions. So we should start executing from the current dex pc.
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ JValue result;
+ result.SetJ(0);
+ Runtime::Current()->GetInstrumentation()->DeoptimizeIfNeeded(
+ self, sp, DeoptimizationMethodType::kKeepDexPc, result, /* is_ref= */ false);
}
extern "C" void artCompileOptimized(ArtMethod* method, Thread* self)
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 4a08f6f..f0c5953 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -61,7 +61,7 @@
namespace art {
extern "C" NO_RETURN void artDeoptimizeFromCompiledCode(DeoptimizationKind kind, Thread* self);
-extern "C" NO_RETURN void artDeoptimize(Thread* self);
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks);
// Visits the arguments as saved to the stack by a CalleeSaveType::kRefAndArgs callee save frame.
class QuickArgumentVisitor {
@@ -224,13 +224,8 @@
#endif
public:
- // Special handling for proxy methods. Proxy methods are instance methods so the
- // 'this' object is the 1st argument. They also have the same frame layout as the
- // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the
- // 1st GPR.
- static StackReference<mirror::Object>* GetProxyThisObjectReference(ArtMethod** sp)
+ static StackReference<mirror::Object>* GetThisObjectReference(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK((*sp)->IsProxyMethod());
CHECK_GT(kNumQuickGprArgs, 0u);
constexpr uint32_t kThisGprIndex = 0u; // 'this' is in the 1st GPR.
size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
@@ -529,7 +524,8 @@
// allows to use the QuickArgumentVisitor constants without moving all the code in its own module.
extern "C" mirror::Object* artQuickGetProxyThisObject(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return QuickArgumentVisitor::GetProxyThisObjectReference(sp)->AsMirrorPtr();
+ DCHECK((*sp)->IsProxyMethod());
+ return QuickArgumentVisitor::GetThisObjectReference(sp)->AsMirrorPtr();
}
// Visits arguments on the stack placing them into the shadow frame.
@@ -647,6 +643,7 @@
method_type);
}
+NO_STACK_PROTECTOR
extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Ensure we don't get thread suspension until the object arguments are safely in the shadow
@@ -654,100 +651,75 @@
ScopedQuickEntrypointChecks sqec(self);
if (UNLIKELY(!method->IsInvokable())) {
- method->ThrowInvocationTimeError();
+ method->ThrowInvocationTimeError(
+ method->IsStatic()
+ ? nullptr
+ : QuickArgumentVisitor::GetThisObjectReference(sp)->AsMirrorPtr());
return 0;
}
- JValue tmp_value;
- ShadowFrame* deopt_frame = self->PopStackedShadowFrame(
- StackedShadowFrameType::kDeoptimizationShadowFrame, false);
- ManagedStack fragment;
-
DCHECK(!method->IsNative()) << method->PrettyMethod();
- uint32_t shorty_len = 0;
- ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
- DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod();
- CodeItemDataAccessor accessor(non_proxy_method->DexInstructionData());
- const char* shorty = non_proxy_method->GetShorty(&shorty_len);
JValue result;
- bool force_frame_pop = false;
+ ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod();
+ uint32_t shorty_len = 0;
+ const char* shorty = non_proxy_method->GetShorty(&shorty_len);
+
+ ManagedStack fragment;
+ ShadowFrame* deopt_frame = self->MaybePopDeoptimizedStackedShadowFrame();
if (UNLIKELY(deopt_frame != nullptr)) {
HandleDeoptimization(&result, method, deopt_frame, &fragment);
} else {
+ CodeItemDataAccessor accessor(non_proxy_method->DexInstructionData());
const char* old_cause = self->StartAssertNoThreadSuspension(
"Building interpreter shadow frame");
uint16_t num_regs = accessor.RegistersSize();
// No last shadow coming from quick.
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, /* link= */ nullptr, method, /* dex_pc= */ 0);
+ CREATE_SHADOW_FRAME(num_regs, method, /* dex_pc= */ 0);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
size_t first_arg_reg = accessor.RegistersSize() - accessor.InsSize();
BuildQuickShadowFrameVisitor shadow_frame_builder(sp, method->IsStatic(), shorty, shorty_len,
shadow_frame, first_arg_reg);
shadow_frame_builder.VisitArguments();
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ // Potentially run <clinit> before pushing the shadow frame. We do not want
+ // to have the called method on the stack if there is an exception.
+ if (!EnsureInitialized(self, shadow_frame)) {
+ DCHECK(self->IsExceptionPending());
+ return 0;
+ }
+
// Push a transition back into managed code onto the linked list in thread.
self->PushManagedStackFragment(&fragment);
self->PushShadowFrame(shadow_frame);
- self->EndAssertNoThreadSuspension(old_cause);
-
- if (NeedsClinitCheckBeforeCall(method)) {
- ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- // Ensure static method's class is initialized.
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
- DCHECK(Thread::Current()->IsExceptionPending()) << method->PrettyMethod();
- self->PopManagedStackFragment(fragment);
- return 0;
- }
- }
- }
-
result = interpreter::EnterInterpreterFromEntryPoint(self, accessor, shadow_frame);
- force_frame_pop = shadow_frame->GetForcePopFrame();
}
// Pop transition.
self->PopManagedStackFragment(fragment);
- // Request a stack deoptimization if needed
- ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
- uintptr_t caller_pc = QuickArgumentVisitor::GetCallingPc(sp);
- // If caller_pc is the instrumentation exit stub, the stub will check to see if deoptimization
- // should be done and it knows the real return pc. NB If the upcall is null we don't need to do
- // anything. This can happen during shutdown or early startup.
- if (UNLIKELY(
- caller != nullptr &&
- caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) &&
- (self->IsForceInterpreter() || Dbg::IsForcedInterpreterNeededForUpcall(self, caller)))) {
- if (!Runtime::Current()->IsAsyncDeoptimizeable(caller_pc)) {
- LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
- << caller->PrettyMethod();
- } else {
- VLOG(deopt) << "Forcing deoptimization on return from method " << method->PrettyMethod()
- << " to " << caller->PrettyMethod()
- << (force_frame_pop ? " for frame-pop" : "");
- DCHECK_IMPLIES(force_frame_pop, result.GetJ() == 0)
- << "Force frame pop should have no result.";
- if (force_frame_pop && self->GetException() != nullptr) {
- LOG(WARNING) << "Suppressing exception for instruction-retry: "
- << self->GetException()->Dump();
- }
- // Push the context of the deoptimization stack so we can restore the return value and the
- // exception before executing the deoptimized frames.
- self->PushDeoptimizationContext(
- result,
- shorty[0] == 'L' || shorty[0] == '[', /* class or array */
- force_frame_pop ? nullptr : self->GetException(),
- /* from_code= */ false,
- DeoptimizationMethodType::kDefault);
+ // Check if caller needs to be deoptimized for instrumentation reasons.
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instr->ShouldDeoptimizeCaller(self, sp))) {
+ ArtMethod* caller = QuickArgumentVisitor::GetOuterMethod(sp);
+ uintptr_t caller_pc = QuickArgumentVisitor::GetCallingPc(sp);
+ DCHECK(Runtime::Current()->IsAsyncDeoptimizeable(caller, caller_pc));
+ DCHECK(caller != nullptr);
+ DCHECK(self->GetException() != Thread::GetDeoptimizationException());
+ // Push the context of the deoptimization stack so we can restore the return value and the
+ // exception before executing the deoptimized frames.
+ self->PushDeoptimizationContext(result,
+ shorty[0] == 'L' || shorty[0] == '[', /* class or array */
+ self->GetException(),
+ /* from_code= */ false,
+ DeoptimizationMethodType::kDefault);
- // Set special exception to cause deoptimization.
- self->SetException(Thread::GetDeoptimizationException());
- }
+ // Set special exception to cause deoptimization.
+ self->SetException(Thread::GetDeoptimizationException());
}
// No need to restore the args since the method has already been run by the interpreter.
@@ -862,7 +834,6 @@
instr->MethodEnterEvent(soa.Self(), proxy_method);
if (soa.Self()->IsExceptionPending()) {
instr->MethodUnwindEvent(self,
- soa.Decode<mirror::Object>(rcvr_jobj),
proxy_method,
0);
return 0;
@@ -872,7 +843,6 @@
if (soa.Self()->IsExceptionPending()) {
if (instr->HasMethodUnwindListeners()) {
instr->MethodUnwindEvent(self,
- soa.Decode<mirror::Object>(rcvr_jobj),
proxy_method,
0);
}
@@ -1023,99 +993,10 @@
}
}
-extern "C" const void* artInstrumentationMethodEntryFromCode(ArtMethod* method,
- mirror::Object* this_object,
- Thread* self,
- ArtMethod** sp)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- const void* result;
- // Instrumentation changes the stack. Thus, when exiting, the stack cannot be verified, so skip
- // that part.
- ScopedQuickEntrypointChecks sqec(self, kIsDebugBuild, false);
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- DCHECK(!method->IsProxyMethod())
- << "Proxy method " << method->PrettyMethod()
- << " (declaring class: " << method->GetDeclaringClass()->PrettyClass() << ")"
- << " should not hit instrumentation entrypoint.";
- DCHECK(!instrumentation->IsDeoptimized(method));
- // This will get the entry point either from the oat file, the JIT or the appropriate bridge
- // method if none of those can be found.
- result = instrumentation->GetCodeForInvoke(method);
- DCHECK_NE(result, GetQuickInstrumentationEntryPoint()) << method->PrettyMethod();
- bool interpreter_entry = Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result);
- bool is_static = method->IsStatic();
- uint32_t shorty_len;
- const char* shorty =
- method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(&shorty_len);
-
- ScopedObjectAccessUnchecked soa(self);
- RememberForGcArgumentVisitor visitor(sp, is_static, shorty, shorty_len, &soa);
- visitor.VisitArguments();
-
- StackHandleScope<2> hs(self);
- Handle<mirror::Object> h_object(hs.NewHandle(is_static ? nullptr : this_object));
- Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
-
- // Ensure that the called method's class is initialized.
- if (NeedsClinitCheckBeforeCall(method) && !h_class->IsVisiblyInitialized()) {
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
- visitor.FixupReferences();
- DCHECK(self->IsExceptionPending());
- return nullptr;
- }
- }
-
- instrumentation->PushInstrumentationStackFrame(self,
- is_static ? nullptr : h_object.Get(),
- method,
- reinterpret_cast<uintptr_t>(
- QuickArgumentVisitor::GetCallingPcAddr(sp)),
- QuickArgumentVisitor::GetCallingPc(sp),
- interpreter_entry);
-
- visitor.FixupReferences();
- if (UNLIKELY(self->IsExceptionPending())) {
- return nullptr;
- }
- CHECK(result != nullptr) << method->PrettyMethod();
- return result;
-}
-
-extern "C" TwoWordReturn artInstrumentationMethodExitFromCode(Thread* self,
- ArtMethod** sp,
- uint64_t* gpr_result,
- uint64_t* fpr_result)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK_EQ(reinterpret_cast<uintptr_t>(self), reinterpret_cast<uintptr_t>(Thread::Current()));
- CHECK(gpr_result != nullptr);
- CHECK(fpr_result != nullptr);
- // Instrumentation exit stub must not be entered with a pending exception.
- CHECK(!self->IsExceptionPending()) << "Enter instrumentation exit stub with pending exception "
- << self->GetException()->Dump();
- // Compute address of return PC and check that it currently holds 0.
- constexpr size_t return_pc_offset =
- RuntimeCalleeSaveFrame::GetReturnPcOffset(CalleeSaveType::kSaveEverything);
- uintptr_t* return_pc_addr = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) +
- return_pc_offset);
- CHECK_EQ(*return_pc_addr, 0U);
-
- // Pop the frame filling in the return pc. The low half of the return value is 0 when
- // deoptimization shouldn't be performed with the high-half having the return address. When
- // deoptimization should be performed the low half is zero and the high-half the address of the
- // deoptimization entry point.
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- TwoWordReturn return_or_deoptimize_pc = instrumentation->PopInstrumentationStackFrame(
- self, return_pc_addr, gpr_result, fpr_result);
- if (self->IsExceptionPending() || self->ObserveAsyncException()) {
- return GetTwoWordFailureValue();
- }
- return return_or_deoptimize_pc;
-}
-
static std::string DumpInstruction(ArtMethod* method, uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (dex_pc == static_cast<uint32_t>(-1)) {
- CHECK(method == jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt));
+ CHECK(method == WellKnownClasses::java_lang_String_charAt);
return "<native>";
} else {
CodeItemInstructionAccessor accessor = method->DexInstructions();
@@ -1154,12 +1035,6 @@
(reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset));
ArtMethod* outer_method = *caller_sp;
- if (UNLIKELY(caller_pc == reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()))) {
- LOG(FATAL_WITHOUT_ABORT) << "Method: " << outer_method->PrettyMethod()
- << " native pc: " << caller_pc << " Instrumented!";
- return;
- }
-
const OatQuickMethodHeader* current_code = outer_method->GetOatQuickMethodHeader(caller_pc);
CHECK(current_code != nullptr);
CHECK(current_code->IsOptimized());
@@ -1193,7 +1068,7 @@
if (dex_pc == static_cast<uint32_t>(-1)) {
tag = "special ";
CHECK(inline_info.Equals(inline_infos.back()));
- caller = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+ caller = WellKnownClasses::java_lang_String_charAt;
CHECK_EQ(caller->GetDexMethodIndex(), method_index);
} else {
ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
@@ -1371,21 +1246,25 @@
// Static invokes need class initialization check but instance invokes can proceed even if
// the class is erroneous, i.e. in the edge case of escaping instances of erroneous classes.
bool success = true;
- ObjPtr<mirror::Class> called_class = called->GetDeclaringClass();
- if (NeedsClinitCheckBeforeCall(called) && !called_class->IsVisiblyInitialized()) {
+ if (called->StillNeedsClinitCheck()) {
// Ensure that the called method's class is initialized.
StackHandleScope<1> hs(soa.Self());
- HandleWrapperObjPtr<mirror::Class> h_called_class(hs.NewHandleWrapper(&called_class));
+ Handle<mirror::Class> h_called_class = hs.NewHandle(called->GetDeclaringClass());
success = linker->EnsureInitialized(soa.Self(), h_called_class, true, true);
}
if (success) {
+ // When the clinit check is at entry of the AOT/nterp code, we do the clinit check
+ // before doing the suspend check. To ensure the code sees the latest
+ // version of the class (the code doesn't do a read barrier to reduce
+ // size), do a suspend check now.
+ self->CheckSuspend();
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
// Check if we need instrumented code here. Since resolution stubs could suspend, it is
// possible that we instrumented the entry points after we started executing the resolution
// stub.
code = instrumentation->GetMaybeInstrumentedCodeForInvoke(called);
} else {
- DCHECK(called_class->IsErroneous());
+ DCHECK(called->GetDeclaringClass()->IsErroneous());
DCHECK(self->IsExceptionPending());
}
}
@@ -1795,7 +1674,7 @@
// Add space for cookie.
DCHECK_ALIGNED(managed_sp, sizeof(uintptr_t));
- static_assert(sizeof(uintptr_t) >= sizeof(IRTSegmentState));
+ static_assert(sizeof(uintptr_t) >= sizeof(jni::LRTSegmentState));
uint8_t* sp8 = reinterpret_cast<uint8_t*>(managed_sp) - sizeof(uintptr_t);
// Layout stack arguments.
@@ -2091,7 +1970,7 @@
}
// Fix up managed-stack things in Thread. After this we can walk the stack.
- self->SetTopOfStackTagged(managed_sp);
+ self->SetTopOfStackGenericJniTagged(managed_sp);
self->VerifyStack();
@@ -2104,16 +1983,21 @@
// We can set the entrypoint of a native method to generic JNI even when the
// class hasn't been initialized, so we need to do the initialization check
// before invoking the native code.
- if (NeedsClinitCheckBeforeCall(called)) {
- ObjPtr<mirror::Class> declaring_class = called->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- // Ensure static method's class is initialized.
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
- DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod();
- return nullptr; // Report error.
- }
+ if (called->StillNeedsClinitCheck()) {
+ // Ensure static method's class is initialized.
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class = hs.NewHandle(called->GetDeclaringClass());
+ if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+ DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod();
+ return nullptr; // Report error.
+ }
+ }
+
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instr->HasMethodEntryListeners())) {
+ instr->MethodEnterEvent(self, called);
+ if (self->IsExceptionPending()) {
+ return nullptr;
}
}
@@ -2185,75 +2069,13 @@
// anything that requires a mutator lock before that would cause problems as GC may have the
// exclusive mutator lock and may be moving objects, etc.
ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
- DCHECK(self->GetManagedStack()->GetTopQuickFrameTag());
+ DCHECK(self->GetManagedStack()->GetTopQuickFrameGenericJniTag());
uint32_t* sp32 = reinterpret_cast<uint32_t*>(sp);
ArtMethod* called = *sp;
uint32_t cookie = *(sp32 - 1);
return GenericJniMethodEnd(self, cookie, result, result_f, called);
}
-// Fast path method resolution that can't throw exceptions.
-template <InvokeType type>
-inline ArtMethod* FindMethodFast(uint32_t method_idx,
- ObjPtr<mirror::Object> this_object,
- ArtMethod* referrer)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Roles::uninterruptible_) {
- ScopedAssertNoThreadSuspension ants(__FUNCTION__);
- if (UNLIKELY(this_object == nullptr && type != kStatic)) {
- return nullptr;
- }
- ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
- constexpr ClassLinker::ResolveMode resolve_mode = ClassLinker::ResolveMode::kCheckICCEAndIAE;
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- ArtMethod* resolved_method = linker->GetResolvedMethod<type, resolve_mode>(method_idx, referrer);
- if (UNLIKELY(resolved_method == nullptr)) {
- return nullptr;
- }
- if (type == kInterface) { // Most common form of slow path dispatch.
- return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method,
- kRuntimePointerSize);
- }
- if (type == kStatic || type == kDirect) {
- return resolved_method;
- }
-
- if (type == kSuper) {
- // TODO This lookup is rather slow.
- dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
- ObjPtr<mirror::Class> method_reference_class = linker->LookupResolvedType(
- method_type_idx, dex_cache, referrer->GetClassLoader());
- if (method_reference_class == nullptr) {
- // Need to do full type resolution...
- return nullptr;
- }
-
- // If the referring class is in the class hierarchy of the
- // referenced class in the bytecode, we use its super class. Otherwise, we cannot
- // resolve the method.
- if (!method_reference_class->IsAssignableFrom(referring_class)) {
- return nullptr;
- }
-
- if (method_reference_class->IsInterface()) {
- return method_reference_class->FindVirtualMethodForInterfaceSuper(
- resolved_method, kRuntimePointerSize);
- }
-
- ObjPtr<mirror::Class> super_class = referring_class->GetSuperClass();
- if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) {
- // The super class does not have the method.
- return nullptr;
- }
- return super_class->GetVTableEntry(resolved_method->GetMethodIndex(), kRuntimePointerSize);
- }
-
- DCHECK(type == kVirtual);
- return this_object->GetClass()->GetVTableEntry(
- resolved_method->GetMethodIndex(), kRuntimePointerSize);
-}
-
// We use TwoWordReturn to optimize scalar returns. We use the hi value for code, and the lo value
// for the method pointer.
//
@@ -2268,18 +2090,35 @@
ScopedQuickEntrypointChecks sqec(self);
DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
- ArtMethod* method = FindMethodFast<type>(method_idx, this_object, caller_method);
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+ CodeItemInstructionAccessor accessor(caller_method->DexInstructions());
+ DCHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits());
+ const Instruction& instr = accessor.InstructionAt(dex_pc);
+ bool string_init = false;
+ ArtMethod* method = FindMethodToCall<type>(
+ self, caller_method, &this_object, instr, /* only_lookup_tls_cache= */ true, &string_init);
+
if (UNLIKELY(method == nullptr)) {
+ if (self->IsExceptionPending()) {
+ // Return a failure if the first lookup threw an exception.
+ return GetTwoWordFailureValue(); // Failure.
+ }
const DexFile* dex_file = caller_method->GetDexFile();
uint32_t shorty_len;
const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len);
{
- // Remember the args in case a GC happens in FindMethodFromCode.
+ // Remember the args in case a GC happens in FindMethodToCall.
ScopedObjectAccessUnchecked soa(self->GetJniEnv());
RememberForGcArgumentVisitor visitor(sp, type == kStatic, shorty, shorty_len, &soa);
visitor.VisitArguments();
- method = FindMethodFromCode<type, /*access_check=*/true>(
- method_idx, &this_object, caller_method, self);
+
+ method = FindMethodToCall<type>(self,
+ caller_method,
+ &this_object,
+ instr,
+ /* only_lookup_tls_cache= */ false,
+ &string_init);
+
visitor.FixupReferences();
}
@@ -2527,10 +2366,9 @@
const size_t num_vregs = is_range ? inst.VRegA_4rcc() : inst.VRegA_45cc();
const size_t first_arg = 0;
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_vregs, /* link= */ nullptr, resolved_method, dex_pc);
+ CREATE_SHADOW_FRAME(num_vregs, resolved_method, dex_pc);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
- ScopedStackedShadowFramePusher
- frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher frame_pusher(self, shadow_frame);
BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
kMethodIsStatic,
shorty,
@@ -2589,6 +2427,10 @@
// Pop transition record.
self->PopManagedStackFragment(fragment);
+ bool is_ref = (shorty[0] == 'L');
+ Runtime::Current()->GetInstrumentation()->PushDeoptContextIfNeeded(
+ self, DeoptimizationMethodType::kDefault, is_ref, result);
+
return result.GetJ();
}
@@ -2620,10 +2462,9 @@
const size_t first_arg = 0;
const size_t num_vregs = ArtMethod::NumArgRegisters(shorty);
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_vregs, /* link= */ nullptr, caller_method, dex_pc);
+ CREATE_SHADOW_FRAME(num_vregs, caller_method, dex_pc);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
- ScopedStackedShadowFramePusher
- frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher frame_pusher(self, shadow_frame);
BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
kMethodIsStatic,
shorty,
@@ -2647,40 +2488,61 @@
// Pop transition record.
self->PopManagedStackFragment(fragment);
+ bool is_ref = (shorty[0] == 'L');
+ Runtime::Current()->GetInstrumentation()->PushDeoptContextIfNeeded(
+ self, DeoptimizationMethodType::kDefault, is_ref, result);
+
return result.GetJ();
}
-extern "C" void artMethodEntryHook(ArtMethod* method, Thread* self, ArtMethod** sp ATTRIBUTE_UNUSED)
+extern "C" void artJniMethodEntryHook(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ ArtMethod* method = *self->GetManagedStack()->GetTopQuickFrame();
instr->MethodEnterEvent(self, method);
- if (instr->IsDeoptimized(method)) {
- // Instrumentation can request deoptimizing only a particular method (for
- // ex: when there are break points on the method). In such cases deoptimize
- // only this method. FullFrame deoptimizations are handled on method exits.
- artDeoptimizeFromCompiledCode(DeoptimizationKind::kDebugging, self);
+}
+
+extern "C" void artMethodEntryHook(ArtMethod* method, Thread* self, ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ if (instr->HasMethodEntryListeners()) {
+ instr->MethodEnterEvent(self, method);
+ // MethodEnter callback could have requested a deopt for ex: by setting a breakpoint, so
+ // check if we need a deopt here.
+ if (instr->ShouldDeoptimizeCaller(self, sp) || instr->IsDeoptimized(method)) {
+ // Instrumentation can request deoptimizing only a particular method (for ex: when
+ // there are break points on the method). In such cases deoptimize only this method.
+ // FullFrame deoptimizations are handled on method exits.
+ artDeoptimizeFromCompiledCode(DeoptimizationKind::kDebugging, self);
+ }
+ } else {
+ DCHECK(!instr->IsDeoptimized(method));
}
}
-extern "C" int artMethodExitHook(Thread* self,
- ArtMethod* method,
- uint64_t* gpr_result,
- uint64_t* fpr_result)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+extern "C" void artMethodExitHook(Thread* self,
+ ArtMethod** sp,
+ uint64_t* gpr_result,
+ uint64_t* fpr_result,
+ uint32_t frame_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(reinterpret_cast<uintptr_t>(self), reinterpret_cast<uintptr_t>(Thread::Current()));
- CHECK(gpr_result != nullptr);
- CHECK(fpr_result != nullptr);
// Instrumentation exit stub must not be entered with a pending exception.
CHECK(!self->IsExceptionPending())
<< "Enter instrumentation exit stub with pending exception " << self->GetException()->Dump();
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
- DCHECK(instr->AreExitStubsInstalled());
- bool is_ref;
- JValue return_value = instr->GetReturnValue(self, method, &is_ref, gpr_result, fpr_result);
- bool deoptimize = false;
- {
+ DCHECK(instr->RunExitHooks());
+
+ bool is_ref = false;
+ ArtMethod* method = *sp;
+ if (instr->HasMethodExitListeners()) {
StackHandleScope<1> hs(self);
+
+ CHECK(gpr_result != nullptr);
+ CHECK(fpr_result != nullptr);
+
+ JValue return_value = instr->GetReturnValue(method, &is_ref, gpr_result, fpr_result);
MutableHandle<mirror::Object> res(hs.NewHandle<mirror::Object>(nullptr));
if (is_ref) {
// Take a handle to the return value so we won't lose it if we suspend.
@@ -2688,20 +2550,10 @@
}
DCHECK(!method->IsRuntimeMethod());
- // Deoptimize if the caller needs to continue execution in the interpreter. Do nothing if we get
- // back to an upcall.
- NthCallerVisitor visitor(self, 1, /*include_runtime_and_upcalls=*/false);
- visitor.WalkStack(true);
- deoptimize = instr->ShouldDeoptimizeMethod(self, visitor);
-
// If we need a deoptimization MethodExitEvent will be called by the interpreter when it
- // re-executes the return instruction.
- if (!deoptimize) {
- instr->MethodExitEvent(self,
- method,
- /* frame= */ {},
- return_value);
- }
+ // re-executes the return instruction. For native methods we have to process method exit
+ // events here since deoptimization just removes the native frame.
+ instr->MethodExitEvent(self, method, /* frame= */ {}, return_value);
if (is_ref) {
// Restore the return value if it's a reference since it might have moved.
@@ -2711,17 +2563,27 @@
}
if (self->IsExceptionPending() || self->ObserveAsyncException()) {
- return 1;
- }
-
- if (deoptimize) {
- DeoptimizationMethodType deopt_method_type = instr->GetDeoptimizationMethodType(method);
- self->PushDeoptimizationContext(return_value, is_ref, nullptr, false, deopt_method_type);
- artDeoptimize(self);
+ // The exception was thrown from the method exit callback. We should not call method unwind
+ // callbacks for this case.
+ self->QuickDeliverException(/* is_method_exit_exception= */ true);
UNREACHABLE();
}
- return 0;
+ // We should deoptimize here if the caller requires a deoptimization or if the current method
+ // needs a deoptimization. We may need deoptimization for the current method if method exit
+ // hooks requested this frame to be popped. IsForcedInterpreterNeededForUpcall checks for that.
+ const bool deoptimize = instr->ShouldDeoptimizeCaller(self, sp, frame_size) ||
+ Dbg::IsForcedInterpreterNeededForUpcall(self, method);
+ if (deoptimize) {
+ JValue ret_val = instr->GetReturnValue(method, &is_ref, gpr_result, fpr_result);
+ DeoptimizationMethodType deopt_method_type = instr->GetDeoptimizationMethodType(method);
+ self->PushDeoptimizationContext(
+ ret_val, is_ref, self->GetException(), false, deopt_method_type);
+ // Method exit callback has already been run for this method. So tell the deoptimizer to skip
+ // callbacks for this frame.
+ artDeoptimize(self, /*skip_method_exit_callbacks = */ true);
+ UNREACHABLE();
+ }
}
} // namespace art
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
index c4e62e5..951398d 100644
--- a/runtime/entrypoints/runtime_asm_entrypoints.h
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -79,21 +79,9 @@
return reinterpret_cast<const void*>(art_quick_deoptimize);
}
-// Return address of instrumentation entry point used by non-interpreter based tracing.
-extern "C" void art_quick_instrumentation_entry(void*);
-static inline const void* GetQuickInstrumentationEntryPoint() {
- return reinterpret_cast<const void*>(art_quick_instrumentation_entry);
-}
-
// Stub to deoptimize from compiled code.
extern "C" void art_quick_deoptimize_from_compiled_code(DeoptimizationKind);
-// The return_pc of instrumentation exit stub.
-extern "C" void art_quick_instrumentation_exit();
-static inline const void* GetQuickInstrumentationExitPc() {
- return reinterpret_cast<const void*>(art_quick_instrumentation_exit);
-}
-
extern "C" void* art_quick_string_builder_append(uint32_t format);
extern "C" void art_quick_compile_optimized(ArtMethod*, Thread*);
extern "C" void art_quick_method_entry_hook(ArtMethod*, Thread*);
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 240ecbd..be1c58d 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -16,8 +16,8 @@
#include <memory>
+#include "base/common_art_test.h"
#include "base/macros.h"
-#include "common_runtime_test.h"
#include "thread.h"
// This test checks the offsets of values in the thread TLS and entrypoint structures. A failure
@@ -59,7 +59,7 @@
#define EXPECT_OFFSET_DIFF_GT3(type, first_field, second_field, diff, name) \
EXPECT_OFFSET_DIFF_GT(type, first_field, type, second_field, diff, name)
-class EntrypointsOrderTest : public CommonRuntimeTest {
+class EntrypointsOrderTest : public CommonArtTest {
protected:
void CheckThreadOffsets() {
CHECKED(OFFSETOF_MEMBER(Thread, tls32_.state_and_flags) == 0, thread_flags_at_zero);
@@ -98,9 +98,8 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, monitor_enter_object, top_handle_scope, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, top_handle_scope, class_loader_override, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, class_loader_override, long_jump_context, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, long_jump_context, instrumentation_stack, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, instrumentation_stack, stacked_shadow_frame_record,
- sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, long_jump_context,
+ stacked_shadow_frame_record, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stacked_shadow_frame_record,
deoptimization_context_stack, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, deoptimization_context_stack,
@@ -109,9 +108,7 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, name, pthread_self, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, pthread_self, last_no_thread_suspension_cause,
sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, last_no_thread_suspension_cause, checkpoint_function,
- sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_function, active_suspend_barriers,
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, last_no_thread_suspension_cause, active_suspend_barriers,
sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, thread_local_start,
sizeof(Thread::tls_ptr_sized_values::active_suspend_barriers));
@@ -119,7 +116,9 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_limit, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_limit, thread_local_objects, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, jni_entrypoints, sizeof(size_t));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, checkpoint_function, sizeof(size_t));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_function, jni_entrypoints,
+ sizeof(void*));
// Skip across the entrypoints structures.
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, rosalloc_runs, thread_local_alloc_stack_top,
@@ -135,10 +134,14 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_mark_stack, async_exception, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, async_exception, top_reflective_handle_scope,
sizeof(void*));
+ EXPECT_OFFSET_DIFFP(
+ Thread, tlsPtr_, top_reflective_handle_scope, method_trace_buffer, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(
+ Thread, tlsPtr_, method_trace_buffer, method_trace_buffer_index, sizeof(void*));
// The first field after tlsPtr_ is forced to a 16 byte alignment so it might have some space.
auto offset_tlsptr_end = OFFSETOF_MEMBER(Thread, tlsPtr_) +
sizeof(decltype(reinterpret_cast<Thread*>(16)->tlsPtr_));
- CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.top_reflective_handle_scope) ==
+ CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.method_trace_buffer_index) ==
sizeof(void*),
"async_exception last field");
}
@@ -225,7 +228,9 @@
pJniUnlockObject, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniUnlockObject,
pQuickGenericJniTrampoline, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickGenericJniTrampoline, pLockObject, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickGenericJniTrampoline,
+ pJniMethodEntryHook, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEntryHook, pLockObject, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLockObject, pUnlockObject, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pUnlockObject, pCmpgDouble, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgDouble, pCmpgFloat, sizeof(void*));
@@ -299,7 +304,9 @@
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Store, pNewEmptyString, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewEmptyString, pNewStringFromBytes_B, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_B, pNewStringFromBytes_BI,
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_B, pNewStringFromBytes_BB,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BB, pNewStringFromBytes_BI,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BI, pNewStringFromBytes_BII,
sizeof(void*));
@@ -327,7 +334,9 @@
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuffer, pNewStringFromStringBuilder,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuilder, pStringBuilderAppend,
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuilder, pNewStringFromUtf16Bytes_BII,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromUtf16Bytes_BII, pStringBuilderAppend,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pStringBuilderAppend, pUpdateInlineCache,
sizeof(void*));
diff --git a/runtime/exec_utils.cc b/runtime/exec_utils.cc
index 58ee5ce..1021429 100644
--- a/runtime/exec_utils.cc
+++ b/runtime/exec_utils.cc
@@ -16,11 +16,9 @@
#include "exec_utils.h"
-#include <errno.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <sysexits.h>
#include <unistd.h>
#include <ctime>
@@ -70,6 +68,7 @@
// Convert the args to char pointers.
const char* program = arg_vector[0].c_str();
std::vector<char*> args;
+ args.reserve(arg_vector.size() + 1);
for (const auto& arg : arg_vector) {
args.push_back(const_cast<char*>(arg.c_str()));
}
@@ -91,12 +90,6 @@
} else {
execve(program, &args[0], envp);
}
- if (errno == EACCES) {
- // This usually happens when a non-Zygote process invokes dex2oat to generate an in-memory
- // boot image, which is WAI.
- PLOG(DEBUG) << "Failed to execute (" << ToCommandLine(arg_vector) << ")";
- _exit(EX_NOPERM);
- }
// This should be regarded as a crash rather than a normal return.
PLOG(FATAL) << "Failed to execute (" << ToCommandLine(arg_vector) << ")";
UNREACHABLE();
diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc
index a435e3c..e89180b 100644
--- a/runtime/exec_utils_test.cc
+++ b/runtime/exec_utils_test.cc
@@ -17,7 +17,6 @@
#include "exec_utils.h"
#include <sys/utsname.h>
-#include <sysexits.h>
#include <csignal>
#include <cstring>
@@ -128,15 +127,6 @@
EXPECT_FALSE(error_msg.empty());
}
-TEST_P(ExecUtilsTest, ExecPermissionDenied) {
- std::vector<std::string> command;
- command.push_back("/dev/null");
- std::string error_msg;
- ExecResult result = exec_utils_->ExecAndReturnResult(command, /*timeout_sec=*/-1, &error_msg);
- EXPECT_EQ(result.status, ExecResult::kExited);
- EXPECT_EQ(result.exit_code, EX_NOPERM);
-}
-
TEST_P(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) {
static constexpr const char* kModifiedVariable = "EXEC_SHOULD_NOT_EXPORT_THIS";
static constexpr int kOverwrite = 1;
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index c6940fa..dd28f36 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -16,16 +16,17 @@
#include "fault_handler.h"
+#include <atomic>
#include <string.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
#include "art_method-inl.h"
#include "base/logging.h" // For VLOG
+#include "base/membarrier.h"
#include "base/safe_copy.h"
#include "base/stl_util.h"
#include "dex/dex_file_types.h"
-#include "gc/space/bump_pointer_space.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "mirror/class.h"
@@ -52,103 +53,10 @@
return fault_manager.HandleFault(sig, info, context);
}
-#if defined(__linux__)
-
-// Change to verify the safe implementations against the original ones.
-constexpr bool kVerifySafeImpls = false;
-
-// Provide implementations of ArtMethod::GetDeclaringClass and VerifyClassClass that use SafeCopy
-// to safely dereference pointers which are potentially garbage.
-// Only available on Linux due to availability of SafeCopy.
-
-static mirror::Class* SafeGetDeclaringClass(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (gUseUserfaultfd) {
- // Avoid SafeCopy on userfaultfd updated memory ranges as kernel-space
- // userfaults are not allowed, which can otherwise happen if compaction is
- // simultaneously going on.
- Runtime* runtime = Runtime::Current();
- DCHECK_NE(runtime->GetHeap()->MarkCompactCollector(), nullptr);
- GcVisitedArenaPool* pool = static_cast<GcVisitedArenaPool*>(runtime->GetLinearAllocArenaPool());
- if (pool->Contains(method)) {
- return method->GetDeclaringClassUnchecked<kWithoutReadBarrier>().Ptr();
- }
- }
-
- char* method_declaring_class =
- reinterpret_cast<char*>(method) + ArtMethod::DeclaringClassOffset().SizeValue();
- // ArtMethod::declaring_class_ is a GcRoot<mirror::Class>.
- // Read it out into as a CompressedReference directly for simplicity's sake.
- mirror::CompressedReference<mirror::Class> cls;
- ssize_t rc = SafeCopy(&cls, method_declaring_class, sizeof(cls));
- CHECK_NE(-1, rc);
-
- if (kVerifySafeImpls) {
- ObjPtr<mirror::Class> actual_class = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
- CHECK_EQ(actual_class, cls.AsMirrorPtr());
- }
-
- if (rc != sizeof(cls)) {
- return nullptr;
- }
-
- return cls.AsMirrorPtr();
-}
-
-static mirror::Class* SafeGetClass(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (gUseUserfaultfd) {
- // Avoid SafeCopy on userfaultfd updated memory ranges as kernel-space
- // userfaults are not allowed, which can otherwise happen if compaction is
- // simultaneously going on.
- gc::Heap* heap = Runtime::Current()->GetHeap();
- DCHECK_NE(heap->MarkCompactCollector(), nullptr);
- if (heap->GetBumpPointerSpace()->Contains(obj)) {
- return obj->GetClass();
- }
- }
-
- char* obj_cls = reinterpret_cast<char*>(obj) + mirror::Object::ClassOffset().SizeValue();
- mirror::HeapReference<mirror::Class> cls;
- ssize_t rc = SafeCopy(&cls, obj_cls, sizeof(cls));
- CHECK_NE(-1, rc);
-
- if (kVerifySafeImpls) {
- mirror::Class* actual_class = obj->GetClass<kVerifyNone>();
- CHECK_EQ(actual_class, cls.AsMirrorPtr());
- }
-
- if (rc != sizeof(cls)) {
- return nullptr;
- }
-
- return cls.AsMirrorPtr();
-}
-
-static bool SafeVerifyClassClass(mirror::Class* cls) REQUIRES_SHARED(Locks::mutator_lock_) {
- mirror::Class* c_c = SafeGetClass(cls);
- bool result = c_c != nullptr && c_c == SafeGetClass(c_c);
-
- if (kVerifySafeImpls) {
- CHECK_EQ(VerifyClassClass(cls), result);
- }
-
- return result;
-}
-
-#else
-
-static mirror::Class* SafeGetDeclaringClass(ArtMethod* method_obj)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return method_obj->GetDeclaringClassUnchecked<kWithoutReadBarrier>().Ptr();
-}
-
-static bool SafeVerifyClassClass(mirror::Class* cls) REQUIRES_SHARED(Locks::mutator_lock_) {
- return VerifyClassClass(cls);
-}
-#endif
-
-
-FaultManager::FaultManager() : initialized_(false) {
+FaultManager::FaultManager()
+ : generated_code_ranges_lock_("FaultHandler generated code ranges lock",
+ LockLevel::kGenericBottomLock),
+ initialized_(false) {
sigaction(SIGSEGV, nullptr, &oldaction_);
}
@@ -172,6 +80,27 @@
};
AddSpecialSignalHandlerFn(SIGSEGV, &sa);
+
+ // Notify the kernel that we intend to use a specific `membarrier()` command.
+ int result = art::membarrier(MembarrierCommand::kRegisterPrivateExpedited);
+ if (result != 0) {
+ LOG(WARNING) << "FaultHandler: MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED failed: "
+ << errno << " " << strerror(errno);
+ }
+
+ {
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ for (size_t i = 0; i != kNumLocalGeneratedCodeRanges; ++i) {
+ GeneratedCodeRange* next = (i + 1u != kNumLocalGeneratedCodeRanges)
+ ? &generated_code_ranges_storage_[i + 1u]
+ : nullptr;
+ generated_code_ranges_storage_[i].next.store(next, std::memory_order_relaxed);
+ generated_code_ranges_storage_[i].start = nullptr;
+ generated_code_ranges_storage_[i].size = 0u;
+ }
+ free_generated_code_ranges_ = generated_code_ranges_storage_;
+ }
+
initialized_ = true;
}
@@ -189,6 +118,24 @@
// Free all handlers.
STLDeleteElements(&generated_code_handlers_);
STLDeleteElements(&other_handlers_);
+
+ // Delete remaining code ranges if any (such as nterp code or oat code from
+ // oat files that have not been unloaded, including boot image oat files).
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ GeneratedCodeRange* range = generated_code_ranges_.load(std::memory_order_acquire);
+ generated_code_ranges_.store(nullptr, std::memory_order_release);
+ while (range != nullptr) {
+ GeneratedCodeRange* next_range = range->next.load(std::memory_order_relaxed);
+ std::less<GeneratedCodeRange*> less;
+ if (!less(range, generated_code_ranges_storage_) &&
+ less(range, generated_code_ranges_storage_ + kNumLocalGeneratedCodeRanges)) {
+ // Nothing to do - not adding `range` to the `free_generated_code_ranges_` anymore.
+ } else {
+ // Range is not in the `generated_code_ranges_storage_`.
+ delete range;
+ }
+ range = next_range;
+ }
}
}
@@ -243,7 +190,7 @@
raise(SIGSEGV);
#endif
- if (IsInGeneratedCode(info, context, true)) {
+ if (IsInGeneratedCode(info, context)) {
VLOG(signals) << "in generated code, looking for handler";
for (const auto& handler : generated_code_handlers_) {
VLOG(signals) << "invoking Action on handler " << handler;
@@ -290,37 +237,143 @@
LOG(FATAL) << "Attempted to remove non existent handler " << handler;
}
-static bool IsKnownPc(uintptr_t pc, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
- // Check whether the pc is within nterp range.
- if (OatQuickMethodHeader::IsNterpPc(pc)) {
- return true;
+inline FaultManager::GeneratedCodeRange* FaultManager::CreateGeneratedCodeRange(
+ const void* start, size_t size) {
+ GeneratedCodeRange* range = free_generated_code_ranges_;
+ if (range != nullptr) {
+ std::less<GeneratedCodeRange*> less;
+ DCHECK(!less(range, generated_code_ranges_storage_));
+ DCHECK(less(range, generated_code_ranges_storage_ + kNumLocalGeneratedCodeRanges));
+ range->start = start;
+ range->size = size;
+ free_generated_code_ranges_ = range->next.load(std::memory_order_relaxed);
+ range->next.store(nullptr, std::memory_order_relaxed);
+ return range;
+ } else {
+ return new GeneratedCodeRange{nullptr, start, size};
}
-
- // Check whether the pc is in the JIT code cache.
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->GetCodeCache()->ContainsPc(reinterpret_cast<const void*>(pc))) {
- return true;
- }
-
- if (method->IsObsolete()) {
- // Obsolete methods never happen on AOT code.
- return false;
- }
-
- // Note: at this point, we trust it's truly an ArtMethod we found at the bottom of the stack,
- // and we can find its oat file through it.
- const OatDexFile* oat_dex_file = method->GetDeclaringClass()->GetDexFile().GetOatDexFile();
- if (oat_dex_file != nullptr &&
- oat_dex_file->GetOatFile()->Contains(reinterpret_cast<const void*>(pc))) {
- return true;
- }
-
- return false;
}
-// This function is called within the signal handler. It checks that
-// the mutator_lock is held (shared). No annotalysis is done.
-bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool check_dex_pc) {
+inline void FaultManager::FreeGeneratedCodeRange(GeneratedCodeRange* range) {
+ std::less<GeneratedCodeRange*> less;
+ if (!less(range, generated_code_ranges_storage_) &&
+ less(range, generated_code_ranges_storage_ + kNumLocalGeneratedCodeRanges)) {
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ range->start = nullptr;
+ range->size = 0u;
+ range->next.store(free_generated_code_ranges_, std::memory_order_relaxed);
+ free_generated_code_ranges_ = range;
+ } else {
+ // Range is not in the `generated_code_ranges_storage_`.
+ delete range;
+ }
+}
+
+void FaultManager::AddGeneratedCodeRange(const void* start, size_t size) {
+ GeneratedCodeRange* new_range = nullptr;
+ {
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ new_range = CreateGeneratedCodeRange(start, size);
+ GeneratedCodeRange* old_head = generated_code_ranges_.load(std::memory_order_relaxed);
+ new_range->next.store(old_head, std::memory_order_relaxed);
+ generated_code_ranges_.store(new_range, std::memory_order_release);
+ }
+
+ // The above release operation on `generated_code_ranges_` with an acquire operation
+ // on the same atomic object in `IsInGeneratedCode()` ensures the correct memory
+ // visibility for the contents of `*new_range` for any thread that loads the value
+ // written above (or a value written by a release sequence headed by that write).
+ //
+ // However, we also need to ensure that any thread that encounters a segmentation
+ // fault in the provided range shall actually see the written value. For JIT code
+ // cache and nterp, the registration happens while the process is single-threaded
+ // but the synchronization is more complicated for code in oat files.
+ //
+ // Threads that load classes register dex files under the `Locks::dex_lock_` and
+ // the first one to register a dex file with a given oat file shall add the oat
+ // code range; the memory visibility for these threads is guaranteed by the lock.
+ // However a thread that did not try to load a class with oat code can execute the
+ // code if a direct or indirect reference to such class escapes from one of the
+ // threads that loaded it. Use `membarrier()` for memory visibility in this case.
+ art::membarrier(MembarrierCommand::kPrivateExpedited);
+}
+
+void FaultManager::RemoveGeneratedCodeRange(const void* start, size_t size) {
+ Thread* self = Thread::Current();
+ GeneratedCodeRange* range = nullptr;
+ {
+ MutexLock lock(self, generated_code_ranges_lock_);
+ std::atomic<GeneratedCodeRange*>* before = &generated_code_ranges_;
+ range = before->load(std::memory_order_relaxed);
+ while (range != nullptr && range->start != start) {
+ before = &range->next;
+ range = before->load(std::memory_order_relaxed);
+ }
+ if (range != nullptr) {
+ GeneratedCodeRange* next = range->next.load(std::memory_order_relaxed);
+ if (before == &generated_code_ranges_) {
+ // Relaxed store directly to `generated_code_ranges_` would not satisfy
+ // conditions for a release sequence, so we need to use store-release.
+ before->store(next, std::memory_order_release);
+ } else {
+ // In the middle of the list, we can use a relaxed store as we're not
+ // publishing any newly written memory to potential reader threads.
+ // Whether they see the removed node or not is unimportant as we should
+ // not execute that code anymore. We're keeping the `next` link of the
+ // removed node, so that concurrent walk can use it to reach remaining
+ // retained nodes, if any.
+ before->store(next, std::memory_order_relaxed);
+ }
+ }
+ }
+ CHECK(range != nullptr);
+ DCHECK_EQ(range->start, start);
+ CHECK_EQ(range->size, size);
+
+ Runtime* runtime = Runtime::Current();
+ CHECK(runtime != nullptr);
+ if (runtime->IsStarted() && runtime->GetThreadList() != nullptr) {
+ // Run a checkpoint before deleting the range to ensure that no thread holds a
+ // pointer to the removed range while walking the list in `IsInGeneratedCode()`.
+ // That walk is guarded by checking that the thread is `Runnable`, so any walk
+ // started before the removal shall be done when running the checkpoint and the
+ // checkpoint also ensures the correct memory visibility of `next` links,
+ // so the thread shall not see the pointer during future walks.
+
+ // This function is currently called in different mutex and thread states.
+ // Semi-space GC performs the cleanup during its `MarkingPhase()` while holding
+ // the mutator exclusively, so we do not need a checkpoint. All other GCs perform
+ // the cleanup in their `ReclaimPhase()` while holding the mutator lock as shared
+ // and it's safe to release and re-acquire the mutator lock. Despite holding the
+ // mutator lock as shared, the thread is not always marked as `Runnable`.
+ // TODO: Clean up state transitions in different GC implementations. b/259440389
+ if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
+ // We do not need a checkpoint because no other thread is Runnable.
+ } else {
+ DCHECK(Locks::mutator_lock_->IsSharedHeld(self));
+ // Use explicit state transitions or unlock/lock.
+ bool runnable = (self->GetState() == ThreadState::kRunnable);
+ if (runnable) {
+ self->TransitionFromRunnableToSuspended(ThreadState::kNative);
+ } else {
+ Locks::mutator_lock_->SharedUnlock(self);
+ }
+ DCHECK(!Locks::mutator_lock_->IsSharedHeld(self));
+ runtime->GetThreadList()->RunEmptyCheckpoint();
+ if (runnable) {
+ self->TransitionFromSuspendedToRunnable();
+ } else {
+ Locks::mutator_lock_->SharedLock(self);
+ }
+ }
+ }
+ FreeGeneratedCodeRange(range);
+}
+
+// This function is called within the signal handler. It checks that the thread
+// is `Runnable`, the `mutator_lock_` is held (shared) and the fault PC is in one
+// of the registered generated code ranges. No annotalysis is done.
+bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context) {
// We can only be running Java code in the current thread if it
// is in Runnable state.
VLOG(signals) << "Checking for generated code";
@@ -343,76 +396,29 @@
return false;
}
- ArtMethod* method_obj = nullptr;
- uintptr_t return_pc = 0;
- uintptr_t sp = 0;
- bool is_stack_overflow = false;
-
- // Get the architecture specific method address and return address. These
- // are in architecture specific files in arch/<arch>/fault_handler_<arch>.
- GetMethodAndReturnPcAndSp(siginfo, context, &method_obj, &return_pc, &sp, &is_stack_overflow);
-
- // If we don't have a potential method, we're outta here.
- VLOG(signals) << "potential method: " << method_obj;
- // TODO: Check linear alloc and image.
- DCHECK_ALIGNED(ArtMethod::Size(kRuntimePointerSize), sizeof(void*))
- << "ArtMethod is not pointer aligned";
- if (method_obj == nullptr || !IsAligned<sizeof(void*)>(method_obj)) {
- VLOG(signals) << "no method";
+ uintptr_t fault_pc = GetFaultPc(siginfo, context);
+ if (fault_pc == 0u) {
+ VLOG(signals) << "no fault PC";
return false;
}
- // Verify that the potential method is indeed a method.
- // TODO: check the GC maps to make sure it's an object.
- // Check that the class pointer inside the object is not null and is aligned.
- // No read barrier because method_obj may not be a real object.
- mirror::Class* cls = SafeGetDeclaringClass(method_obj);
- if (cls == nullptr) {
- VLOG(signals) << "not a class";
- return false;
+ // Walk over the list of registered code ranges.
+ GeneratedCodeRange* range = generated_code_ranges_.load(std::memory_order_acquire);
+ while (range != nullptr) {
+ if (fault_pc - reinterpret_cast<uintptr_t>(range->start) < range->size) {
+ return true;
+ }
+ // We may or may not see ranges that were concurrently removed, depending
+ // on when the relaxed writes of the `next` links become visible. However,
+ // even if we're currently at a node that is being removed, we shall visit
+ // all remaining ranges that are not being removed as the removed nodes
+ // retain the `next` link at the time of removal (which may lead to other
+ // removed nodes before reaching remaining retained nodes, if any). Correct
+ // memory visibility of `start` and `size` fields of the visited ranges is
+ // ensured by the release and acquire operations on `generated_code_ranges_`.
+ range = range->next.load(std::memory_order_relaxed);
}
-
- if (!IsAligned<kObjectAlignment>(cls)) {
- VLOG(signals) << "not aligned";
- return false;
- }
-
- if (!SafeVerifyClassClass(cls)) {
- VLOG(signals) << "not a class class";
- return false;
- }
-
- if (!IsKnownPc(return_pc, method_obj)) {
- VLOG(signals) << "PC not in Java code";
- return false;
- }
-
- const OatQuickMethodHeader* method_header = method_obj->GetOatQuickMethodHeader(return_pc);
-
- if (method_header == nullptr) {
- VLOG(signals) << "no compiled code";
- return false;
- }
-
- // We can be certain that this is a method now. Check if we have a GC map
- // at the return PC address.
- if (true || kIsDebugBuild) {
- VLOG(signals) << "looking for dex pc for return pc " << std::hex << return_pc;
- uint32_t sought_offset = return_pc -
- reinterpret_cast<uintptr_t>(method_header->GetEntryPoint());
- VLOG(signals) << "pc offset: " << std::hex << sought_offset;
- }
- uint32_t dexpc = dex::kDexNoIndex;
- if (is_stack_overflow) {
- // If it's an implicit stack overflow check, the frame is not setup, so we
- // just infer the dex PC as zero.
- dexpc = 0;
- } else {
- CHECK_EQ(*reinterpret_cast<ArtMethod**>(sp), method_obj);
- dexpc = method_header->ToDexPc(reinterpret_cast<ArtMethod**>(sp), return_pc, false);
- }
- VLOG(signals) << "dexpc: " << dexpc;
- return !check_dex_pc || dexpc != dex::kDexNoIndex;
+ return false;
}
FaultHandler::FaultHandler(FaultManager* manager) : manager_(manager) {
@@ -425,6 +431,76 @@
manager_->AddHandler(this, true);
}
+bool NullPointerHandler::IsValidMethod(ArtMethod* method) {
+ // At this point we know that the thread is `Runnable` and the PC is in one of
+ // the registered code ranges. The `method` was read from the top of the stack
+ // and should really point to an actual `ArtMethod`, unless we're crashing during
+ // prologue or epilogue, or somehow managed to jump to the compiled code by some
+ // unexpected path, other than method invoke or exception delivery. We do a few
+ // quick checks without guarding from another fault.
+ VLOG(signals) << "potential method: " << method;
+
+ static_assert(IsAligned<sizeof(void*)>(ArtMethod::Size(kRuntimePointerSize)));
+ if (method == nullptr || !IsAligned<sizeof(void*)>(method)) {
+ VLOG(signals) << ((method == nullptr) ? "null method" : "unaligned method");
+ return false;
+ }
+
+ // Check that the presumed method actually points to a class. Read barriers
+ // are not needed (and would be undesirable in a signal handler) when reading
+ // a chain of constant references to get to a non-movable `Class.class` object.
+
+ // Note: Allowing nested faults. Checking that the method is in one of the
+ // `LinearAlloc` spaces, or that objects we look at are in the `Heap` would be
+ // slow and require locking a mutex, which is undesirable in a signal handler.
+ // (Though we could register valid ranges similarly to the generated code ranges.)
+
+ mirror::Object* klass =
+ method->GetDeclaringClassAddressWithoutBarrier()->AsMirrorPtr();
+ if (klass == nullptr || !IsAligned<kObjectAlignment>(klass)) {
+ VLOG(signals) << ((klass == nullptr) ? "null class" : "unaligned class");
+ return false;
+ }
+
+ mirror::Class* class_class = klass->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (class_class == nullptr || !IsAligned<kObjectAlignment>(class_class)) {
+ VLOG(signals) << ((klass == nullptr) ? "null class_class" : "unaligned class_class");
+ return false;
+ }
+
+ if (class_class != class_class->GetClass<kVerifyNone, kWithoutReadBarrier>()) {
+ VLOG(signals) << "invalid class_class";
+ return false;
+ }
+
+ return true;
+}
+
+bool NullPointerHandler::IsValidReturnPc(ArtMethod** sp, uintptr_t return_pc) {
+ // Check if we can associate a dex PC with the return PC, whether from Nterp,
+ // or with an existing stack map entry for a compiled method.
+ // Note: Allowing nested faults if `IsValidMethod()` returned a false positive.
+ // Note: The `ArtMethod::GetOatQuickMethodHeader()` can acquire locks (at least
+ // `Locks::jit_lock_`) and if the thread already held such a lock, the signal
+ // handler would deadlock. However, if a thread is holding one of the locks
+ // below the mutator lock, the PC should be somewhere in ART code and should
+ // not match any registered generated code range, so such as a deadlock is
+ // unlikely. If it happens anyway, the worst case is that an internal ART crash
+ // would be reported as ANR.
+ ArtMethod* method = *sp;
+ const OatQuickMethodHeader* method_header = method->GetOatQuickMethodHeader(return_pc);
+ if (method_header == nullptr) {
+ VLOG(signals) << "No method header.";
+ return false;
+ }
+ VLOG(signals) << "looking for dex pc for return pc 0x" << std::hex << return_pc
+ << " pc offset: 0x" << std::hex
+ << (return_pc - reinterpret_cast<uintptr_t>(method_header->GetEntryPoint()));
+ uint32_t dexpc = method_header->ToDexPc(reinterpret_cast<ArtMethod**>(sp), return_pc, false);
+ VLOG(signals) << "dexpc: " << dexpc;
+ return dexpc != dex::kDexNoIndex;
+}
+
//
// Suspension fault handler
//
@@ -448,17 +524,13 @@
bool JavaStackTraceHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* siginfo, void* context) {
// Make sure that we are in the generated code, but we may not have a dex pc.
- bool in_generated_code = manager_->IsInGeneratedCode(siginfo, context, false);
+ bool in_generated_code = manager_->IsInGeneratedCode(siginfo, context);
if (in_generated_code) {
LOG(ERROR) << "Dumping java stack trace for crash in generated code";
- ArtMethod* method = nullptr;
- uintptr_t return_pc = 0;
- uintptr_t sp = 0;
- bool is_stack_overflow = false;
Thread* self = Thread::Current();
- manager_->GetMethodAndReturnPcAndSp(
- siginfo, context, &method, &return_pc, &sp, &is_stack_overflow);
+ uintptr_t sp = FaultManager::GetFaultSp(context);
+ CHECK_NE(sp, 0u); // Otherwise we should not have reached this handler.
// Inside of generated code, sp[0] is the method, so sp is the frame.
self->SetTopOfStack(reinterpret_cast<ArtMethod**>(sp));
self->DumpJavaStack(LOG_STREAM(ERROR));
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 8b89c22..43f93e4 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -21,9 +21,11 @@
#include <signal.h>
#include <stdint.h>
+#include <atomic>
#include <vector>
#include "base/locks.h" // For annotalysis.
+#include "base/mutex.h"
#include "runtime_globals.h" // For CanDoImplicitNullCheckOn.
namespace art {
@@ -51,29 +53,57 @@
void AddHandler(FaultHandler* handler, bool generated_code);
void RemoveHandler(FaultHandler* handler);
- // Note that the following two functions are called in the context of a signal handler.
- // The IsInGeneratedCode() function checks that the mutator lock is held before it
- // calls GetMethodAndReturnPCAndSP().
- // TODO: think about adding lock assertions and fake lock and unlock functions.
- void GetMethodAndReturnPcAndSp(siginfo_t* siginfo,
- void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow)
- NO_THREAD_SAFETY_ANALYSIS;
- bool IsInGeneratedCode(siginfo_t* siginfo, void *context, bool check_dex_pc)
- NO_THREAD_SAFETY_ANALYSIS;
+ void AddGeneratedCodeRange(const void* start, size_t size);
+ void RemoveGeneratedCodeRange(const void* start, size_t size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Retrieves fault PC from architecture-dependent `context`, returns 0 on failure.
+ // Called in the context of a signal handler.
+ static uintptr_t GetFaultPc(siginfo_t* siginfo, void* context);
+
+ // Retrieves SP from architecture-dependent `context`.
+ // Called in the context of a signal handler.
+ static uintptr_t GetFaultSp(void* context);
+
+ // Checks if the fault happened while running generated code.
+ // Called in the context of a signal handler.
+ bool IsInGeneratedCode(siginfo_t* siginfo, void *context) NO_THREAD_SAFETY_ANALYSIS;
private:
+ struct GeneratedCodeRange {
+ std::atomic<GeneratedCodeRange*> next;
+ const void* start;
+ size_t size;
+ };
+
+ GeneratedCodeRange* CreateGeneratedCodeRange(const void* start, size_t size)
+ REQUIRES(generated_code_ranges_lock_);
+ void FreeGeneratedCodeRange(GeneratedCodeRange* range) REQUIRES(!generated_code_ranges_lock_);
+
// The HandleFaultByOtherHandlers function is only called by HandleFault function for generated code.
bool HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context)
NO_THREAD_SAFETY_ANALYSIS;
+ // Note: The lock guards modifications of the ranges but the function `IsInGeneratedCode()`
+ // walks the list in the context of a signal handler without holding the lock.
+ Mutex generated_code_ranges_lock_;
+ std::atomic<GeneratedCodeRange*> generated_code_ranges_ GUARDED_BY(generated_code_ranges_lock_);
+
std::vector<FaultHandler*> generated_code_handlers_;
std::vector<FaultHandler*> other_handlers_;
struct sigaction oldaction_;
bool initialized_;
+
+ // We keep a certain number of generated code ranges locally to avoid too many
+ // cache misses while traversing the singly-linked list `generated_code_ranges_`.
+ // 16 should be enough for the boot image (assuming `--multi-image`; there is
+ // only one entry for `--single-image`), nterp, JIT code cache and a few other
+ // entries for the app or system server.
+ static constexpr size_t kNumLocalGeneratedCodeRanges = 16;
+ GeneratedCodeRange generated_code_ranges_storage_[kNumLocalGeneratedCodeRanges];
+ GeneratedCodeRange* free_generated_code_ranges_
+ GUARDED_BY(generated_code_ranges_lock_);
+
DISALLOW_COPY_AND_ASSIGN(FaultManager);
};
@@ -98,17 +128,29 @@
public:
explicit NullPointerHandler(FaultManager* manager);
- bool Action(int sig, siginfo_t* siginfo, void* context) override;
-
- static bool IsValidImplicitCheck(siginfo_t* siginfo) {
- // Our implicit NPE checks always limit the range to a page.
- // Note that the runtime will do more exhaustive checks (that we cannot
- // reasonably do in signal processing code) based on the dex instruction
- // faulting.
- return CanDoImplicitNullCheckOn(reinterpret_cast<uintptr_t>(siginfo->si_addr));
- }
+ // NO_THREAD_SAFETY_ANALYSIS: Called after the fault manager determined that
+ // the thread is `Runnable` and holds the mutator lock (shared) but without
+ // telling annotalysis that we actually hold the lock.
+ bool Action(int sig, siginfo_t* siginfo, void* context) override
+ NO_THREAD_SAFETY_ANALYSIS;
private:
+ // Helper functions for checking whether the signal can be interpreted
+ // as implicit NPE check. Note that the runtime will do more exhaustive
+ // checks (that we cannot reasonably do in signal processing code) based
+ // on the dex instruction faulting.
+
+ static bool IsValidFaultAddress(uintptr_t fault_address) {
+ // Our implicit NPE checks always limit the range to a page.
+ return CanDoImplicitNullCheckOn(fault_address);
+ }
+
+ static bool IsValidMethod(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static bool IsValidReturnPc(ArtMethod** sp, uintptr_t return_pc)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
DISALLOW_COPY_AND_ASSIGN(NullPointerHandler);
};
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index fdf1615..b8b328c 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -31,11 +31,6 @@
namespace gc {
namespace accounting {
-constexpr size_t CardTable::kCardShift;
-constexpr size_t CardTable::kCardSize;
-constexpr uint8_t CardTable::kCardClean;
-constexpr uint8_t CardTable::kCardDirty;
-
/*
* Maintain a card table from the write barrier. All writes of
* non-null values to heap addresses should go through an entry in
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index 12baaa4..b34a883 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -19,8 +19,8 @@
#include <string>
#include "base/atomic.h"
+#include "base/common_art_test.h"
#include "base/utils.h"
-#include "common_runtime_test.h"
#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
#include "mirror/string-inl.h" // Strings are easiest to allocate
@@ -36,7 +36,7 @@
namespace gc {
namespace accounting {
-class CardTableTest : public CommonRuntimeTest {
+class CardTableTest : public CommonArtTest {
public:
std::unique_ptr<CardTable> card_table_;
diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc
index e42682a..3f38f50 100644
--- a/runtime/gc/accounting/mod_union_table_test.cc
+++ b/runtime/gc/accounting/mod_union_table_test.cc
@@ -46,6 +46,7 @@
class ModUnionTableTest : public CommonRuntimeTest {
public:
ModUnionTableTest() : java_lang_object_array_(nullptr) {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
}
mirror::ObjectArray<mirror::Object>* AllocObjectArray(
Thread* self, space::ContinuousMemMapAllocSpace* space, size_t component_count)
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index eca770e..e318933 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -40,8 +40,8 @@
template<size_t kAlignment>
class SpaceBitmap {
public:
- typedef void ScanCallback(mirror::Object* obj, void* finger, void* arg);
- typedef void SweepCallback(size_t ptr_count, mirror::Object** ptrs, void* arg);
+ using ScanCallback = void(mirror::Object* obj, void* finger, void* arg);
+ using SweepCallback = void(size_t ptr_count, mirror::Object** ptrs, void* arg);
// Initialize a space bitmap so that it points to a bitmap large enough to cover a heap at
// heap_begin of heap_capacity bytes, where objects are guaranteed to be kAlignment-aligned.
@@ -164,7 +164,7 @@
void CopyFrom(SpaceBitmap* source_bitmap);
// Starting address of our internal storage.
- Atomic<uintptr_t>* Begin() {
+ Atomic<uintptr_t>* Begin() const {
return bitmap_begin_;
}
diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc
index 3a69865..8fcf102 100644
--- a/runtime/gc/accounting/space_bitmap_test.cc
+++ b/runtime/gc/accounting/space_bitmap_test.cc
@@ -19,8 +19,8 @@
#include <stdint.h>
#include <memory>
+#include "base/common_art_test.h"
#include "base/mutex.h"
-#include "common_runtime_test.h"
#include "runtime_globals.h"
#include "space_bitmap-inl.h"
@@ -28,7 +28,7 @@
namespace gc {
namespace accounting {
-class SpaceBitmapTest : public CommonRuntimeTest {};
+class SpaceBitmapTest : public CommonArtTest {};
TEST_F(SpaceBitmapTest, Init) {
uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x10000000);
diff --git a/runtime/gc/allocator/art-dlmalloc.cc b/runtime/gc/allocator/art-dlmalloc.cc
new file mode 100644
index 0000000..de0c85a
--- /dev/null
+++ b/runtime/gc/allocator/art-dlmalloc.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 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 "art-dlmalloc.h"
+
+#include <android-base/logging.h>
+
+#include "base/bit_utils.h"
+
+// ART specific morecore implementation defined in space.cc.
+static void* art_heap_morecore(void* m, intptr_t increment);
+#define MORECORE(x) art_heap_morecore(m, x)
+
+// Custom heap error handling.
+#define PROCEED_ON_ERROR 0
+static void art_heap_corruption(const char* function);
+static void art_heap_usage_error(const char* function, void* p);
+#define CORRUPTION_ERROR_ACTION(m) art_heap_corruption(__FUNCTION__)
+#define USAGE_ERROR_ACTION(m, p) art_heap_usage_error(__FUNCTION__, p)
+
+// Ugly inclusion of C file so that ART specific #defines configure dlmalloc for our use for
+// mspaces (regular dlmalloc is still declared in bionic).
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
+#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
+#include "dlmalloc.c" // NOLINT
+// Note: dlmalloc.c uses a DEBUG define to drive debug code. This interferes with the DEBUG severity
+// of libbase, so undefine it now.
+#undef DEBUG
+#pragma GCC diagnostic pop
+
+static void* art_heap_morecore(void* m, intptr_t increment) {
+ return ::art::gc::allocator::ArtDlMallocMoreCore(m, increment);
+}
+
+static void art_heap_corruption(const char* function) {
+ LOG(FATAL) << "Corrupt heap detected in: " << function;
+}
+
+static void art_heap_usage_error(const char* function, void* p) {
+ LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p
+ << " not expected";
+}
+
+#include <sys/mman.h>
+
+#include "base/utils.h"
+#include "runtime_globals.h"
+
+extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) {
+ // Is this chunk in use?
+ if (used_bytes != 0) {
+ return;
+ }
+ // Do we have any whole pages to give back?
+ start = reinterpret_cast<void*>(art::RoundUp(reinterpret_cast<uintptr_t>(start), art::kPageSize));
+ end = reinterpret_cast<void*>(art::RoundDown(reinterpret_cast<uintptr_t>(end), art::kPageSize));
+ if (end > start) {
+ size_t length = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start);
+ int rc = madvise(start, length, MADV_DONTNEED);
+ if (UNLIKELY(rc != 0)) {
+ errno = rc;
+ PLOG(FATAL) << "madvise failed during heap trimming";
+ }
+ size_t* reclaimed = reinterpret_cast<size_t*>(arg);
+ *reclaimed += length;
+ }
+}
+
+extern "C" void DlmallocBytesAllocatedCallback(void* start ATTRIBUTE_UNUSED,
+ void* end ATTRIBUTE_UNUSED,
+ size_t used_bytes,
+ void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* bytes_allocated = reinterpret_cast<size_t*>(arg);
+ *bytes_allocated += used_bytes + sizeof(size_t);
+}
+
+extern "C" void DlmallocObjectsAllocatedCallback(void* start ATTRIBUTE_UNUSED,
+ void* end ATTRIBUTE_UNUSED,
+ size_t used_bytes,
+ void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* objects_allocated = reinterpret_cast<size_t*>(arg);
+ ++(*objects_allocated);
+}
diff --git a/runtime/gc/allocator/art-dlmalloc.h b/runtime/gc/allocator/art-dlmalloc.h
new file mode 100644
index 0000000..296de72
--- /dev/null
+++ b/runtime/gc/allocator/art-dlmalloc.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_ALLOCATOR_ART_DLMALLOC_H_
+#define ART_RUNTIME_GC_ALLOCATOR_ART_DLMALLOC_H_
+
+#include <cstdint>
+
+// Configure dlmalloc for mspaces.
+// Avoid a collision with one used in llvm.
+#undef HAVE_MMAP
+#define HAVE_MMAP 0
+#define HAVE_MREMAP 0
+#define HAVE_MORECORE 1
+#define MSPACES 1
+#define NO_MALLINFO 1
+#define ONLY_MSPACES 1
+#define MALLOC_INSPECT_ALL 1
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
+#include "dlmalloc.h"
+#pragma GCC diagnostic pop
+
+// Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused
+// pages back to the kernel.
+extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
+
+// Callbacks for dlmalloc_inspect_all or mspace_inspect_all that will
+// count the number of bytes allocated and objects allocated,
+// respectively.
+extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+
+namespace art {
+namespace gc {
+namespace allocator {
+
+// Callback from dlmalloc when it needs to increase the footprint. Must be implemented somewhere
+// else (currently dlmalloc_space.cc).
+void* ArtDlMallocMoreCore(void* mspace, intptr_t increment);
+
+} // namespace allocator
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_ALLOCATOR_ART_DLMALLOC_H_
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
deleted file mode 100644
index 79d4fbf..0000000
--- a/runtime/gc/allocator/dlmalloc.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2012 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 "dlmalloc.h"
-
-#include <android-base/logging.h>
-
-#include "base/bit_utils.h"
-
-// ART specific morecore implementation defined in space.cc.
-static void* art_heap_morecore(void* m, intptr_t increment);
-#define MORECORE(x) art_heap_morecore(m, x)
-
-// Custom heap error handling.
-#define PROCEED_ON_ERROR 0
-static void art_heap_corruption(const char* function);
-static void art_heap_usage_error(const char* function, void* p);
-#define CORRUPTION_ERROR_ACTION(m) art_heap_corruption(__FUNCTION__)
-#define USAGE_ERROR_ACTION(m, p) art_heap_usage_error(__FUNCTION__, p)
-
-// Ugly inclusion of C file so that ART specific #defines configure dlmalloc for our use for
-// mspaces (regular dlmalloc is still declared in bionic).
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#pragma GCC diagnostic ignored "-Wempty-body"
-#pragma GCC diagnostic ignored "-Wstrict-aliasing"
-#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
-#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
-#include "../../../external/dlmalloc/malloc.c"
-// Note: malloc.c uses a DEBUG define to drive debug code. This interferes with the DEBUG severity
-// of libbase, so undefine it now.
-#undef DEBUG
-#pragma GCC diagnostic pop
-
-static void* art_heap_morecore(void* m, intptr_t increment) {
- return ::art::gc::allocator::ArtDlMallocMoreCore(m, increment);
-}
-
-static void art_heap_corruption(const char* function) {
- LOG(FATAL) << "Corrupt heap detected in: " << function;
-}
-
-static void art_heap_usage_error(const char* function, void* p) {
- LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p
- << " not expected";
-}
-
-#include <sys/mman.h>
-
-#include "base/utils.h"
-#include "runtime_globals.h"
-
-extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) {
- // Is this chunk in use?
- if (used_bytes != 0) {
- return;
- }
- // Do we have any whole pages to give back?
- start = reinterpret_cast<void*>(art::RoundUp(reinterpret_cast<uintptr_t>(start), art::kPageSize));
- end = reinterpret_cast<void*>(art::RoundDown(reinterpret_cast<uintptr_t>(end), art::kPageSize));
- if (end > start) {
- size_t length = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start);
- int rc = madvise(start, length, MADV_DONTNEED);
- if (UNLIKELY(rc != 0)) {
- errno = rc;
- PLOG(FATAL) << "madvise failed during heap trimming";
- }
- size_t* reclaimed = reinterpret_cast<size_t*>(arg);
- *reclaimed += length;
- }
-}
-
-extern "C" void DlmallocBytesAllocatedCallback(void* start ATTRIBUTE_UNUSED,
- void* end ATTRIBUTE_UNUSED,
- size_t used_bytes,
- void* arg) {
- if (used_bytes == 0) {
- return;
- }
- size_t* bytes_allocated = reinterpret_cast<size_t*>(arg);
- *bytes_allocated += used_bytes + sizeof(size_t);
-}
-
-extern "C" void DlmallocObjectsAllocatedCallback(void* start ATTRIBUTE_UNUSED,
- void* end ATTRIBUTE_UNUSED,
- size_t used_bytes,
- void* arg) {
- if (used_bytes == 0) {
- return;
- }
- size_t* objects_allocated = reinterpret_cast<size_t*>(arg);
- ++(*objects_allocated);
-}
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
deleted file mode 100644
index b12691a..0000000
--- a/runtime/gc/allocator/dlmalloc.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
-#define ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
-
-#include <cstdint>
-
-// Configure dlmalloc for mspaces.
-// Avoid a collision with one used in llvm.
-#undef HAVE_MMAP
-#define HAVE_MMAP 0
-#define HAVE_MREMAP 0
-#define HAVE_MORECORE 1
-#define MSPACES 1
-#define NO_MALLINFO 1
-#define ONLY_MSPACES 1
-#define MALLOC_INSPECT_ALL 1
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
-#include "../../external/dlmalloc/malloc.h"
-#pragma GCC diagnostic pop
-
-// Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused
-// pages back to the kernel.
-extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
-
-// Callbacks for dlmalloc_inspect_all or mspace_inspect_all that will
-// count the number of bytes allocated and objects allocated,
-// respectively.
-extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
-extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
-
-namespace art {
-namespace gc {
-namespace allocator {
-
-// Callback from dlmalloc when it needs to increase the footprint. Must be implemented somewhere
-// else (currently dlmalloc_space.cc).
-void* ArtDlMallocMoreCore(void* mspace, intptr_t increment);
-
-} // namespace allocator
-} // namespace gc
-} // namespace art
-
-#endif // ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 44aeeff..5118523 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -589,10 +589,11 @@
if (kIsDebugBuild && !cc->use_generational_cc_) {
cc->region_space_->AssertAllRegionLiveBytesZeroOrCleared();
}
- if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
- CHECK(Runtime::Current()->IsAotCompiler());
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(runtime->IsActiveTransaction())) {
+ CHECK(runtime->IsAotCompiler());
TimingLogger::ScopedTiming split3("(Paused)VisitTransactionRoots", cc->GetTimings());
- Runtime::Current()->VisitTransactionRoots(cc);
+ runtime->VisitTransactionRoots(cc);
}
if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
cc->GrayAllNewlyDirtyImmuneObjects();
@@ -601,15 +602,10 @@
cc->VerifyGrayImmuneObjects();
}
}
- // May be null during runtime creation, in this case leave java_lang_Object null.
- // This is safe since single threaded behavior should mean FillWithFakeObject does not
- // happen when java_lang_Object_ is null.
- if (WellKnownClasses::java_lang_Object != nullptr) {
- cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread,
- WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object).Ptr()));
- } else {
- cc->java_lang_Object_ = nullptr;
- }
+ ObjPtr<mirror::Class> java_lang_Object =
+ GetClassRoot<mirror::Object, kWithoutReadBarrier>(runtime->GetClassLinker());
+ DCHECK(java_lang_Object != nullptr);
+ cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread, java_lang_Object.Ptr()));
}
private:
@@ -1706,8 +1702,6 @@
if (kVerboseMode) {
LOG(INFO) << "SweepSystemWeaks done";
}
- // Free data for class loaders that we unloaded.
- Runtime::Current()->GetClassLinker()->CleanupClassLoaders();
// Marking is done. Disable marking.
DisableMarking();
CheckEmptyMarkStack();
@@ -2734,6 +2728,11 @@
}
Thread* self = Thread::Current();
+ // Free data for class loaders that we unloaded. This includes removing
+ // dead methods from JIT's internal maps. This must be done before
+ // reclaiming the memory of the dead methods' declaring classes.
+ Runtime::Current()->GetClassLinker()->CleanupClassLoaders();
+
{
// Double-check that the mark stack is empty.
// Note: need to set this after VerifyNoFromSpaceRef().
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index a0ea60d..caa8106 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -16,7 +16,8 @@
#include <sys/mman.h>
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
+#include "base/utils.h"
#include "gc/collector/immune_spaces.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -46,7 +47,7 @@
MemMap&& oat_map)
: ImageSpace("FakeImageSpace",
/*image_location=*/"",
- /*profile_file=*/{},
+ /*profile_files=*/{},
std::move(map),
std::move(live_bitmap),
map.End()),
@@ -59,7 +60,7 @@
MemMap oat_map_;
};
-class ImmuneSpacesTest : public CommonRuntimeTest {
+class ImmuneSpacesTest : public CommonArtTest {
static constexpr size_t kMaxBitmaps = 10;
public:
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 78ee5c5..febd70e 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -159,7 +159,7 @@
};
private:
- using ObjReference = mirror::ObjectReference</*kPoisonReferences*/ false, mirror::Object>;
+ using ObjReference = mirror::CompressedReference<mirror::Object>;
// Number of bits (live-words) covered by a single chunk-info (below)
// entry/word.
// TODO: Since popcount is performed usomg SIMD instructions, we should
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index c450676..9f1f2dc 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -103,6 +103,7 @@
#include "runtime.h"
#include "javaheapprof/javaheapsampler.h"
#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
#include "thread_list.h"
#include "verify_object-inl.h"
#include "well_known_classes.h"
@@ -336,6 +337,7 @@
// this one.
process_state_update_lock_("process state update lock", kPostMonitorLock),
min_foreground_target_footprint_(0),
+ min_foreground_concurrent_start_bytes_(0),
concurrent_start_bytes_(std::numeric_limits<size_t>::max()),
total_bytes_freed_ever_(0),
total_objects_freed_ever_(0),
@@ -495,6 +497,7 @@
runtime->ShouldRelocate(),
/*executable=*/ !runtime->IsAotCompiler(),
heap_reservation_size,
+ runtime->AllowInMemoryCompilation(),
&boot_image_spaces,
&heap_reservation)) {
DCHECK_EQ(heap_reservation_size, heap_reservation.IsValid() ? heap_reservation.Size() : 0u);
@@ -1086,7 +1089,9 @@
min_foreground_target_footprint_,
std::memory_order_relaxed);
}
- min_foreground_target_footprint_ = 0;
+ if (IsGcConcurrent() && concurrent_start_bytes_ < min_foreground_concurrent_start_bytes_) {
+ concurrent_start_bytes_ = min_foreground_concurrent_start_bytes_;
+ }
}
void Heap::UpdateProcessState(ProcessState old_process_state, ProcessState new_process_state) {
@@ -1488,6 +1493,8 @@
Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
return;
}
+ // Allow plugins to intercept out of memory errors.
+ Runtime::Current()->OutOfMemoryErrorHook();
std::ostringstream oss;
size_t total_bytes_free = GetFreeMemory();
@@ -1534,6 +1541,20 @@
void Heap::DoPendingCollectorTransition() {
CollectorType desired_collector_type = desired_collector_type_;
+
+ if (collector_type_ == kCollectorTypeCC) {
+ // App's allocations (since last GC) more than the threshold then do TransitionGC
+ // when the app was in background. If not then don't do TransitionGC.
+ size_t num_bytes_allocated_since_gc = GetBytesAllocated() - num_bytes_alive_after_gc_;
+ if (num_bytes_allocated_since_gc <
+ (UnsignedDifference(target_footprint_.load(std::memory_order_relaxed),
+ num_bytes_alive_after_gc_)/4)
+ && !kStressCollectorTransition
+ && !IsLowMemoryMode()) {
+ return;
+ }
+ }
+
// Launch homogeneous space compaction if it is desired.
if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
if (!CareAboutPauseTimes()) {
@@ -1547,7 +1568,7 @@
// Invoke CC full compaction.
CollectGarbageInternal(collector::kGcTypeFull,
kGcCauseCollectorTransition,
- /*clear_soft_references=*/false, GC_NUM_ANY);
+ /*clear_soft_references=*/false, GetCurrentGcNum() + 1);
} else {
VLOG(gc) << "CC background compaction ignored due to jank perceptible process state";
}
@@ -3729,7 +3750,9 @@
// process-state switch.
min_foreground_target_footprint_ =
(multiplier <= 1.0 && grow_bytes > 0)
- ? bytes_allocated + static_cast<size_t>(grow_bytes * foreground_heap_growth_multiplier_)
+ ? std::min(
+ bytes_allocated + static_cast<size_t>(grow_bytes * foreground_heap_growth_multiplier_),
+ GetMaxMemory())
: 0;
if (IsGcConcurrent()) {
@@ -3761,6 +3784,12 @@
// allocation rate is very high, remaining_bytes could tell us that we should start a GC
// right away.
concurrent_start_bytes_ = std::max(target_footprint - remaining_bytes, bytes_allocated);
+ // Store concurrent_start_bytes_ (computed with foreground heap growth multiplier) for update
+ // itself when process state switches to foreground.
+ min_foreground_concurrent_start_bytes_ =
+ min_foreground_target_footprint_ != 0
+ ? std::max(min_foreground_target_footprint_ - remaining_bytes, bytes_allocated)
+ : 0;
}
}
}
@@ -3811,12 +3840,11 @@
void Heap::AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object) {
ScopedObjectAccess soa(self);
- ScopedLocalRef<jobject> arg(self->GetJniEnv(), soa.AddLocalReference<jobject>(*object));
- jvalue args[1];
- args[0].l = arg.get();
- InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_FinalizerReference_add, args);
- // Restore object in case it gets moved.
- *object = soa.Decode<mirror::Object>(arg.get());
+ StackHandleScope<1u> hs(self);
+ // Use handle wrapper to update the `*object` if the object gets moved.
+ HandleWrapperObjPtr<mirror::Object> h_object = hs.NewHandleWrapper(object);
+ WellKnownClasses::java_lang_ref_FinalizerReference_add->InvokeStatic<'V', 'L'>(
+ self, h_object.Get());
}
void Heap::RequestConcurrentGCAndSaveObject(Thread* self,
@@ -3890,7 +3918,7 @@
}
// If we can't run the GC type we wanted to run, find the next appropriate one and try
// that instead. E.g. can't do partial, so do full instead.
- // We must ensure that we run something that ends up inrementing gcs_completed_.
+ // We must ensure that we run something that ends up incrementing gcs_completed_.
// In the kGcTypePartial case, the initial CollectGarbageInternal call may not have that
// effect, but the subsequent KGcTypeFull call will.
if (CollectGarbageInternal(next_gc_type, cause, false, requested_gc_num)
@@ -3938,16 +3966,6 @@
// For CC, we invoke a full compaction when going to the background, but the collector type
// doesn't change.
DCHECK_EQ(desired_collector_type_, kCollectorTypeCCBackground);
- // App's allocations (since last GC) more than the threshold then do TransitionGC
- // when the app was in background. If not then don't do TransitionGC.
- size_t num_bytes_allocated_since_gc = GetBytesAllocated() - num_bytes_alive_after_gc_;
- if (num_bytes_allocated_since_gc <
- (UnsignedDifference(target_footprint_.load(std::memory_order_relaxed),
- num_bytes_alive_after_gc_)/4)
- && !kStressCollectorTransition
- && !IsLowMemoryMode()) {
- return;
- }
}
DCHECK_NE(collector_type_, kCollectorTypeCCBackground);
CollectorTransitionTask* added_task = nullptr;
@@ -4058,12 +4076,6 @@
}
}
-void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) {
- env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_runFinalization,
- static_cast<jlong>(timeout));
-}
-
// For GC triggering purposes, we count old (pre-last-GC) and new native allocations as
// different fractions of Java allocations.
// For now, we essentially do not count old native allocations at all, so that we can preserve the
@@ -4149,7 +4161,7 @@
// About kNotifyNativeInterval allocations have occurred. Check whether we should garbage collect.
void Heap::NotifyNativeAllocations(JNIEnv* env) {
native_objects_notified_.fetch_add(kNotifyNativeInterval, std::memory_order_relaxed);
- CheckGCForNative(ThreadForEnv(env));
+ CheckGCForNative(Thread::ForEnv(env));
}
// Register a native allocation with an explicit size.
@@ -4163,7 +4175,7 @@
native_objects_notified_.fetch_add(1, std::memory_order_relaxed);
if (objects_notified % kNotifyNativeInterval == kNotifyNativeInterval - 1
|| bytes > kCheckImmediatelyThreshold) {
- CheckGCForNative(ThreadForEnv(env));
+ CheckGCForNative(Thread::ForEnv(env));
}
// Heap profiler treats this as a Java allocation with a null object.
JHPCheckNonTlabSampleAllocation(Thread::Current(), nullptr, bytes);
@@ -4656,27 +4668,25 @@
// Temporarily increase target_footprint_ and concurrent_start_bytes_ to
// max values to avoid GC during app launch.
- if (!IsLowMemoryMode()) {
- // Set target_footprint_ to the largest allowed value.
- SetIdealFootprint(growth_limit_);
- SetDefaultConcurrentStartBytes();
+ // Set target_footprint_ to the largest allowed value.
+ SetIdealFootprint(growth_limit_);
+ SetDefaultConcurrentStartBytes();
- // Shrink heap after kPostForkMaxHeapDurationMS, to force a memory hog process to GC.
- // This remains high enough that many processes will continue without a GC.
- if (initial_heap_size_ < growth_limit_) {
- size_t first_shrink_size = std::max(growth_limit_ / 4, initial_heap_size_);
- last_adj_time += MsToNs(kPostForkMaxHeapDurationMS);
+ // Shrink heap after kPostForkMaxHeapDurationMS, to force a memory hog process to GC.
+ // This remains high enough that many processes will continue without a GC.
+ if (initial_heap_size_ < growth_limit_) {
+ size_t first_shrink_size = std::max(growth_limit_ / 4, initial_heap_size_);
+ last_adj_time += MsToNs(kPostForkMaxHeapDurationMS);
+ GetTaskProcessor()->AddTask(
+ self, new ReduceTargetFootprintTask(last_adj_time, first_shrink_size, starting_gc_num));
+ // Shrink to a small value after a substantial time period. This will typically force a
+ // GC if none has occurred yet. Has no effect if there was a GC before this anyway, which
+ // is commonly the case, e.g. because of a process transition.
+ if (initial_heap_size_ < first_shrink_size) {
+ last_adj_time += MsToNs(4 * kPostForkMaxHeapDurationMS);
GetTaskProcessor()->AddTask(
- self, new ReduceTargetFootprintTask(last_adj_time, first_shrink_size, starting_gc_num));
- // Shrink to a small value after a substantial time period. This will typically force a
- // GC if none has occurred yet. Has no effect if there was a GC before this anyway, which
- // is commonly the case, e.g. because of a process transition.
- if (initial_heap_size_ < first_shrink_size) {
- last_adj_time += MsToNs(4 * kPostForkMaxHeapDurationMS);
- GetTaskProcessor()->AddTask(
- self,
- new ReduceTargetFootprintTask(last_adj_time, initial_heap_size_, starting_gc_num));
- }
+ self,
+ new ReduceTargetFootprintTask(last_adj_time, initial_heap_size_, starting_gc_num));
}
}
// Schedule a GC after a substantial period of time. This will become a no-op if another GC is
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 26cb3be..1148a76 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1126,9 +1126,6 @@
size_t alloc_size,
bool grow);
- // Run the finalizers. If timeout is non zero, then we use the VMRuntime version.
- void RunFinalization(JNIEnv* env, uint64_t timeout);
-
// Blocks the caller until the garbage collector becomes idle and returns the type of GC we
// waited for.
collector::GcType WaitForGcToCompleteLocked(GcCause cause, Thread* self)
@@ -1447,8 +1444,9 @@
// Computed with foreground-multiplier in GrowForUtilization() when run in
// jank non-perceptible state. On update to process state from background to
- // foreground we set target_footprint_ to this value.
+ // foreground we set target_footprint_ and concurrent_start_bytes_ to the corresponding value.
size_t min_foreground_target_footprint_ GUARDED_BY(process_state_update_lock_);
+ size_t min_foreground_concurrent_start_bytes_ GUARDED_BY(process_state_update_lock_);
// When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that
// it completes ahead of an allocation failing.
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 5e8c1e3..55ddd72 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -30,6 +30,10 @@
class HeapTest : public CommonRuntimeTest {
public:
+ HeapTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void SetUp() override {
MemMap::Init();
std::string error_msg;
@@ -100,6 +104,11 @@
}
class ZygoteHeapTest : public CommonRuntimeTest {
+ public:
+ ZygoteHeapTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void SetUpRuntimeOptions(RuntimeOptions* options) override {
CommonRuntimeTest::SetUpRuntimeOptions(options);
options->push_back(std::make_pair("-Xzygote", nullptr));
diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc
index 789a8e3..a7583fe 100644
--- a/runtime/gc/heap_verification_test.cc
+++ b/runtime/gc/heap_verification_test.cc
@@ -33,7 +33,9 @@
class VerificationTest : public CommonRuntimeTest {
protected:
- VerificationTest() {}
+ VerificationTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
template <class T>
ObjPtr<mirror::ObjectArray<T>> AllocObjectArray(Thread* self, size_t length)
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 772174f..f24c942 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -363,9 +363,8 @@
}
void Run(Thread* thread) override {
ScopedObjectAccess soa(thread);
- jvalue args[1];
- args[0].l = cleared_references_;
- InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
+ WellKnownClasses::java_lang_ref_ReferenceQueue_add->InvokeStatic<'V', 'L'>(
+ thread, soa.Decode<mirror::Object>(cleared_references_));
soa.Env()->DeleteGlobalRef(cleared_references_);
}
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
index c680fb5..c8e71b0 100644
--- a/runtime/gc/reference_queue_test.cc
+++ b/runtime/gc/reference_queue_test.cc
@@ -26,7 +26,12 @@
namespace art {
namespace gc {
-class ReferenceQueueTest : public CommonRuntimeTest {};
+class ReferenceQueueTest : public CommonRuntimeTest {
+ protected:
+ ReferenceQueueTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
TEST_F(ReferenceQueueTest, EnqueueDequeue) {
Thread* self = Thread::Current();
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index d2fc884..bba1711 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -41,7 +41,7 @@
// implementation as its intended to be evacuated.
class BumpPointerSpace final : public ContinuousMemMapAllocSpace {
public:
- typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+ using WalkCallback = void (*)(void *, void *, int, void *);
SpaceType GetType() const override {
return kSpaceTypeBumpPointerSpace;
diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h
index 4fc4ada..6041fd0 100644
--- a/runtime/gc/space/dlmalloc_space-inl.h
+++ b/runtime/gc/space/dlmalloc_space-inl.h
@@ -18,7 +18,7 @@
#define ART_RUNTIME_GC_SPACE_DLMALLOC_SPACE_INL_H_
#include "dlmalloc_space.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "thread.h"
namespace art {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 5eee76b..72fce00 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -23,7 +23,9 @@
#include <memory>
#include <random>
#include <string>
+#include <vector>
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
@@ -205,7 +207,6 @@
// Helper class for relocating from one range of memory to another.
class RelocationRange {
public:
- RelocationRange() = default;
RelocationRange(const RelocationRange&) = default;
RelocationRange(uintptr_t source, uintptr_t dest, uintptr_t length)
: source_(source),
@@ -520,7 +521,8 @@
// Check the oat file checksum.
const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
const uint32_t image_oat_checksum = image_header.GetOatChecksum();
- if (oat_checksum != image_oat_checksum) {
+ // Note image_oat_checksum is 0 for images generated by the runtime.
+ if (image_oat_checksum != 0u && oat_checksum != image_oat_checksum) {
*error_msg = StringPrintf("Oat checksum 0x%x does not match the image one 0x%x in image %s",
oat_checksum,
image_oat_checksum,
@@ -1373,9 +1375,9 @@
}
};
-static void AppendImageChecksum(uint32_t component_count,
- uint32_t checksum,
- /*inout*/std::string* checksums) {
+void ImageSpace::AppendImageChecksum(uint32_t component_count,
+ uint32_t checksum,
+ /*inout*/ std::string* checksums) {
static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
StringAppendF(checksums, "i;%u/%08x", component_count, checksum);
}
@@ -1385,7 +1387,7 @@
/*inout*/std::string_view* oat_checksums,
/*out*/std::string* error_msg) {
std::string image_checksum;
- AppendImageChecksum(component_count, checksum, &image_checksum);
+ ImageSpace::AppendImageChecksum(component_count, checksum, &image_checksum);
if (!StartsWith(*oat_checksums, image_checksum)) {
*error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
std::string(*oat_checksums).c_str(),
@@ -1396,182 +1398,6 @@
return true;
}
-// Helper class to find the primary boot image and boot image extensions
-// and determine the boot image layout.
-class ImageSpace::BootImageLayout {
- public:
- // Description of a "chunk" of the boot image, i.e. either primary boot image
- // or a boot image extension, used in conjunction with the boot class path to
- // load boot image components.
- struct ImageChunk {
- std::string base_location;
- std::string base_filename;
- std::vector<std::string> profile_files;
- size_t start_index;
- uint32_t component_count;
- uint32_t image_space_count;
- uint32_t reservation_size;
- uint32_t checksum;
- uint32_t boot_image_component_count;
- uint32_t boot_image_checksum;
- uint32_t boot_image_size;
-
- // The following file descriptors hold the memfd files for extensions compiled
- // in memory and described by the above fields. We want to use them to mmap()
- // the contents and then close them while treating the ImageChunk description
- // as immutable (const), so make these fields explicitly mutable.
- mutable android::base::unique_fd art_fd;
- mutable android::base::unique_fd vdex_fd;
- mutable android::base::unique_fd oat_fd;
- };
-
- BootImageLayout(ArrayRef<const std::string> image_locations,
- ArrayRef<const std::string> boot_class_path,
- ArrayRef<const std::string> boot_class_path_locations,
- ArrayRef<const int> boot_class_path_fds,
- ArrayRef<const int> boot_class_path_image_fds,
- ArrayRef<const int> boot_class_path_vdex_fds,
- ArrayRef<const int> boot_class_path_oat_fds)
- : image_locations_(image_locations),
- boot_class_path_(boot_class_path),
- boot_class_path_locations_(boot_class_path_locations),
- boot_class_path_fds_(boot_class_path_fds),
- boot_class_path_image_fds_(boot_class_path_image_fds),
- boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
- boot_class_path_oat_fds_(boot_class_path_oat_fds) {}
-
- std::string GetPrimaryImageLocation();
-
- bool LoadFromSystem(InstructionSet image_isa, /*out*/std::string* error_msg) {
- return LoadOrValidateFromSystem(image_isa, /*oat_checksums=*/ nullptr, error_msg);
- }
-
- bool ValidateFromSystem(InstructionSet image_isa,
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg) {
- DCHECK(oat_checksums != nullptr);
- return LoadOrValidateFromSystem(image_isa, oat_checksums, error_msg);
- }
-
- ArrayRef<const ImageChunk> GetChunks() const {
- return ArrayRef<const ImageChunk>(chunks_);
- }
-
- uint32_t GetBaseAddress() const {
- return base_address_;
- }
-
- size_t GetNextBcpIndex() const {
- return next_bcp_index_;
- }
-
- size_t GetTotalComponentCount() const {
- return total_component_count_;
- }
-
- size_t GetTotalReservationSize() const {
- return total_reservation_size_;
- }
-
- private:
- struct NamedComponentLocation {
- std::string base_location;
- size_t bcp_index;
- std::vector<std::string> profile_filenames;
- };
-
- std::string ExpandLocationImpl(const std::string& location,
- size_t bcp_index,
- bool boot_image_extension) {
- std::vector<std::string> expanded = ExpandMultiImageLocations(
- ArrayRef<const std::string>(boot_class_path_).SubArray(bcp_index, 1u),
- location,
- boot_image_extension);
- DCHECK_EQ(expanded.size(), 1u);
- return expanded[0];
- }
-
- std::string ExpandLocation(const std::string& location, size_t bcp_index) {
- if (bcp_index == 0u) {
- DCHECK_EQ(location, ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ false));
- return location;
- } else {
- return ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ true);
- }
- }
-
- std::string GetBcpComponentPath(size_t bcp_index) {
- DCHECK_LE(bcp_index, boot_class_path_.size());
- size_t bcp_slash_pos = boot_class_path_[bcp_index].rfind('/');
- DCHECK_NE(bcp_slash_pos, std::string::npos);
- return boot_class_path_[bcp_index].substr(0u, bcp_slash_pos + 1u);
- }
-
- bool VerifyImageLocation(ArrayRef<const std::string> components,
- /*out*/size_t* named_components_count,
- /*out*/std::string* error_msg);
-
- bool MatchNamedComponents(
- ArrayRef<const std::string> named_components,
- /*out*/std::vector<NamedComponentLocation>* named_component_locations,
- /*out*/std::string* error_msg);
-
- bool ValidateBootImageChecksum(const char* file_description,
- const ImageHeader& header,
- /*out*/std::string* error_msg);
-
- bool ValidateHeader(const ImageHeader& header,
- size_t bcp_index,
- const char* file_description,
- /*out*/std::string* error_msg);
-
- bool ValidateOatFile(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- size_t component_count,
- /*out*/std::string* error_msg);
-
- bool ReadHeader(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- /*out*/std::string* error_msg);
-
- // Compiles a consecutive subsequence of bootclasspath dex files, whose contents are included in
- // the profiles specified by `profile_filenames`, starting from `bcp_index`.
- bool CompileBootclasspathElements(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- const std::vector<std::string>& profile_filenames,
- ArrayRef<const std::string> dependencies,
- /*out*/std::string* error_msg);
-
- bool CheckAndRemoveLastChunkChecksum(/*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg);
-
- template <typename FilenameFn>
- bool LoadOrValidate(FilenameFn&& filename_fn,
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg);
-
- bool LoadOrValidateFromSystem(InstructionSet image_isa,
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg);
-
- ArrayRef<const std::string> image_locations_;
- ArrayRef<const std::string> boot_class_path_;
- ArrayRef<const std::string> boot_class_path_locations_;
- ArrayRef<const int> boot_class_path_fds_;
- ArrayRef<const int> boot_class_path_image_fds_;
- ArrayRef<const int> boot_class_path_vdex_fds_;
- ArrayRef<const int> boot_class_path_oat_fds_;
-
- std::vector<ImageChunk> chunks_;
- uint32_t base_address_ = 0u;
- size_t next_bcp_index_ = 0u;
- size_t total_component_count_ = 0u;
- size_t total_reservation_size_ = 0u;
-};
-
std::string ImageSpace::BootImageLayout::GetPrimaryImageLocation() {
DCHECK(!image_locations_.empty());
std::string location = image_locations_[0];
@@ -1893,7 +1719,7 @@
error_msg->c_str());
return false;
}
- if (!ImageSpace::ValidateOatFile(*oat_file, error_msg, dex_filenames, dex_fds)) {
+ if (!ImageSpace::ValidateOatFile(*oat_file, error_msg, dex_filenames, dex_fds, apex_versions_)) {
return false;
}
return true;
@@ -1929,29 +1755,12 @@
return false;
}
- bool validate_oat_files = true;
- Runtime* runtime = Runtime::Current();
- if (runtime != nullptr && runtime->GetHeap() != nullptr) {
- for (const ImageSpace* image_space : runtime->GetHeap()->GetBootImageSpaces()) {
- if (image_space->GetComponentCount() == header.GetImageSpaceCount() &&
- image_space->GetImageHeader().GetImageChecksum() == header.GetImageChecksum()) {
- // This image has been loaded by the runtime, meaning that the oat files were validated when
- // the runtime loaded them, so we don't have to validate them again.
- validate_oat_files = false;
- break;
- }
- }
- }
-
- if (validate_oat_files) {
- // Validate oat files. We do it here so that the boot image will be re-compiled in memory if
- // it's outdated.
- size_t component_count = (header.GetImageSpaceCount() == 1u) ? header.GetComponentCount() : 1u;
- for (size_t i = 0; i < header.GetImageSpaceCount(); i++) {
- if (!ValidateOatFile(
- base_location, base_filename, bcp_index + i, component_count, error_msg)) {
- return false;
- }
+ // Validate oat files. We do it here so that the boot image will be re-compiled in memory if it's
+ // outdated.
+ size_t component_count = (header.GetImageSpaceCount() == 1u) ? header.GetComponentCount() : 1u;
+ for (size_t i = 0; i < header.GetImageSpaceCount(); i++) {
+ if (!ValidateOatFile(base_location, base_filename, bcp_index + i, component_count, error_msg)) {
+ return false;
}
}
@@ -2175,48 +1984,12 @@
return true;
}
-bool ImageSpace::BootImageLayout::CheckAndRemoveLastChunkChecksum(
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg) {
- DCHECK(oat_checksums != nullptr);
- DCHECK(!chunks_.empty());
- const ImageChunk& chunk = chunks_.back();
- size_t component_count = chunk.component_count;
- size_t checksum = chunk.checksum;
- if (!CheckAndRemoveImageChecksum(component_count, checksum, oat_checksums, error_msg)) {
- DCHECK(!error_msg->empty());
- return false;
- }
- if (oat_checksums->empty()) {
- if (next_bcp_index_ != boot_class_path_.size()) {
- *error_msg = StringPrintf("Checksum too short, missing %zu components.",
- boot_class_path_.size() - next_bcp_index_);
- return false;
- }
- return true;
- }
- if (!StartsWith(*oat_checksums, ":")) {
- *error_msg = StringPrintf("Missing ':' separator at start of %s",
- std::string(*oat_checksums).c_str());
- return false;
- }
- oat_checksums->remove_prefix(1u);
- if (oat_checksums->empty()) {
- *error_msg = "Missing checksums after the ':' separator.";
- return false;
- }
- return true;
-}
-
template <typename FilenameFn>
-bool ImageSpace::BootImageLayout::LoadOrValidate(FilenameFn&& filename_fn,
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg) {
+bool ImageSpace::BootImageLayout::Load(FilenameFn&& filename_fn,
+ bool allow_in_memory_compilation,
+ /*out*/ std::string* error_msg) {
DCHECK(GetChunks().empty());
DCHECK_EQ(GetBaseAddress(), 0u);
- bool validate = (oat_checksums != nullptr);
- static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
- DCHECK_IMPLIES(validate, StartsWith(*oat_checksums, "i"));
ArrayRef<const std::string> components = image_locations_;
size_t named_components_count = 0u;
@@ -2247,24 +2020,21 @@
LOG(ERROR) << "Named image component already covered by previous image: " << base_location;
continue;
}
- if (validate && bcp_index > bcp_pos) {
- *error_msg = StringPrintf("End of contiguous boot class path images, remaining checksum: %s",
- std::string(*oat_checksums).c_str());
- return false;
- }
std::string local_error_msg;
- std::string* err_msg = validate ? error_msg : &local_error_msg;
std::string base_filename;
- if (!filename_fn(base_location, &base_filename, err_msg) ||
- !ReadHeader(base_location, base_filename, bcp_index, err_msg)) {
- if (validate) {
- return false;
- }
+ if (!filename_fn(base_location, &base_filename, &local_error_msg) ||
+ !ReadHeader(base_location, base_filename, bcp_index, &local_error_msg)) {
LOG(ERROR) << "Error reading named image component header for " << base_location
<< ", error: " << local_error_msg;
// If the primary boot image is invalid, we generate a single full image. This is faster than
// generating the primary boot image and the extension separately.
if (bcp_index == 0) {
+ if (!allow_in_memory_compilation) {
+ // The boot image is unusable and we can't continue by generating a boot image in memory.
+ // All we can do is to return.
+ *error_msg = std::move(local_error_msg);
+ return false;
+ }
// We must at least have profiles for the core libraries.
if (profile_filenames.empty()) {
*error_msg = "Full boot image cannot be compiled because no profile is provided.";
@@ -2288,14 +2058,15 @@
// No extensions are needed.
return true;
}
- if (profile_filenames.empty() ||
+ bool should_compile_extension = allow_in_memory_compilation && !profile_filenames.empty();
+ if (!should_compile_extension ||
!CompileBootclasspathElements(base_location,
base_filename,
bcp_index,
profile_filenames,
components.SubArray(/*pos=*/ 0, /*length=*/ 1),
&local_error_msg)) {
- if (!profile_filenames.empty()) {
+ if (should_compile_extension) {
LOG(ERROR) << "Error compiling boot image extension for " << boot_class_path_[bcp_index]
<< ", error: " << local_error_msg;
}
@@ -2304,14 +2075,6 @@
continue;
}
}
- if (validate) {
- if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
- return false;
- }
- if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
- return true; // Let the caller deal with the dex file checksums if any.
- }
- }
bcp_pos = GetNextBcpIndex();
}
@@ -2344,24 +2107,10 @@
VLOG(image) << "Found image extension for " << ExpandLocation(base_location, bcp_pos);
bcp_pos = GetNextBcpIndex();
found = true;
- if (validate) {
- if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
- return false;
- }
- if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
- return true; // Let the caller deal with the dex file checksums if any.
- }
- }
break;
}
}
if (!found) {
- if (validate) {
- *error_msg = StringPrintf("Missing extension for %s, remaining checksum: %s",
- bcp_component.c_str(),
- std::string(*oat_checksums).c_str());
- return false;
- }
++bcp_pos;
}
}
@@ -2370,16 +2119,16 @@
return true;
}
-bool ImageSpace::BootImageLayout::LoadOrValidateFromSystem(InstructionSet image_isa,
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg) {
+bool ImageSpace::BootImageLayout::LoadFromSystem(InstructionSet image_isa,
+ bool allow_in_memory_compilation,
+ /*out*/ std::string* error_msg) {
auto filename_fn = [image_isa](const std::string& location,
/*out*/std::string* filename,
/*out*/std::string* err_msg ATTRIBUTE_UNUSED) {
*filename = GetSystemImageFilename(location.c_str(), image_isa);
return true;
};
- return LoadOrValidate(filename_fn, oat_checksums, error_msg);
+ return Load(filename_fn, allow_in_memory_compilation, error_msg);
}
class ImageSpace::BootImageLoader {
@@ -2427,6 +2176,7 @@
bool HasSystem() const { return has_system_; }
bool LoadFromSystem(size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -2721,7 +2471,8 @@
int32_t class_roots_index = enum_cast<int32_t>(ImageHeader::kClassRoots);
DCHECK_LT(class_roots_index, image_roots->GetLength<kVerifyNone>());
class_roots = ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
- image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
+ image_roots->GetWithoutChecks<kVerifyNone,
+ kWithoutReadBarrier>(class_roots_index).Ptr()));
if (kExtension) {
// Class roots must have been visited if we relocated the primary boot image.
DCHECK(base_diff == 0 || patched_objects->Test(class_roots.Ptr()));
@@ -2887,6 +2638,14 @@
DCHECK_EQ(base_diff64, 0);
}
+ // While `Thread::Current()` is null, the `ScopedDebugDisallowReadBarriers`
+ // cannot be used but the class `ReadBarrier` shall not allow read barriers anyway.
+ // For some gtests we actually have an initialized `Thread:Current()`.
+ std::optional<ScopedDebugDisallowReadBarriers> sddrb(std::nullopt);
+ if (kCheckDebugDisallowReadBarrierCount && Thread::Current() != nullptr) {
+ sddrb.emplace(Thread::Current());
+ }
+
ArrayRef<const std::unique_ptr<ImageSpace>> spaces_ref(spaces);
PointerSize pointer_size = first_space_header.GetPointerSize();
if (pointer_size == PointerSize::k64) {
@@ -3135,8 +2894,24 @@
return false;
}
}
+
+ // As an optimization, madvise the oat file into memory if it's being used
+ // for execution with an active runtime. This can significantly improve
+ // ZygoteInit class preload performance.
+ if (executable_) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr) {
+ Runtime::MadviseFileForRange(runtime->GetMadviseWillNeedSizeOdex(),
+ oat_file->Size(),
+ oat_file->Begin(),
+ oat_file->End(),
+ oat_file->GetLocation());
+ }
+ }
+
space->oat_file_ = std::move(oat_file);
space->oat_file_non_owned_ = space->oat_file_.get();
+
return true;
}
@@ -3369,6 +3144,7 @@
bool ImageSpace::BootImageLoader::LoadFromSystem(
size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
/*out*/std::string* error_msg) {
@@ -3381,7 +3157,7 @@
boot_class_path_image_fds_,
boot_class_path_vdex_fds_,
boot_class_path_oat_fds_);
- if (!layout.LoadFromSystem(image_isa_, error_msg)) {
+ if (!layout.LoadFromSystem(image_isa_, allow_in_memory_compilation, error_msg)) {
return false;
}
@@ -3444,6 +3220,7 @@
bool relocate,
bool executable,
size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) {
ScopedTrace trace(__FUNCTION__);
@@ -3474,8 +3251,11 @@
std::vector<std::string> error_msgs;
std::string error_msg;
- if (loader.LoadFromSystem(
- extra_reservation_size, boot_image_spaces, extra_reservation, &error_msg)) {
+ if (loader.LoadFromSystem(extra_reservation_size,
+ allow_in_memory_compilation,
+ boot_image_spaces,
+ extra_reservation,
+ &error_msg)) {
return true;
}
error_msgs.push_back(error_msg);
@@ -3543,7 +3323,9 @@
<< ",name=\"" << GetName() << "\"]";
}
-bool ImageSpace::ValidateApexVersions(const OatFile& oat_file, std::string* error_msg) {
+bool ImageSpace::ValidateApexVersions(const OatFile& oat_file,
+ const std::string& apex_versions,
+ std::string* error_msg) {
// For a boot image, the key value store only exists in the first OAT file. Skip other OAT files.
if (oat_file.GetOatHeader().GetKeyValueStoreSize() == 0) {
return true;
@@ -3566,27 +3348,33 @@
// For a boot image, it can be generated from a subset of the bootclasspath.
// For an app image, some dex files get compiled with a subset of the bootclasspath.
// For such cases, the OAT APEX versions will be a prefix of the runtime APEX versions.
- if (!android::base::StartsWith(Runtime::Current()->GetApexVersions(), oat_apex_versions)) {
+ if (!android::base::StartsWith(apex_versions, oat_apex_versions)) {
*error_msg = StringPrintf(
"ValidateApexVersions found APEX versions mismatch between oat file '%s' and the runtime "
"(Oat file: '%s', Runtime: '%s')",
oat_file.GetLocation().c_str(),
oat_apex_versions,
- Runtime::Current()->GetApexVersions().c_str());
+ apex_versions.c_str());
return false;
}
return true;
}
bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
- return ValidateOatFile(oat_file, error_msg, ArrayRef<const std::string>(), ArrayRef<const int>());
+ DCHECK(Runtime::Current() != nullptr);
+ return ValidateOatFile(oat_file,
+ error_msg,
+ ArrayRef<const std::string>(),
+ ArrayRef<const int>(),
+ Runtime::Current()->GetApexVersions());
}
bool ImageSpace::ValidateOatFile(const OatFile& oat_file,
std::string* error_msg,
ArrayRef<const std::string> dex_filenames,
- ArrayRef<const int> dex_fds) {
- if (!ValidateApexVersions(oat_file, error_msg)) {
+ ArrayRef<const int> dex_fds,
+ const std::string& apex_versions) {
+ if (!ValidateApexVersions(oat_file, apex_versions, error_msg)) {
return false;
}
@@ -3728,7 +3516,7 @@
return n;
}
-static size_t CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
+size_t ImageSpace::CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
ArrayRef<const std::string> boot_class_path,
/*out*/std::string* error_msg) {
// Check that the oat BCP is a prefix of current BCP locations and count components.
@@ -3760,110 +3548,6 @@
return component_count;
}
-bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums,
- std::string_view oat_boot_class_path,
- ArrayRef<const std::string> image_locations,
- ArrayRef<const std::string> boot_class_path_locations,
- ArrayRef<const std::string> boot_class_path,
- ArrayRef<const int> boot_class_path_fds,
- InstructionSet image_isa,
- /*out*/std::string* error_msg) {
- if (oat_checksums.empty() || oat_boot_class_path.empty()) {
- *error_msg = oat_checksums.empty() ? "Empty checksums." : "Empty boot class path.";
- return false;
- }
-
- DCHECK_EQ(boot_class_path_locations.size(), boot_class_path.size());
- size_t bcp_size =
- CheckAndCountBCPComponents(oat_boot_class_path, boot_class_path_locations, error_msg);
- if (bcp_size == static_cast<size_t>(-1)) {
- DCHECK(!error_msg->empty());
- return false;
- }
-
- size_t bcp_pos = 0u;
- if (StartsWith(oat_checksums, "i")) {
- // Use only the matching part of the BCP for validation. FDs are optional, so only pass the
- // sub-array if provided.
- ArrayRef<const int> bcp_fds = boot_class_path_fds.empty()
- ? ArrayRef<const int>()
- : boot_class_path_fds.SubArray(/*pos=*/ 0u, bcp_size);
- BootImageLayout layout(image_locations,
- boot_class_path.SubArray(/*pos=*/ 0u, bcp_size),
- boot_class_path_locations.SubArray(/*pos=*/ 0u, bcp_size),
- bcp_fds,
- /*boot_class_path_image_fds=*/ ArrayRef<const int>(),
- /*boot_class_path_vdex_fds=*/ ArrayRef<const int>(),
- /*boot_class_path_oat_fds=*/ ArrayRef<const int>());
- std::string primary_image_location = layout.GetPrimaryImageLocation();
- std::string system_filename;
- bool has_system = false;
- if (!FindImageFilename(primary_image_location.c_str(),
- image_isa,
- &system_filename,
- &has_system)) {
- *error_msg = StringPrintf("Unable to find image file for %s and %s",
- android::base::Join(image_locations, kComponentSeparator).c_str(),
- GetInstructionSetString(image_isa));
- return false;
- }
-
- DCHECK(has_system);
- if (!layout.ValidateFromSystem(image_isa, &oat_checksums, error_msg)) {
- return false;
- }
- bcp_pos = layout.GetNextBcpIndex();
- }
-
- for ( ; bcp_pos != bcp_size; ++bcp_pos) {
- static_assert(ImageSpace::kDexFileChecksumPrefix == 'd', "Format prefix check.");
- if (!StartsWith(oat_checksums, "d")) {
- *error_msg = StringPrintf("Missing dex checksums, expected %s to start with 'd'",
- std::string(oat_checksums).c_str());
- return false;
- }
- oat_checksums.remove_prefix(1u);
-
- const std::string& bcp_filename = boot_class_path[bcp_pos];
- std::vector<uint32_t> checksums;
- std::vector<std::string> dex_locations;
- const ArtDexFileLoader dex_file_loader;
- if (!dex_file_loader.GetMultiDexChecksums(bcp_filename.c_str(),
- &checksums,
- &dex_locations,
- error_msg)) {
- return false;
- }
- DCHECK(!checksums.empty());
- for (uint32_t checksum : checksums) {
- std::string dex_file_checksum = StringPrintf("/%08x", checksum);
- if (!StartsWith(oat_checksums, dex_file_checksum)) {
- *error_msg = StringPrintf(
- "Dex checksum mismatch for bootclasspath file %s, expected %s to start with %s",
- bcp_filename.c_str(),
- std::string(oat_checksums).c_str(),
- dex_file_checksum.c_str());
- return false;
- }
- oat_checksums.remove_prefix(dex_file_checksum.size());
- }
- if (bcp_pos + 1u != bcp_size) {
- if (!StartsWith(oat_checksums, ":")) {
- *error_msg = StringPrintf("Missing ':' separator at start of %s",
- std::string(oat_checksums).c_str());
- return false;
- }
- oat_checksums.remove_prefix(1u);
- }
- }
- if (!oat_checksums.empty()) {
- *error_msg = StringPrintf("Checksum too long, unexpected tail %s",
- std::string(oat_checksums).c_str());
- return false;
- }
- return true;
-}
-
bool ImageSpace::VerifyBootClassPathChecksums(
std::string_view oat_checksums,
std::string_view oat_boot_class_path,
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 8a93f2b..58030de 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -17,13 +17,15 @@
#ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
#define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
+#include "android-base/unique_fd.h"
+#include "base/array_ref.h"
#include "gc/accounting/space_bitmap.h"
#include "image.h"
+#include "runtime.h"
#include "space.h"
namespace art {
-template <typename T> class ArrayRef;
class DexFile;
enum class InstructionSet;
class OatFile;
@@ -142,6 +144,7 @@
bool relocate,
bool executable,
size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -239,18 +242,6 @@
// Returns the total number of components (jar files) associated with the image spaces.
static size_t GetNumberOfComponents(ArrayRef<gc::space::ImageSpace* const> image_spaces);
- // Returns whether the checksums are valid for the given boot class path,
- // image location and ISA (may differ from the ISA of an initialized Runtime).
- // The boot image and dex files do not need to be loaded in memory.
- static bool VerifyBootClassPathChecksums(std::string_view oat_checksums,
- std::string_view oat_boot_class_path,
- ArrayRef<const std::string> image_locations,
- ArrayRef<const std::string> boot_class_path_locations,
- ArrayRef<const std::string> boot_class_path,
- ArrayRef<const int> boot_class_path_fds,
- InstructionSet image_isa,
- /*out*/std::string* error_msg);
-
// Returns whether the oat checksums and boot class path description are valid
// for the given boot image spaces and boot class path. Used for boot image extensions.
static bool VerifyBootClassPathChecksums(
@@ -267,8 +258,10 @@
const std::string& image_location,
bool boot_image_extension = false);
- // Returns true if the APEX versions in the OAT file match the current APEX versions.
- static bool ValidateApexVersions(const OatFile& oat_file, std::string* error_msg);
+ // Returns true if the APEX versions in the OAT file match the given APEX versions.
+ static bool ValidateApexVersions(const OatFile& oat_file,
+ const std::string& apex_versions,
+ std::string* error_msg);
// Returns true if the dex checksums in the given oat file match the
// checksums of the original dex files on disk. This is intended to be used
@@ -279,17 +272,23 @@
// oat and odex file.
//
// This function is exposed for testing purposes.
+ //
+ // Calling this function requires an active runtime.
static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg);
// Same as above, but allows to use `dex_filenames` and `dex_fds` to find the dex files instead of
- // using the dex filenames in the header of the oat file. This overload is useful when the actual
- // dex filenames are different from what's in the header (e.g., when we run dex2oat on host), or
- // when the runtime can only access files through FDs (e.g., when we run dex2oat on target in a
- // restricted SELinux domain).
+ // using the dex filenames in the header of the oat file, and also takes `apex_versions` from the
+ // input. This overload is useful when the actual dex filenames are different from what's in the
+ // header (e.g., when we run dex2oat on host), when the runtime can only access files through FDs
+ // (e.g., when we run dex2oat on target in a restricted SELinux domain), or when there is no
+ // active runtime.
+ //
+ // Calling this function does not require an active runtime.
static bool ValidateOatFile(const OatFile& oat_file,
std::string* error_msg,
ArrayRef<const std::string> dex_filenames,
- ArrayRef<const int> dex_fds);
+ ArrayRef<const int> dex_fds,
+ const std::string& apex_versions);
// Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
uint8_t* GetImageEnd() const {
@@ -303,6 +302,182 @@
void ReleaseMetadata() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void AppendImageChecksum(uint32_t component_count,
+ uint32_t checksum,
+ /*inout*/ std::string* checksums);
+
+ static size_t CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
+ ArrayRef<const std::string> boot_class_path,
+ /*out*/ std::string* error_msg);
+
+ // Helper class to find the primary boot image and boot image extensions
+ // and determine the boot image layout.
+ class BootImageLayout {
+ public:
+ // Description of a "chunk" of the boot image, i.e. either primary boot image
+ // or a boot image extension, used in conjunction with the boot class path to
+ // load boot image components.
+ struct ImageChunk {
+ std::string base_location;
+ std::string base_filename;
+ std::vector<std::string> profile_files;
+ size_t start_index;
+ uint32_t component_count;
+ uint32_t image_space_count;
+ uint32_t reservation_size;
+ uint32_t checksum;
+ uint32_t boot_image_component_count;
+ uint32_t boot_image_checksum;
+ uint32_t boot_image_size;
+
+ // The following file descriptors hold the memfd files for extensions compiled
+ // in memory and described by the above fields. We want to use them to mmap()
+ // the contents and then close them while treating the ImageChunk description
+ // as immutable (const), so make these fields explicitly mutable.
+ mutable android::base::unique_fd art_fd;
+ mutable android::base::unique_fd vdex_fd;
+ mutable android::base::unique_fd oat_fd;
+ };
+
+ BootImageLayout(ArrayRef<const std::string> image_locations,
+ ArrayRef<const std::string> boot_class_path,
+ ArrayRef<const std::string> boot_class_path_locations,
+ ArrayRef<const int> boot_class_path_fds,
+ ArrayRef<const int> boot_class_path_image_fds,
+ ArrayRef<const int> boot_class_path_vdex_fds,
+ ArrayRef<const int> boot_class_path_oat_fds,
+ const std::string* apex_versions = nullptr)
+ : image_locations_(image_locations),
+ boot_class_path_(boot_class_path),
+ boot_class_path_locations_(boot_class_path_locations),
+ boot_class_path_fds_(boot_class_path_fds),
+ boot_class_path_image_fds_(boot_class_path_image_fds),
+ boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
+ boot_class_path_oat_fds_(boot_class_path_oat_fds),
+ apex_versions_(GetApexVersions(apex_versions)) {}
+
+ std::string GetPrimaryImageLocation();
+
+ bool LoadFromSystem(InstructionSet image_isa,
+ bool allow_in_memory_compilation,
+ /*out*/ std::string* error_msg);
+
+ ArrayRef<const ImageChunk> GetChunks() const { return ArrayRef<const ImageChunk>(chunks_); }
+
+ uint32_t GetBaseAddress() const { return base_address_; }
+
+ size_t GetNextBcpIndex() const { return next_bcp_index_; }
+
+ size_t GetTotalComponentCount() const { return total_component_count_; }
+
+ size_t GetTotalReservationSize() const { return total_reservation_size_; }
+
+ private:
+ struct NamedComponentLocation {
+ std::string base_location;
+ size_t bcp_index;
+ std::vector<std::string> profile_filenames;
+ };
+
+ std::string ExpandLocationImpl(const std::string& location,
+ size_t bcp_index,
+ bool boot_image_extension) {
+ std::vector<std::string> expanded = ExpandMultiImageLocations(
+ ArrayRef<const std::string>(boot_class_path_).SubArray(bcp_index, 1u),
+ location,
+ boot_image_extension);
+ DCHECK_EQ(expanded.size(), 1u);
+ return expanded[0];
+ }
+
+ std::string ExpandLocation(const std::string& location, size_t bcp_index) {
+ if (bcp_index == 0u) {
+ DCHECK_EQ(location,
+ ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/false));
+ return location;
+ } else {
+ return ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/true);
+ }
+ }
+
+ std::string GetBcpComponentPath(size_t bcp_index) {
+ DCHECK_LE(bcp_index, boot_class_path_.size());
+ size_t bcp_slash_pos = boot_class_path_[bcp_index].rfind('/');
+ DCHECK_NE(bcp_slash_pos, std::string::npos);
+ return boot_class_path_[bcp_index].substr(0u, bcp_slash_pos + 1u);
+ }
+
+ bool VerifyImageLocation(ArrayRef<const std::string> components,
+ /*out*/ size_t* named_components_count,
+ /*out*/ std::string* error_msg);
+
+ bool MatchNamedComponents(
+ ArrayRef<const std::string> named_components,
+ /*out*/ std::vector<NamedComponentLocation>* named_component_locations,
+ /*out*/ std::string* error_msg);
+
+ bool ValidateBootImageChecksum(const char* file_description,
+ const ImageHeader& header,
+ /*out*/ std::string* error_msg);
+
+ bool ValidateHeader(const ImageHeader& header,
+ size_t bcp_index,
+ const char* file_description,
+ /*out*/ std::string* error_msg);
+
+ bool ValidateOatFile(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ size_t component_count,
+ /*out*/ std::string* error_msg);
+
+ bool ReadHeader(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ /*out*/ std::string* error_msg);
+
+ // Compiles a consecutive subsequence of bootclasspath dex files, whose contents are included in
+ // the profiles specified by `profile_filenames`, starting from `bcp_index`.
+ bool CompileBootclasspathElements(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ const std::vector<std::string>& profile_filenames,
+ ArrayRef<const std::string> dependencies,
+ /*out*/ std::string* error_msg);
+
+ // Returns true if a least one chuck has been loaded.
+ template <typename FilenameFn>
+ bool Load(FilenameFn&& filename_fn,
+ bool allow_in_memory_compilation,
+ /*out*/ std::string* error_msg);
+
+ // This function prefers taking APEX versions from the input instead of from the runtime if
+ // possible. If the input is present, `ValidateFromSystem` can work without an active runtime.
+ static const std::string& GetApexVersions(const std::string* apex_versions) {
+ if (apex_versions == nullptr) {
+ DCHECK(Runtime::Current() != nullptr);
+ return Runtime::Current()->GetApexVersions();
+ } else {
+ return *apex_versions;
+ }
+ }
+
+ ArrayRef<const std::string> image_locations_;
+ ArrayRef<const std::string> boot_class_path_;
+ ArrayRef<const std::string> boot_class_path_locations_;
+ ArrayRef<const int> boot_class_path_fds_;
+ ArrayRef<const int> boot_class_path_image_fds_;
+ ArrayRef<const int> boot_class_path_vdex_fds_;
+ ArrayRef<const int> boot_class_path_oat_fds_;
+
+ std::vector<ImageChunk> chunks_;
+ uint32_t base_address_ = 0u;
+ size_t next_bcp_index_ = 0u;
+ size_t total_component_count_ = 0u;
+ size_t total_reservation_size_ = 0u;
+ const std::string& apex_versions_;
+ };
+
protected:
// Tries to initialize an ImageSpace from the given image path, returning null on error.
//
@@ -342,7 +517,6 @@
friend class Space;
private:
- class BootImageLayout;
class BootImageLoader;
template <typename ReferenceVisitor>
class ClassTableVisitor;
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index 3a6d0e1..b85560c 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -145,6 +145,7 @@
/*relocate=*/ false,
/*executable=*/ true,
/*extra_reservation_size=*/ 0u,
+ /*allow_in_memory_compilation=*/ false,
&boot_image_spaces,
&extra_reservation);
};
@@ -321,56 +322,6 @@
EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
}
-TEST_F(DexoptTest, Checksums) {
- Runtime* runtime = Runtime::Current();
- ASSERT_TRUE(runtime != nullptr);
- ASSERT_FALSE(runtime->GetHeap()->GetBootImageSpaces().empty());
-
- std::vector<std::string> bcp = runtime->GetBootClassPath();
- std::vector<std::string> bcp_locations = runtime->GetBootClassPathLocations();
- std::vector<const DexFile*> dex_files = runtime->GetClassLinker()->GetBootClassPath();
-
- std::string error_msg;
- auto create_and_verify = [&]() {
- std::string checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
- ArrayRef<gc::space::ImageSpace* const>(runtime->GetHeap()->GetBootImageSpaces()),
- ArrayRef<const DexFile* const>(dex_files));
- return gc::space::ImageSpace::VerifyBootClassPathChecksums(
- checksums,
- android::base::Join(bcp_locations, ':'),
- ArrayRef<const std::string>(runtime->GetImageLocations()),
- ArrayRef<const std::string>(bcp_locations),
- ArrayRef<const std::string>(bcp),
- /*boot_class_path_fds=*/ ArrayRef<const int>(),
- kRuntimeISA,
- &error_msg);
- };
-
- ASSERT_TRUE(create_and_verify()) << error_msg;
-
- std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
- for (const std::string& src : { GetDexSrc1(), GetDexSrc2() }) {
- std::vector<std::unique_ptr<const DexFile>> new_dex_files;
- const ArtDexFileLoader dex_file_loader;
- ASSERT_TRUE(dex_file_loader.Open(src.c_str(),
- src,
- /*verify=*/ true,
- /*verify_checksum=*/ false,
- &error_msg,
- &new_dex_files))
- << error_msg;
-
- bcp.push_back(src);
- bcp_locations.push_back(src);
- for (std::unique_ptr<const DexFile>& df : new_dex_files) {
- dex_files.push_back(df.get());
- opened_dex_files.push_back(std::move(df));
- }
-
- ASSERT_TRUE(create_and_verify()) << error_msg;
- }
-}
-
template <bool kImage, bool kRelocate>
class ImageSpaceLoadingTest : public CommonRuntimeTest {
protected:
@@ -380,6 +331,7 @@
options->emplace_back(android::base::StringPrintf("-Ximage:%s", image_location.c_str()),
nullptr);
options->emplace_back(kRelocate ? "-Xrelocate" : "-Xnorelocate", nullptr);
+ options->emplace_back("-Xallowinmemorycompilation", nullptr);
// We want to test the relocation behavior of ImageSpace. As such, don't pretend we're a
// compiler.
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 5000656..59ab3f3 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -38,7 +38,7 @@
// A common parent of DlMallocSpace and RosAllocSpace.
class MallocSpace : public ContinuousMemMapAllocSpace {
public:
- typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+ using WalkCallback = void (*)(void *start, void *end, size_t num_bytes, void* callback_arg);
SpaceType GetType() const override {
return kSpaceTypeMallocSpace;
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 171c5cd..60141d6 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -36,7 +36,7 @@
static constexpr bool kProtectClearedRegions = kIsDebugBuild;
// Wether we poison memory areas occupied by dead objects in unevacuated regions.
-static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = true;
+static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = kIsDebugBuild;
// Special 32-bit value used to poison memory areas occupied by dead
// objects in unevacuated regions. Dereferencing this value is expected
@@ -741,10 +741,19 @@
max_contiguous_allocation = std::min(max_contiguous_allocation,
regions_free_for_alloc * kRegionSize);
if (failed_alloc_bytes > max_contiguous_allocation) {
+ // Region space does not normally fragment in the conventional sense. However we can run out
+ // of region space prematurely if we have many threads, each with a partially committed TLAB.
+ // The whole TLAB uses up region address space, but we only count the section that was
+ // actually given to the thread so far as allocated. For unlikely allocation request sequences
+ // involving largish objects that don't qualify for large objects space, we may also be unable
+ // to fully utilize entire TLABs, and thus generate enough actual fragmentation to get
+ // here. This appears less likely, since we usually reuse sufficiently large TLAB "tails"
+ // that are no longer needed.
os << "; failed due to fragmentation (largest possible contiguous allocation "
- << max_contiguous_allocation << " bytes). Number of "
- << PrettySize(kRegionSize)
- << " sized free regions are: " << regions_free_for_alloc;
+ << max_contiguous_allocation << " bytes). Number of " << PrettySize(kRegionSize)
+ << " sized free regions are: " << regions_free_for_alloc
+ << ". Likely cause: (1) Too much memory in use, and "
+ << "(2) many threads or many larger objects of the wrong kind";
return true;
}
// Caller's job to print failed_alloc_bytes.
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index 1463eb7..27b9e9c 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -46,7 +46,7 @@
// A space that consists of equal-sized regions.
class RegionSpace final : public ContinuousMemMapAllocSpace {
public:
- typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+ using WalkCallback = void (*)(void *start, void *end, size_t num_bytes, void* callback_arg);
enum EvacMode {
kEvacModeNewlyAllocated,
diff --git a/runtime/gc/system_weak_test.cc b/runtime/gc/system_weak_test.cc
index 4f552a6..dd93653 100644
--- a/runtime/gc/system_weak_test.cc
+++ b/runtime/gc/system_weak_test.cc
@@ -35,6 +35,10 @@
namespace gc {
class SystemWeakTest : public CommonRuntimeTest {
+ protected:
+ SystemWeakTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
};
struct CountingSystemWeakHolder : public SystemWeakHolder {
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index 5790755..195986f 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -86,7 +86,7 @@
std::ostringstream oss;
oss << "GC tried to mark invalid reference " << ref << std::endl;
oss << DumpObjectInfo(ref, "ref") << "\n";
- oss << DumpObjectInfo(holder.Ptr(), "holder");
+ oss << DumpObjectInfo(holder.Ptr(), "holder") << "\n";
if (holder != nullptr) {
mirror::Class* holder_klass = holder->GetClass<kVerifyNone, kWithoutReadBarrier>();
if (IsValidClass(holder_klass)) {
diff --git a/runtime/gc_root-inl.h b/runtime/gc_root-inl.h
index 7795c66..e561d29 100644
--- a/runtime/gc_root-inl.h
+++ b/runtime/gc_root-inl.h
@@ -34,8 +34,12 @@
}
template<class MirrorType>
+inline GcRoot<MirrorType>::GcRoot(mirror::CompressedReference<mirror::Object> ref)
+ : root_(ref) { }
+
+template<class MirrorType>
inline GcRoot<MirrorType>::GcRoot(MirrorType* ref)
- : root_(mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref)) { }
+ : GcRoot(mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref)) { }
template<class MirrorType>
inline GcRoot<MirrorType>::GcRoot(ObjPtr<MirrorType> ref)
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 553f3d6..3f207ec 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -215,6 +215,8 @@
}
ALWAYS_INLINE GcRoot() {}
+ explicit ALWAYS_INLINE GcRoot(mirror::CompressedReference<mirror::Object> ref)
+ REQUIRES_SHARED(Locks::mutator_lock_);
explicit ALWAYS_INLINE GcRoot(MirrorType* ref)
REQUIRES_SHARED(Locks::mutator_lock_);
explicit ALWAYS_INLINE GcRoot(ObjPtr<MirrorType> ref)
diff --git a/runtime/handle.cc b/runtime/handle.cc
index af77e23..e9c9113 100644
--- a/runtime/handle.cc
+++ b/runtime/handle.cc
@@ -42,6 +42,7 @@
namespace art {
+// NOLINTBEGIN(bugprone-macro-parentheses)
#define MAKE_OBJECT_FOR_GDB(ROOT, NAME, MIRROR) \
template <> MIRROR* Handle<MIRROR>::GetFromGdb() { \
return Get(); \
@@ -53,5 +54,6 @@
CLASS_MIRROR_ROOT_LIST(MAKE_OBJECT_FOR_GDB)
#undef MAKE_OBJECT_FOR_GDB
+// NOLINTEND(bugprone-macro-parentheses)
} // namespace art
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index 3aa9e52..60a82a2 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -121,6 +121,15 @@
}
}
+template <typename Visitor>
+inline void HandleScope::VisitHandles(Visitor& visitor) {
+ for (size_t i = 0, count = NumberOfReferences(); i < count; ++i) {
+ if (GetHandle(i) != nullptr) {
+ visitor.Visit(GetHandle(i));
+ }
+ }
+}
+
template<size_t kNumReferences> template<class T>
inline MutableHandle<T> FixedSizeHandleScope<kNumReferences>::NewHandle(T* object) {
return NewHandle(ObjPtr<T>(object));
@@ -179,6 +188,15 @@
}
}
+template <typename Visitor>
+inline void BaseHandleScope::VisitHandles(Visitor& visitor) {
+ if (LIKELY(!IsVariableSized())) {
+ AsHandleScope()->VisitHandles(visitor);
+ } else {
+ AsVariableSized()->VisitHandles(visitor);
+ }
+}
+
inline VariableSizedHandleScope* BaseHandleScope::AsVariableSized() {
DCHECK(IsVariableSized());
return down_cast<VariableSizedHandleScope*>(this);
@@ -269,6 +287,15 @@
}
}
+template <typename Visitor>
+inline void VariableSizedHandleScope::VisitHandles(Visitor& visitor) {
+ LocalScopeType* cur = current_scope_;
+ while (cur != nullptr) {
+ cur->VisitHandles(visitor);
+ cur = reinterpret_cast<LocalScopeType*>(cur->GetLink());
+ }
+}
+
} // namespace art
#endif // ART_RUNTIME_HANDLE_SCOPE_INL_H_
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 89127e4..a43e889 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -56,6 +56,9 @@
template <typename Visitor>
ALWAYS_INLINE void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+ template <typename Visitor>
+ ALWAYS_INLINE void VisitHandles(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
// Link to previous BaseHandleScope or null.
BaseHandleScope* GetLink() const {
return link_;
@@ -148,6 +151,9 @@
template <typename Visitor>
ALWAYS_INLINE void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+ template <typename Visitor>
+ ALWAYS_INLINE void VisitHandles(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
protected:
// Return backing storage used for references.
ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const {
@@ -261,6 +267,9 @@
template <typename Visitor>
void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+ template <typename Visitor>
+ ALWAYS_INLINE void VisitHandles(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
static constexpr size_t kLocalScopeSize = 64u;
static constexpr size_t kSizeOfReferencesPerScope =
diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc
index d72dbe6..9207303 100644
--- a/runtime/handle_scope_test.cc
+++ b/runtime/handle_scope_test.cc
@@ -38,7 +38,12 @@
static_assert(std::is_trivially_copyable<ScopedNullHandle<mirror::Object>>::value,
"ScopedNullHandle should be trivially copyable");
-class HandleScopeTest : public CommonRuntimeTest {};
+class HandleScopeTest : public CommonRuntimeTest {
+ protected:
+ HandleScopeTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
// Test the offsets computed for members of HandleScope. Because of cross-compiling
// it is impossible the use OFFSETOF_MEMBER, so we do some reasonable computations ourselves. This
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 54acbb4..6acbe51 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -149,7 +149,7 @@
void InitializeCorePlatformApiPrivateFields() {
// The following fields in WellKnownClasses correspond to private fields in the Core Platform
// API that cannot be otherwise expressed and propagated through tooling (b/144502743).
- jfieldID private_core_platform_api_fields[] = {
+ ArtField* private_core_platform_api_fields[] = {
WellKnownClasses::java_io_FileDescriptor_descriptor,
WellKnownClasses::java_nio_Buffer_address,
WellKnownClasses::java_nio_Buffer_elementSizeShift,
@@ -158,8 +158,7 @@
};
ScopedObjectAccess soa(Thread::Current());
- for (const auto private_core_platform_api_field : private_core_platform_api_fields) {
- ArtField* field = jni::DecodeArtField(private_core_platform_api_field);
+ for (ArtField* field : private_core_platform_api_fields) {
const uint32_t access_flags = field->GetAccessFlags();
uint32_t new_access_flags = access_flags | kAccCorePlatformApi;
DCHECK(new_access_flags != access_flags);
@@ -367,30 +366,35 @@
if (runtime->IsAotCompiler()) {
return;
}
- JNIEnvExt* env = Thread::Current()->GetJniEnv();
- const std::string& package_name = Runtime::Current()->GetProcessPackageName();
- ScopedLocalRef<jstring> package_str(env, env->NewStringUTF(package_name.c_str()));
- if (env->ExceptionCheck()) {
- env->ExceptionClear();
- LOG(ERROR) << "Unable to allocate string for package name which called hidden api";
- }
+
+ const std::string& package_name = runtime->GetProcessPackageName();
std::ostringstream signature_str;
Dump(signature_str);
- ScopedLocalRef<jstring> signature_jstr(env,
- env->NewStringUTF(signature_str.str().c_str()));
- if (env->ExceptionCheck()) {
- env->ExceptionClear();
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2u> hs(soa.Self());
+ Handle<mirror::String> package_str = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), package_name.c_str()));
+ if (soa.Self()->IsExceptionPending()) {
+ soa.Self()->ClearException();
+ LOG(ERROR) << "Unable to allocate string for package name which called hidden api";
+ }
+ Handle<mirror::String> signature_jstr = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), signature_str.str().c_str()));
+ if (soa.Self()->IsExceptionPending()) {
+ soa.Self()->ClearException();
LOG(ERROR) << "Unable to allocate string for hidden api method signature";
}
- env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed,
- sampled_value,
- package_str.get(),
- signature_jstr.get(),
- static_cast<jint>(access_method),
- access_denied);
- if (env->ExceptionCheck()) {
- env->ExceptionClear();
+ WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed
+ ->InvokeStatic<'V', 'I', 'L', 'L', 'I', 'Z'>(
+ soa.Self(),
+ static_cast<jint>(sampled_value),
+ package_str.Get(),
+ signature_jstr.Get(),
+ static_cast<jint>(access_method),
+ access_denied);
+ if (soa.Self()->IsExceptionPending()) {
+ soa.Self()->ClearException();
LOG(ERROR) << "Unable to report hidden api usage";
}
#else
@@ -408,26 +412,28 @@
Runtime* runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2u> hs(soa.Self());
- ScopedLocalRef<jobject> consumer_object(soa.Env(),
- soa.Env()->GetStaticObjectField(
- WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
+ ArtField* consumer_field = WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ DCHECK(consumer_field->GetDeclaringClass()->IsInitialized());
+ Handle<mirror::Object> consumer_object =
+ hs.NewHandle(consumer_field->GetObject(consumer_field->GetDeclaringClass()));
+
// If the consumer is non-null, we call back to it to let it know that we
// have encountered an API that's in one of our lists.
if (consumer_object != nullptr) {
std::ostringstream member_signature_str;
Dump(member_signature_str);
- ScopedLocalRef<jobject> signature_str(
- soa.Env(),
- soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
+ Handle<mirror::String> signature_str = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), member_signature_str.str().c_str()));
+ // FIXME: Handle OOME. For now, crash immediatelly (do not continue with a pending exception).
+ CHECK(signature_str != nullptr);
// Call through to Consumer.accept(String memberSignature);
- soa.Env()->CallVoidMethod(consumer_object.get(),
- WellKnownClasses::java_util_function_Consumer_accept,
- signature_str.get());
+ WellKnownClasses::java_util_function_Consumer_accept->InvokeInterface<'V', 'L'>(
+ soa.Self(), consumer_object.Get(), signature_str.Get());
}
}
}
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 1eb5e17..281c1f3 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -29,7 +29,6 @@
#include "mirror/class_loader.h"
#include "reflection.h"
#include "runtime.h"
-#include "well_known_classes.h"
namespace art {
namespace hiddenapi {
@@ -123,7 +122,7 @@
if (domain == Domain::kApplication &&
klass->ShouldSkipHiddenApiChecks() &&
- Runtime::Current()->IsJavaDebuggable()) {
+ Runtime::Current()->IsJavaDebuggableAtInit()) {
// Class is known, it is marked trusted and we are in debuggable mode.
domain = ComputeDomain(/* is_trusted= */ true);
}
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index e204c57..a3d5db1 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -26,7 +26,7 @@
#include "common_runtime_test.h"
#include "jni/jni_internal.h"
#include "proxy_test.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -59,7 +59,7 @@
}
static bool LoadDexFiles(const std::string& path,
- ScopedObjectAccess& soa,
+ Thread* self,
/* out */ std::vector<std::unique_ptr<const DexFile>>* dex_files,
/* out */ ObjPtr<mirror::ClassLoader>* class_loader,
/* out */ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -74,11 +74,11 @@
ClassLinker* const linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> h_class = hs.NewHandle(soa.Decode<mirror::Class>(
- WellKnownClasses::dalvik_system_PathClassLoader));
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Class> h_class =
+ hs.NewHandle(WellKnownClasses::dalvik_system_PathClassLoader.Get());
Handle<mirror::ClassLoader> h_loader = hs.NewHandle(linker->CreateWellKnownClassLoader(
- soa.Self(),
+ self,
MakeNonOwningPointerVector(*dex_files),
h_class,
/* parent_loader= */ ScopedNullHandle<mirror::ClassLoader>(),
@@ -198,7 +198,7 @@
ObjPtr<mirror::ClassLoader> class_loader;
ASSERT_TRUE(Copy(GetTestDexFileName("Main"), location, &error_msg)) << error_msg;
- ASSERT_TRUE(LoadDexFiles(location, soa, &dex_files, &class_loader, &error_msg))
+ ASSERT_TRUE(LoadDexFiles(location, soa.Self(), &dex_files, &class_loader, &error_msg))
<< error_msg;
ASSERT_GE(dex_files.size(), 1u);
ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader,
diff --git a/runtime/image.cc b/runtime/image.cc
index b8536db..133782d 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -31,8 +31,8 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-// Last change: Math.fma(double, double, double) intrinsic.
-const uint8_t ImageHeader::kImageVersion[] = { '1', '0', '6', '\0' };
+// Last change: StringBuilderAppend for float/double.
+const uint8_t ImageHeader::kImageVersion[] = { '1', '0', '7', '\0' };
ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t component_count,
@@ -67,12 +67,14 @@
image_roots_(image_roots),
pointer_size_(pointer_size) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
- CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
- CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
- CHECK_LT(image_roots, oat_file_begin);
- CHECK_LE(oat_file_begin, oat_data_begin);
- CHECK_LT(oat_data_begin, oat_data_end);
- CHECK_LE(oat_data_end, oat_file_end);
+ if (oat_checksum != 0u) {
+ CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
+ CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
+ CHECK_LT(image_roots, oat_file_begin);
+ CHECK_LE(oat_file_begin, oat_data_begin);
+ CHECK_LT(oat_data_begin, oat_data_end);
+ CHECK_LE(oat_data_end, oat_file_end);
+ }
CHECK(ValidPointerSize(pointer_size_)) << pointer_size_;
memcpy(magic_, kImageMagic, sizeof(kImageMagic));
memcpy(version_, kImageVersion, sizeof(kImageVersion));
@@ -129,14 +131,16 @@
if (image_begin_ >= image_begin_ + image_size_) {
return false;
}
- if (oat_file_begin_ > oat_file_end_) {
- return false;
- }
- if (oat_data_begin_ > oat_data_end_) {
- return false;
- }
- if (oat_file_begin_ >= oat_data_begin_) {
- return false;
+ if (oat_checksum_ != 0u) {
+ if (oat_file_begin_ > oat_file_end_) {
+ return false;
+ }
+ if (oat_data_begin_ > oat_data_end_) {
+ return false;
+ }
+ if (oat_file_begin_ >= oat_data_begin_) {
+ return false;
+ }
}
return true;
}
diff --git a/runtime/image.h b/runtime/image.h
index bc0c9dd..88cfad0 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -230,6 +230,10 @@
// Aliases.
kAppImageClassLoader = kSpecialRoots, // The class loader used to build the app image.
kBootImageLiveObjects = kSpecialRoots, // Array of boot image objects that must be kept live.
+ kAppImageContextAndDexChecksums = kSpecialRoots, // Array of size 2, containing the class
+ // loader context in first entry, and an array
+ // of dex checksums for app images generated
+ // by the runtime.
};
enum BootImageLiveObjects {
@@ -502,6 +506,7 @@
uint32_t blocks_count_ = 0u;
friend class linker::ImageWriter;
+ friend class RuntimeImageHelper;
};
/*
diff --git a/runtime/index_bss_mapping.cc b/runtime/index_bss_mapping.cc
index 8d9d8cf..f6e083d 100644
--- a/runtime/index_bss_mapping.cc
+++ b/runtime/index_bss_mapping.cc
@@ -44,8 +44,6 @@
}
}
-constexpr size_t IndexBssMappingLookup::npos;
-
size_t IndexBssMappingLookup::GetBssOffset(const IndexBssMapping* mapping,
uint32_t index,
uint32_t number_of_indexes,
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 6ea035b..23df2c8 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -37,7 +37,7 @@
/*out*/std::string* error_msg) const {
DCHECK(iref != nullptr);
DCHECK_EQ(GetIndirectRefKind(iref), kind_);
- const uint32_t top_index = segment_state_.top_index;
+ const uint32_t top_index = top_index_;
uint32_t idx = ExtractIndex(iref);
if (UNLIKELY(idx >= top_index)) {
*error_msg = android::base::StringPrintf("deleted reference at index %u in a table of size %u",
@@ -82,7 +82,7 @@
inline ObjPtr<mirror::Object> IndirectReferenceTable::Get(IndirectRef iref) const {
DCHECK_EQ(GetIndirectRefKind(iref), kind_);
uint32_t idx = ExtractIndex(iref);
- DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_LT(idx, top_index_);
DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
DCHECK(!table_[idx].GetReference()->IsNull());
ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
@@ -93,7 +93,7 @@
inline void IndirectReferenceTable::Update(IndirectRef iref, ObjPtr<mirror::Object> obj) {
DCHECK_EQ(GetIndirectRefKind(iref), kind_);
uint32_t idx = ExtractIndex(iref);
- DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_LT(idx, top_index_);
DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
DCHECK(!table_[idx].GetReference()->IsNull());
table_[idx].SetReference(obj);
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index ebf382f..479eda5 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "base/bit_utils.h"
-#include "base/globals.h"
#include "indirect_reference_table-inl.h"
+#include "base/bit_utils.h"
+#include "base/globals.h"
#include "base/mutator_locked_dumpable.h"
#include "base/systrace.h"
#include "base/utils.h"
@@ -26,8 +26,9 @@
#include "jni/jni_internal.h"
#include "mirror/object-inl.h"
#include "nth_caller_visitor.h"
+#include "object_callbacks.h"
#include "reference_table.h"
-#include "runtime.h"
+#include "runtime-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
@@ -35,16 +36,15 @@
namespace art {
-static constexpr bool kDumpStackOnNonLocalReference = false;
static constexpr bool kDebugIRT = false;
// Maximum table size we allow.
static constexpr size_t kMaxTableSizeInBytes = 128 * MB;
-const char* GetIndirectRefKindString(const IndirectRefKind& kind) {
+const char* GetIndirectRefKindString(IndirectRefKind kind) {
switch (kind) {
- case kJniTransitionOrInvalid:
- return "JniTransitionOrInvalid";
+ case kJniTransition:
+ return "JniTransition";
case kLocal:
return "Local";
case kGlobal:
@@ -79,86 +79,37 @@
return result;
}
-SmallIrtAllocator::SmallIrtAllocator()
- : small_irt_freelist_(nullptr), lock_("Small IRT table lock", LockLevel::kGenericBottomLock) {
-}
-
-// Allocate an IRT table for kSmallIrtEntries.
-IrtEntry* SmallIrtAllocator::Allocate(std::string* error_msg) {
- MutexLock lock(Thread::Current(), lock_);
- if (small_irt_freelist_ == nullptr) {
- // Refill.
- MemMap map = NewIRTMap(kPageSize, error_msg);
- if (map.IsValid()) {
- small_irt_freelist_ = reinterpret_cast<IrtEntry*>(map.Begin());
- for (uint8_t* p = map.Begin(); p + kInitialIrtBytes < map.End(); p += kInitialIrtBytes) {
- *reinterpret_cast<IrtEntry**>(p) = reinterpret_cast<IrtEntry*>(p + kInitialIrtBytes);
- }
- shared_irt_maps_.emplace_back(std::move(map));
- }
- }
- if (small_irt_freelist_ == nullptr) {
- return nullptr;
- }
- IrtEntry* result = small_irt_freelist_;
- small_irt_freelist_ = *reinterpret_cast<IrtEntry**>(small_irt_freelist_);
- // Clear pointer in first entry.
- new(result) IrtEntry();
- return result;
-}
-
-void SmallIrtAllocator::Deallocate(IrtEntry* unneeded) {
- MutexLock lock(Thread::Current(), lock_);
- *reinterpret_cast<IrtEntry**>(unneeded) = small_irt_freelist_;
- small_irt_freelist_ = unneeded;
-}
-
-IndirectReferenceTable::IndirectReferenceTable(size_t max_count,
- IndirectRefKind desired_kind,
- ResizableCapacity resizable,
- std::string* error_msg)
- : segment_state_(kIRTFirstSegment),
+IndirectReferenceTable::IndirectReferenceTable(IndirectRefKind kind)
+ : table_mem_map_(),
table_(nullptr),
- kind_(desired_kind),
- max_entries_(max_count),
- current_num_holes_(0),
- resizable_(resizable) {
+ kind_(kind),
+ top_index_(0u),
+ max_entries_(0u),
+ current_num_holes_(0) {
+ CHECK_NE(kind, kJniTransition);
+ CHECK_NE(kind, kLocal);
+}
+
+bool IndirectReferenceTable::Initialize(size_t max_count, std::string* error_msg) {
CHECK(error_msg != nullptr);
- CHECK_NE(desired_kind, kJniTransitionOrInvalid);
// Overflow and maximum check.
CHECK_LE(max_count, kMaxTableSizeInBytes / sizeof(IrtEntry));
- if (max_entries_ <= kSmallIrtEntries) {
- table_ = Runtime::Current()->GetSmallIrtAllocator()->Allocate(error_msg);
- if (table_ != nullptr) {
- max_entries_ = kSmallIrtEntries;
- // table_mem_map_ remains invalid.
- }
+ const size_t table_bytes = RoundUp(max_count * sizeof(IrtEntry), kPageSize);
+ table_mem_map_ = NewIRTMap(table_bytes, error_msg);
+ if (!table_mem_map_.IsValid()) {
+ DCHECK(!error_msg->empty());
+ return false;
}
- if (table_ == nullptr) {
- const size_t table_bytes = RoundUp(max_count * sizeof(IrtEntry), kPageSize);
- table_mem_map_ = NewIRTMap(table_bytes, error_msg);
- if (!table_mem_map_.IsValid() && error_msg->empty()) {
- *error_msg = "Unable to map memory for indirect ref table";
- }
- if (table_mem_map_.IsValid()) {
- table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
- } else {
- table_ = nullptr;
- }
- // Take into account the actual length.
- max_entries_ = table_bytes / sizeof(IrtEntry);
- }
- segment_state_ = kIRTFirstSegment;
- last_known_previous_state_ = kIRTFirstSegment;
+ table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
+ // Take into account the actual length.
+ max_entries_ = table_bytes / sizeof(IrtEntry);
+ return true;
}
IndirectReferenceTable::~IndirectReferenceTable() {
- if (table_ != nullptr && !table_mem_map_.IsValid()) {
- Runtime::Current()->GetSmallIrtAllocator()->Deallocate(table_);
- }
}
void IndirectReferenceTable::ConstexprChecks() {
@@ -189,10 +140,6 @@
static_assert(DecodeIndex(EncodeIndex(3u)) == 3u, "Index encoding error");
}
-bool IndirectReferenceTable::IsValid() const {
- return table_ != nullptr;
-}
-
// Holes:
//
// To keep the IRT compact, we want to fill "holes" created by non-stack-discipline Add & Remove
@@ -200,37 +147,10 @@
// similar. Instead, we scan for holes, with the expectation that we will find holes fast as they
// are usually near the end of the table (see the header, TODO: verify this assumption). To avoid
// scans when there are no holes, the number of known holes should be tracked.
-//
-// A previous implementation stored the top index and the number of holes as the segment state.
-// This constraints the maximum number of references to 16-bit. We want to relax this, as it
-// is easy to require more references (e.g., to list all classes in large applications). Thus,
-// the implicitly stack-stored state, the IRTSegmentState, is only the top index.
-//
-// Thus, hole count is a local property of the current segment, and needs to be recovered when
-// (or after) a frame is pushed or popped. To keep JNI transitions simple (and inlineable), we
-// cannot do work when the segment changes. Thus, Add and Remove need to ensure the current
-// hole count is correct.
-//
-// To be able to detect segment changes, we require an additional local field that can describe
-// the known segment. This is last_known_previous_state_. The requirement will become clear with
-// the following (some non-trivial) cases that have to be supported:
-//
-// 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
-// 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
-// 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
-// reference
-// 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
-// 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
-// reference
-//
-// Storing the last known *previous* state (bottom index) allows conservatively detecting all the
-// segment changes above. The condition is simply that the last known state is greater than or
-// equal to the current previous state, and smaller than the current state (top index). The
-// condition is conservative as it adds O(1) overhead to operations on an empty segment.
-static size_t CountNullEntries(const IrtEntry* table, size_t from, size_t to) {
+static size_t CountNullEntries(const IrtEntry* table, size_t to) {
size_t count = 0;
- for (size_t index = from; index != to; ++index) {
+ for (size_t index = 0u; index != to; ++index) {
if (table[index].GetReference()->IsNull()) {
count++;
}
@@ -238,121 +158,37 @@
return count;
}
-void IndirectReferenceTable::RecoverHoles(IRTSegmentState prev_state) {
- if (last_known_previous_state_.top_index >= segment_state_.top_index ||
- last_known_previous_state_.top_index < prev_state.top_index) {
- const size_t top_index = segment_state_.top_index;
- size_t count = CountNullEntries(table_, prev_state.top_index, top_index);
-
- if (kDebugIRT) {
- LOG(INFO) << "+++ Recovered holes: "
- << " Current prev=" << prev_state.top_index
- << " Current top_index=" << top_index
- << " Old num_holes=" << current_num_holes_
- << " New num_holes=" << count;
- }
-
- current_num_holes_ = count;
- last_known_previous_state_ = prev_state;
- } else if (kDebugIRT) {
- LOG(INFO) << "No need to recover holes";
- }
-}
-
ALWAYS_INLINE
static inline void CheckHoleCount(IrtEntry* table,
size_t exp_num_holes,
- IRTSegmentState prev_state,
- IRTSegmentState cur_state) {
+ size_t top_index) {
if (kIsDebugBuild) {
- size_t count = CountNullEntries(table, prev_state.top_index, cur_state.top_index);
- CHECK_EQ(exp_num_holes, count) << "prevState=" << prev_state.top_index
- << " topIndex=" << cur_state.top_index;
+ size_t count = CountNullEntries(table, top_index);
+ CHECK_EQ(exp_num_holes, count) << " topIndex=" << top_index;
}
}
-bool IndirectReferenceTable::Resize(size_t new_size, std::string* error_msg) {
- CHECK_GT(new_size, max_entries_);
-
- constexpr size_t kMaxEntries = kMaxTableSizeInBytes / sizeof(IrtEntry);
- if (new_size > kMaxEntries) {
- *error_msg = android::base::StringPrintf("Requested size exceeds maximum: %zu", new_size);
- return false;
- }
- // Note: the above check also ensures that there is no overflow below.
-
- const size_t table_bytes = RoundUp(new_size * sizeof(IrtEntry), kPageSize);
-
- MemMap new_map = NewIRTMap(table_bytes, error_msg);
- if (!new_map.IsValid()) {
- return false;
- }
-
- memcpy(new_map.Begin(), table_, max_entries_ * sizeof(IrtEntry));
- if (!table_mem_map_.IsValid()) {
- // Didn't have its own map; deallocate old table.
- Runtime::Current()->GetSmallIrtAllocator()->Deallocate(table_);
- }
- table_mem_map_ = std::move(new_map);
- table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
- const size_t real_new_size = table_bytes / sizeof(IrtEntry);
- DCHECK_GE(real_new_size, new_size);
- max_entries_ = real_new_size;
-
- return true;
-}
-
-IndirectRef IndirectReferenceTable::Add(IRTSegmentState previous_state,
- ObjPtr<mirror::Object> obj,
- std::string* error_msg) {
+IndirectRef IndirectReferenceTable::Add(ObjPtr<mirror::Object> obj, std::string* error_msg) {
if (kDebugIRT) {
- LOG(INFO) << "+++ Add: previous_state=" << previous_state.top_index
- << " top_index=" << segment_state_.top_index
- << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ LOG(INFO) << "+++ Add: top_index=" << top_index_
<< " holes=" << current_num_holes_;
}
- size_t top_index = segment_state_.top_index;
-
CHECK(obj != nullptr);
VerifyObject(obj);
DCHECK(table_ != nullptr);
- if (top_index == max_entries_) {
- if (resizable_ == ResizableCapacity::kNo) {
- std::ostringstream oss;
- oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")"
- << MutatorLockedDumpable<IndirectReferenceTable>(*this);
- *error_msg = oss.str();
- return nullptr;
- }
-
- // Try to double space.
- if (std::numeric_limits<size_t>::max() / 2 < max_entries_) {
- std::ostringstream oss;
- oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")" << std::endl
- << MutatorLockedDumpable<IndirectReferenceTable>(*this)
- << " Resizing failed: exceeds size_t";
- *error_msg = oss.str();
- return nullptr;
- }
-
- std::string inner_error_msg;
- if (!Resize(max_entries_ * 2, &inner_error_msg)) {
- std::ostringstream oss;
- oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")" << std::endl
- << MutatorLockedDumpable<IndirectReferenceTable>(*this)
- << " Resizing failed: " << inner_error_msg;
- *error_msg = oss.str();
- return nullptr;
- }
+ if (top_index_ == max_entries_) {
+ // TODO: Fill holes before reporting error.
+ std::ostringstream oss;
+ oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
+ << "(max=" << max_entries_ << ")"
+ << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+ *error_msg = oss.str();
+ return nullptr;
}
- RecoverHoles(previous_state);
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
// We know there's enough room in the table. Now we just need to find
// the right spot. If there's a hole, find it and fill it; otherwise,
@@ -360,26 +196,26 @@
IndirectRef result;
size_t index;
if (current_num_holes_ > 0) {
- DCHECK_GT(top_index, 1U);
+ DCHECK_GT(top_index_, 1U);
// Find the first hole; likely to be near the end of the list.
- IrtEntry* p_scan = &table_[top_index - 1];
+ IrtEntry* p_scan = &table_[top_index_ - 1];
DCHECK(!p_scan->GetReference()->IsNull());
--p_scan;
while (!p_scan->GetReference()->IsNull()) {
- DCHECK_GE(p_scan, table_ + previous_state.top_index);
+ DCHECK_GT(p_scan, table_);
--p_scan;
}
index = p_scan - table_;
current_num_holes_--;
} else {
// Add to the end.
- index = top_index++;
- segment_state_.top_index = top_index;
+ index = top_index_;
+ ++top_index_;
}
table_[index].Add(obj);
result = ToIndirectRef(index);
if (kDebugIRT) {
- LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.top_index
+ LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << top_index_
<< " holes=" << current_num_holes_;
}
@@ -387,72 +223,31 @@
return result;
}
-void IndirectReferenceTable::AssertEmpty() {
- for (size_t i = 0; i < Capacity(); ++i) {
- if (!table_[i].GetReference()->IsNull()) {
- LOG(FATAL) << "Internal Error: non-empty local reference table\n"
- << MutatorLockedDumpable<IndirectReferenceTable>(*this);
- UNREACHABLE();
- }
- }
-}
-
// Removes an object. We extract the table offset bits from "iref"
// and zap the corresponding entry, leaving a hole if it's not at the top.
-// If the entry is not between the current top index and the bottom index
-// specified by the cookie, we don't remove anything. This is the behavior
-// required by JNI's DeleteLocalRef function.
-// This method is not called when a local frame is popped; this is only used
-// for explicit single removals.
// Returns "false" if nothing was removed.
-bool IndirectReferenceTable::Remove(IRTSegmentState previous_state, IndirectRef iref) {
+bool IndirectReferenceTable::Remove(IndirectRef iref) {
if (kDebugIRT) {
- LOG(INFO) << "+++ Remove: previous_state=" << previous_state.top_index
- << " top_index=" << segment_state_.top_index
- << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ LOG(INFO) << "+++ Remove: top_index=" << top_index_
<< " holes=" << current_num_holes_;
}
- const uint32_t top_index = segment_state_.top_index;
- const uint32_t bottom_index = previous_state.top_index;
+ // TODO: We should eagerly check the ref kind against the `kind_` instead of postponing until
+ // `CheckEntry()` below. Passing the wrong kind shall currently result in misleading warnings.
+
+ const uint32_t top_index = top_index_;
DCHECK(table_ != nullptr);
- // TODO: We should eagerly check the ref kind against the `kind_` instead of
- // relying on this weak check and postponing the rest until `CheckEntry()` below.
- // Passing the wrong kind shall currently result in misleading warnings.
- if (GetIndirectRefKind(iref) == kJniTransitionOrInvalid) {
- auto* self = Thread::Current();
- ScopedObjectAccess soa(self);
- if (self->IsJniTransitionReference(reinterpret_cast<jobject>(iref))) {
- auto* env = self->GetJniEnv();
- DCHECK(env != nullptr);
- if (env->IsCheckJniEnabled()) {
- LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
- if (kDumpStackOnNonLocalReference) {
- self->Dump(LOG_STREAM(WARNING));
- }
- }
- return true;
- }
- }
-
const uint32_t idx = ExtractIndex(iref);
- if (idx < bottom_index) {
- // Wrong segment.
- LOG(WARNING) << "Attempt to remove index outside index area (" << idx
- << " vs " << bottom_index << "-" << top_index << ")";
- return false;
- }
if (idx >= top_index) {
// Bad --- stale reference?
LOG(WARNING) << "Attempt to remove invalid index " << idx
- << " (bottom=" << bottom_index << " top=" << top_index << ")";
+ << " (top=" << top_index << ")";
return false;
}
- RecoverHoles(previous_state);
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
if (idx == top_index - 1) {
// Top-most entry. Scan up and consume holes.
@@ -464,11 +259,10 @@
*table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
if (current_num_holes_ != 0) {
uint32_t collapse_top_index = top_index;
- while (--collapse_top_index > bottom_index && current_num_holes_ != 0) {
+ while (--collapse_top_index > 0u && current_num_holes_ != 0) {
if (kDebugIRT) {
ScopedObjectAccess soa(Thread::Current());
- LOG(INFO) << "+++ checking for hole at " << collapse_top_index - 1
- << " (previous_state=" << bottom_index << ") val="
+ LOG(INFO) << "+++ checking for hole at " << collapse_top_index - 1 << " val="
<< table_[collapse_top_index - 1].GetReference()->Read<kWithoutReadBarrier>();
}
if (!table_[collapse_top_index - 1].GetReference()->IsNull()) {
@@ -479,11 +273,11 @@
}
current_num_holes_--;
}
- segment_state_.top_index = collapse_top_index;
+ top_index_ = collapse_top_index;
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
} else {
- segment_state_.top_index = top_index - 1;
+ top_index_ = top_index - 1;
if (kDebugIRT) {
LOG(INFO) << "+++ ate last entry " << top_index - 1;
}
@@ -501,7 +295,7 @@
*table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
current_num_holes_++;
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
if (kDebugIRT) {
LOG(INFO) << "+++ left hole at " << idx << ", holes=" << current_num_holes_;
}
@@ -512,10 +306,7 @@
void IndirectReferenceTable::Trim() {
ScopedTrace trace(__PRETTY_FUNCTION__);
- if (!table_mem_map_.IsValid()) {
- // Small table; nothing to do here.
- return;
- }
+ DCHECK(table_mem_map_.IsValid());
const size_t top_index = Capacity();
uint8_t* release_start = AlignUp(reinterpret_cast<uint8_t*>(&table_[top_index]), kPageSize);
uint8_t* release_end = static_cast<uint8_t*>(table_mem_map_.BaseEnd());
@@ -529,7 +320,8 @@
void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
BufferedRootVisitor<kDefaultBufferedRootCount> root_visitor(visitor, root_info);
- for (auto ref : *this) {
+ for (size_t i = 0, capacity = Capacity(); i != capacity; ++i) {
+ GcRoot<mirror::Object>* ref = table_[i].GetReference();
if (!ref->IsNull()) {
root_visitor.VisitRoot(*ref);
DCHECK(!ref->IsNull());
@@ -537,6 +329,24 @@
}
}
+void IndirectReferenceTable::SweepJniWeakGlobals(IsMarkedVisitor* visitor) {
+ CHECK_EQ(kind_, kWeakGlobal);
+ MutexLock mu(Thread::Current(), *Locks::jni_weak_globals_lock_);
+ Runtime* const runtime = Runtime::Current();
+ for (size_t i = 0, capacity = Capacity(); i != capacity; ++i) {
+ GcRoot<mirror::Object>* entry = table_[i].GetReference();
+ // Need to skip null here to distinguish between null entries and cleared weak ref entries.
+ if (!entry->IsNull()) {
+ mirror::Object* obj = entry->Read<kWithoutReadBarrier>();
+ mirror::Object* new_obj = visitor->IsMarked(obj);
+ if (new_obj == nullptr) {
+ new_obj = runtime->GetClearedJniWeakGlobal();
+ }
+ *entry = GcRoot<mirror::Object>(new_obj);
+ }
+ }
+}
+
void IndirectReferenceTable::Dump(std::ostream& os) const {
os << kind_ << " table dump:\n";
ReferenceTable::Table entries;
@@ -550,47 +360,8 @@
ReferenceTable::Dump(os, entries);
}
-void IndirectReferenceTable::SetSegmentState(IRTSegmentState new_state) {
- if (kDebugIRT) {
- LOG(INFO) << "Setting segment state: "
- << segment_state_.top_index
- << " -> "
- << new_state.top_index;
- }
- segment_state_ = new_state;
-}
-
-bool IndirectReferenceTable::EnsureFreeCapacity(size_t free_capacity, std::string* error_msg) {
- DCHECK_GE(free_capacity, static_cast<size_t>(1));
- if (free_capacity > kMaxTableSizeInBytes) {
- // Arithmetic might even overflow.
- *error_msg = "Requested table size implausibly large";
- return false;
- }
- size_t top_index = segment_state_.top_index;
- if (top_index + free_capacity <= max_entries_) {
- return true;
- }
-
- // We're only gonna do a simple best-effort here, ensuring the asked-for capacity at the end.
- if (resizable_ == ResizableCapacity::kNo) {
- *error_msg = "Table is not resizable";
- return false;
- }
-
- // Try to increase the table size.
- if (!Resize(top_index + free_capacity, error_msg)) {
- LOG(WARNING) << "JNI ERROR: Unable to reserve space in EnsureFreeCapacity (" << free_capacity
- << "): " << std::endl
- << MutatorLockedDumpable<IndirectReferenceTable>(*this)
- << " Resizing failed: " << *error_msg;
- return false;
- }
- return true;
-}
-
size_t IndirectReferenceTable::FreeCapacity() const {
- return max_entries_ - segment_state_.top_index;
+ return max_entries_ - top_index_;
}
} // namespace art
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index e279422..59729ac 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -37,75 +37,65 @@
namespace art {
+class IsMarkedVisitor;
class RootInfo;
namespace mirror {
class Object;
} // namespace mirror
-// Maintain a table of indirect references. Used for local/global JNI references.
-//
-// The table contains object references, where the strong (local/global) references are part of the
-// GC root set (but not the weak global references). When an object is added we return an
-// IndirectRef that is not a valid pointer but can be used to find the original value in O(1) time.
-// Conversions to and from indirect references are performed on upcalls and downcalls, so they need
-// to be very fast.
-//
-// To be efficient for JNI local variable storage, we need to provide operations that allow us to
-// operate on segments of the table, where segments are pushed and popped as if on a stack. For
-// example, deletion of an entry should only succeed if it appears in the current segment, and we
-// want to be able to strip off the current segment quickly when a method returns. Additions to the
-// table must be made in the current segment even if space is available in an earlier area.
-//
-// A new segment is created when we call into native code from interpreted code, or when we handle
-// the JNI PushLocalFrame function.
-//
-// The GC must be able to scan the entire table quickly.
-//
-// In summary, these must be very fast:
-// - adding or removing a segment
-// - adding references to a new segment
-// - converting an indirect reference back to an Object
-// These can be a little slower, but must still be pretty quick:
-// - adding references to a "mature" segment
-// - removing individual references
-// - scanning the entire table straight through
-//
-// If there's more than one segment, we don't guarantee that the table will fill completely before
-// we fail due to lack of space. We do ensure that the current segment will pack tightly, which
-// should satisfy JNI requirements (e.g. EnsureLocalCapacity).
-//
-// Only SynchronizedGet is synchronized.
-
// Indirect reference definition. This must be interchangeable with JNI's jobject, and it's
// convenient to let null be null, so we use void*.
//
-// We need a (potentially) large table index and a 2-bit reference type (global, local, weak
-// global). We also reserve some bits to be used to detect stale indirect references: we put a
-// serial number in the extra bits, and keep a copy of the serial number in the table. This requires
-// more memory and additional memory accesses on add/get, but is moving-GC safe. It will catch
-// additional problems, e.g.: create iref1 for obj, delete iref1, create iref2 for same obj,
-// lookup iref1. A pattern based on object bits will miss this.
+// We need a 2-bit reference kind (global, local, weak global) and the rest of the `IndirectRef`
+// is used to locate the actual reference storage.
+//
+// For global and weak global references, we need a (potentially) large table index and we also
+// reserve some bits to be used to detect stale indirect references: we put a serial number in
+// the extra bits, and keep a copy of the serial number in the table. This requires more memory
+// and additional memory accesses on add/get, but is moving-GC safe. It will catch additional
+// problems, e.g.: create iref1 for obj, delete iref1, create iref2 for same obj, lookup iref1.
+// A pattern based on object bits will miss this.
+//
+// Local references use the same bits for the reference kind but the rest of their `IndirectRef`
+// encoding is different, see `LocalReferenceTable` for details.
using IndirectRef = void*;
// Indirect reference kind, used as the two low bits of IndirectRef.
//
-// For convenience these match up with enum jobjectRefType from jni.h.
+// For convenience these match up with enum jobjectRefType from jni.h, except that
+// we use value 0 for JNI transitions instead of marking invalid reference type.
enum IndirectRefKind {
- kJniTransitionOrInvalid = 0, // <<JNI transition frame reference or invalid reference>>
- kLocal = 1, // <<local reference>>
- kGlobal = 2, // <<global reference>>
- kWeakGlobal = 3, // <<weak global reference>>
- kLastKind = kWeakGlobal
+ kJniTransition = 0, // <<JNI transition frame reference>>
+ kLocal = 1, // <<local reference>>
+ kGlobal = 2, // <<global reference>>
+ kWeakGlobal = 3, // <<weak global reference>>
+ kLastKind = kWeakGlobal
};
std::ostream& operator<<(std::ostream& os, IndirectRefKind rhs);
-const char* GetIndirectRefKindString(const IndirectRefKind& kind);
+const char* GetIndirectRefKindString(IndirectRefKind kind);
+
+// Maintain a table of indirect references. Used for global and weak global JNI references.
+//
+// The table contains object references, where the strong global references are part of the
+// GC root set (but not the weak global references). When an object is added we return an
+// `IndirectRef` that is not a valid pointer but can be used to find the original value in O(1)
+// time. Conversions to and from indirect references are performed in JNI functions and when
+// returning from native methods to managed code, so they need to be very fast.
+//
+// The GC must be able to scan the entire table quickly.
+//
+// In summary, these must be very fast:
+// - adding references
+// - converting an indirect reference back to an Object
+// These can be a little slower, but must still be pretty quick:
+// - removing individual references
+// - scanning the entire table straight through
// Table definition.
//
-// For the global reference table, the expected common operations are adding a new entry and
-// removing a recently-added entry (usually the most-recently-added entry). For JNI local
-// references, the common operations are adding a new entry and removing an entire table segment.
+// For the global reference tables, the expected common operations are adding a new entry and
+// removing a recently-added entry (usually the most-recently-added entry).
//
// If we delete entries from the middle of the list, we will be left with "holes". We track the
// number of holes so that, when adding new elements, we can quickly decide to do a trivial append
@@ -114,18 +104,6 @@
// When the top-most entry is removed, any holes immediately below it are also removed. Thus,
// deletion of an entry may reduce "top_index" by more than one.
//
-// To get the desired behavior for JNI locals, we need to know the bottom and top of the current
-// "segment". The top is managed internally, and the bottom is passed in as a function argument.
-// When we call a native method or push a local frame, the current top index gets pushed on, and
-// serves as the new bottom. When we pop a frame off, the value from the stack becomes the new top
-// index, and the value stored in the previous frame becomes the new bottom.
-//
-// Holes are being locally cached for the segment. Otherwise we'd have to pass bottom index and
-// number of holes, which restricts us to 16 bits for the top index. The value is cached within the
-// table. To avoid code in generated JNI transitions, which implicitly form segments, the code for
-// adding and removing references needs to detect the change of a segment. Helper fields are used
-// for this detection.
-//
// Common alternative implementation: make IndirectRef a pointer to the actual reference slot.
// Instead of getting a table and doing a lookup, the lookup can be done instantly. Operations like
// determining the type and deleting the reference are more expensive because the table must be
@@ -135,20 +113,7 @@
// approaches).
//
// TODO: consider a "lastDeleteIndex" for quick hole-filling when an add immediately follows a
-// delete; must invalidate after segment pop might be worth only using it for JNI globals.
-//
-// TODO: may want completely different add/remove algorithms for global and local refs to improve
-// performance. A large circular buffer might reduce the amortized cost of adding global
-// references.
-
-// The state of the current segment. We only store the index. Splitting it for index and hole
-// count restricts the range too much.
-struct IRTSegmentState {
- uint32_t top_index;
-};
-
-// Use as initial value for "cookie", and when table has only one segment.
-static constexpr IRTSegmentState kIRTFirstSegment = { 0 };
+// delete.
// We associate a few bits of serial number with each reference, for error checking.
static constexpr unsigned int kIRTSerialBits = 3;
@@ -181,110 +146,22 @@
static_assert(sizeof(IrtEntry) == 2 * sizeof(uint32_t), "Unexpected sizeof(IrtEntry)");
static_assert(IsPowerOfTwo(sizeof(IrtEntry)), "Unexpected sizeof(IrtEntry)");
-class IrtIterator {
- public:
- IrtIterator(IrtEntry* table, size_t i, size_t capacity) REQUIRES_SHARED(Locks::mutator_lock_)
- : table_(table), i_(i), capacity_(capacity) {
- // capacity_ is used in some target; has warning with unused attribute.
- UNUSED(capacity_);
- }
-
- IrtIterator& operator++() REQUIRES_SHARED(Locks::mutator_lock_) {
- ++i_;
- return *this;
- }
-
- GcRoot<mirror::Object>* operator*() REQUIRES_SHARED(Locks::mutator_lock_) {
- // This does not have a read barrier as this is used to visit roots.
- return table_[i_].GetReference();
- }
-
- bool equals(const IrtIterator& rhs) const {
- return (i_ == rhs.i_ && table_ == rhs.table_);
- }
-
- private:
- IrtEntry* const table_;
- size_t i_;
- const size_t capacity_;
-};
-
-bool inline operator==(const IrtIterator& lhs, const IrtIterator& rhs) {
- return lhs.equals(rhs);
-}
-
-bool inline operator!=(const IrtIterator& lhs, const IrtIterator& rhs) {
- return !lhs.equals(rhs);
-}
-
-// We initially allocate local reference tables with a very small number of entries, packing
-// multiple tables into a single page. If we need to expand one, we allocate them in units of
-// pages.
-// TODO: We should allocate all IRT tables as nonmovable Java objects, That in turn works better
-// if we break up each table into 2 parallel arrays, one for the Java reference, and one for the
-// serial number. The current scheme page-aligns regions containing IRT tables, and so allows them
-// to be identified and page-protected in the future.
-constexpr size_t kInitialIrtBytes = 512; // Number of bytes in an initial local table.
-constexpr size_t kSmallIrtEntries = kInitialIrtBytes / sizeof(IrtEntry);
-static_assert(kPageSize % kInitialIrtBytes == 0);
-static_assert(kInitialIrtBytes % sizeof(IrtEntry) == 0);
-static_assert(kInitialIrtBytes % sizeof(void *) == 0);
-
-// A minimal stopgap allocator for initial small local IRT tables.
-class SmallIrtAllocator {
- public:
- SmallIrtAllocator();
-
- // Allocate an IRT table for kSmallIrtEntries.
- IrtEntry* Allocate(std::string* error_msg) REQUIRES(!lock_);
-
- void Deallocate(IrtEntry* unneeded) REQUIRES(!lock_);
-
- private:
- // A free list of kInitialIrtBytes chunks linked through the first word.
- IrtEntry* small_irt_freelist_;
-
- // Repository of MemMaps used for small IRT tables.
- std::vector<MemMap> shared_irt_maps_;
-
- Mutex lock_; // Level kGenericBottomLock; acquired before mem_map_lock_, which is a C++ mutex.
-};
-
class IndirectReferenceTable {
public:
- enum class ResizableCapacity {
- kNo,
- kYes
- };
+ // Constructs an uninitialized indirect reference table. Use `Initialize()` to initialize it.
+ explicit IndirectReferenceTable(IndirectRefKind kind);
- // WARNING: Construction of the IndirectReferenceTable may fail.
- // error_msg must not be null. If error_msg is set by the constructor, then
- // construction has failed and the IndirectReferenceTable will be in an
- // invalid state. Use IsValid to check whether the object is in an invalid
- // state.
- // Max_count is the minimum initial capacity (resizable), or minimum total capacity
- // (not resizable). A value of 1 indicates an implementation-convenient small size.
- IndirectReferenceTable(size_t max_count,
- IndirectRefKind kind,
- ResizableCapacity resizable,
- std::string* error_msg);
+ // Initialize the indirect reference table.
+ //
+ // Max_count is the requested total capacity (not resizable). The actual total capacity
+ // can be higher to utilize all allocated memory (rounding up to whole pages).
+ bool Initialize(size_t max_count, std::string* error_msg);
~IndirectReferenceTable();
- /*
- * Checks whether construction of the IndirectReferenceTable succeeded.
- *
- * This object must only be used if IsValid() returns true. It is safe to
- * call IsValid from multiple threads without locking or other explicit
- * synchronization.
- */
- bool IsValid() const;
-
// Add a new entry. "obj" must be a valid non-null object reference. This function will
// return null if an error happened (with an appropriate error message set).
- IndirectRef Add(IRTSegmentState previous_state,
- ObjPtr<mirror::Object> obj,
- std::string* error_msg)
+ IndirectRef Add(ObjPtr<mirror::Object> obj, std::string* error_msg)
REQUIRES_SHARED(Locks::mutator_lock_);
// Given an IndirectRef in the table, return the Object it refers to.
@@ -294,13 +171,6 @@
ObjPtr<mirror::Object> Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_)
ALWAYS_INLINE;
- // Synchronized get which reads a reference, acquiring a lock if necessary.
- template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
- ObjPtr<mirror::Object> SynchronizedGet(IndirectRef iref) const
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return Get<kReadBarrierOption>(iref);
- }
-
// Updates an existing indirect reference to point to a new object.
void Update(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -311,9 +181,7 @@
// required by JNI's DeleteLocalRef function.
//
// Returns "false" if nothing was removed.
- bool Remove(IRTSegmentState previous_state, IndirectRef iref);
-
- void AssertEmpty() REQUIRES_SHARED(Locks::mutator_lock_);
+ bool Remove(IndirectRef iref);
void Dump(std::ostream& os) const
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -326,48 +194,22 @@
// Return the #of entries in the entire table. This includes holes, and
// so may be larger than the actual number of "live" entries.
size_t Capacity() const {
- return segment_state_.top_index;
+ return top_index_;
}
// Return the number of non-null entries in the table. Only reliable for a
// single segment table.
int32_t NEntriesForGlobal() {
- return segment_state_.top_index - current_num_holes_;
+ return top_index_ - current_num_holes_;
}
- // Ensure that at least free_capacity elements are available, or return false.
- // Caller ensures free_capacity > 0.
- bool EnsureFreeCapacity(size_t free_capacity, std::string* error_msg)
- REQUIRES_SHARED(Locks::mutator_lock_);
- // See implementation of EnsureFreeCapacity. We'll only state here how much is trivially free,
- // without recovering holes. Thus this is a conservative estimate.
+ // We'll only state here how much is trivially free, without recovering holes.
+ // Thus this is a conservative estimate.
size_t FreeCapacity() const;
- // Note IrtIterator does not have a read barrier as it's used to visit roots.
- IrtIterator begin() {
- return IrtIterator(table_, 0, Capacity());
- }
-
- IrtIterator end() {
- return IrtIterator(table_, Capacity(), Capacity());
- }
-
void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
REQUIRES_SHARED(Locks::mutator_lock_);
- IRTSegmentState GetSegmentState() const {
- return segment_state_;
- }
-
- void SetSegmentState(IRTSegmentState new_state);
-
- static Offset SegmentStateOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
- // Note: Currently segment_state_ is at offset 0. We're testing the expected value in
- // jni_internal_test to make sure it stays correct. It is not OFFSETOF_MEMBER, as that
- // is not pointer-size-safe.
- return Offset(0);
- }
-
// Release pages past the end of the table that may have previously held references.
void Trim() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -380,6 +222,10 @@
bool IsValidReference(IndirectRef, /*out*/std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ void SweepJniWeakGlobals(IsMarkedVisitor* visitor)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::jni_weak_globals_lock_);
+
private:
static constexpr uint32_t kShiftedSerialMask = (1u << kIRTSerialBits) - 1;
@@ -429,42 +275,30 @@
return reinterpret_cast<IndirectRef>(EncodeIndirectRef(table_index, serial));
}
- // Resize the backing table to be at least new_size elements long. Currently
- // must be larger than the current size. After return max_entries_ >= new_size.
- bool Resize(size_t new_size, std::string* error_msg);
-
- void RecoverHoles(IRTSegmentState from);
-
// Abort if check_jni is not enabled. Otherwise, just log as an error.
static void AbortIfNoCheckJNI(const std::string& msg);
/* extra debugging checks */
bool CheckEntry(const char*, IndirectRef, uint32_t) const;
- /// semi-public - read/write by jni down calls.
- IRTSegmentState segment_state_;
-
- // Mem map where we store the indirect refs. If it's invalid, and table_ is non-null, then
- // table_ is valid, but was allocated via allocSmallIRT();
+ // Mem map where we store the indirect refs.
MemMap table_mem_map_;
- // bottom of the stack. Do not directly access the object references
+ // Bottom of the stack. Do not directly access the object references
// in this as they are roots. Use Get() that has a read barrier.
IrtEntry* table_;
- // bit mask, ORed into all irefs.
+ // Bit mask, ORed into all irefs.
const IndirectRefKind kind_;
- // max #of entries allowed (modulo resizing).
+ // The "top of stack" index where new references are added.
+ size_t top_index_;
+
+ // Maximum number of entries allowed.
size_t max_entries_;
- // Some values to retain old behavior with holes. Description of the algorithm is in the .cc
- // file.
+ // Some values to retain old behavior with holes.
+ // Description of the algorithm is in the .cc file.
// TODO: Consider other data structures for compact tables, e.g., free lists.
size_t current_num_holes_; // Number of holes in the current / top segment.
- IRTSegmentState last_known_previous_state_;
-
- // Whether the table's capacity may be resized. As there are no locks used, it is the caller's
- // responsibility to ensure thread-safety.
- ResizableCapacity resizable_;
};
} // namespace art
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 5da7a30..ac22f3f 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -28,7 +28,12 @@
using android::base::StringPrintf;
-class IndirectReferenceTableTest : public CommonRuntimeTest {};
+class IndirectReferenceTableTest : public CommonRuntimeTest {
+ protected:
+ IndirectReferenceTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -54,12 +59,10 @@
ScopedObjectAccess soa(Thread::Current());
static const size_t kTableMax = 20;
+ IndirectReferenceTable irt(kGlobal);
std::string error_msg;
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
+ bool success = irt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
StackHandleScope<5> hs(soa.Self());
Handle<mirror::Class> c =
@@ -74,21 +77,19 @@
Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
ASSERT_TRUE(obj3 != nullptr);
- const IRTSegmentState cookie = kIRTFirstSegment;
-
CheckDump(&irt, 0, 0);
IndirectRef iref0 = (IndirectRef) 0x11110;
- EXPECT_FALSE(irt.Remove(cookie, iref0)) << "unexpectedly successful removal";
+ EXPECT_FALSE(irt.Remove(iref0)) << "unexpectedly successful removal";
// Add three, check, remove in the order in which they were added.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- IndirectRef iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ IndirectRef iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 2, 2);
- IndirectRef iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ IndirectRef iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
@@ -96,11 +97,11 @@
EXPECT_OBJ_PTR_EQ(obj1.Get(), irt.Get(iref1));
EXPECT_OBJ_PTR_EQ(obj2.Get(), irt.Get(iref2));
- EXPECT_TRUE(irt.Remove(cookie, iref0));
+ EXPECT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 2, 2);
- EXPECT_TRUE(irt.Remove(cookie, iref1));
+ EXPECT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 1, 1);
- EXPECT_TRUE(irt.Remove(cookie, iref2));
+ EXPECT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 0, 0);
// Table should be empty now.
@@ -111,19 +112,19 @@
EXPECT_FALSE(irt.IsValidReference(iref0, &error_msg));
// Add three, remove in the opposite order.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
- iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
- ASSERT_TRUE(irt.Remove(cookie, iref2));
+ ASSERT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 2, 2);
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
// Table should be empty now.
@@ -131,27 +132,27 @@
// Add three, remove middle / middle / bottom / top. (Second attempt
// to remove middle should fail.)
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
- iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
ASSERT_EQ(3U, irt.Capacity());
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 2, 2);
- ASSERT_FALSE(irt.Remove(cookie, iref1));
+ ASSERT_FALSE(irt.Remove(iref1));
CheckDump(&irt, 2, 2);
// Check that the reference to the hole is not valid.
EXPECT_FALSE(irt.IsValidReference(iref1, &error_msg));
- ASSERT_TRUE(irt.Remove(cookie, iref2));
+ ASSERT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
// Table should be empty now.
@@ -160,35 +161,35 @@
// Add four entries. Remove #1, add new entry, verify that table size
// is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify
// that we delete one and don't hole-compact the other.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
- iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
- IndirectRef iref3 = irt.Add(cookie, obj3.Get(), &error_msg);
+ IndirectRef iref3 = irt.Add(obj3.Get(), &error_msg);
EXPECT_TRUE(iref3 != nullptr);
CheckDump(&irt, 4, 4);
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 3, 3);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
ASSERT_EQ(4U, irt.Capacity()) << "hole not filled";
CheckDump(&irt, 4, 4);
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 3, 3);
- ASSERT_TRUE(irt.Remove(cookie, iref3));
+ ASSERT_TRUE(irt.Remove(iref3));
CheckDump(&irt, 2, 2);
ASSERT_EQ(3U, irt.Capacity()) << "should be 3 after two deletions";
- ASSERT_TRUE(irt.Remove(cookie, iref2));
+ ASSERT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
ASSERT_EQ(0U, irt.Capacity()) << "not empty after split remove";
@@ -196,320 +197,72 @@
// Add an entry, remove it, add a new entry, and try to use the original
// iref. They have the same slot number but are for different objects.
// With the extended checks in place, this should fail.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded";
+ ASSERT_FALSE(irt.Remove(iref0)) << "mismatched del succeeded";
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref1)) << "switched del failed";
+ ASSERT_TRUE(irt.Remove(iref1)) << "switched del failed";
ASSERT_EQ(0U, irt.Capacity()) << "switching del not empty";
CheckDump(&irt, 0, 0);
// Same as above, but with the same object. A more rigorous checker
// (e.g. with slot serialization) will catch this.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
- iref1 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref1 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 1, 1);
if (iref0 != iref1) {
// Try 0, should not work.
- ASSERT_FALSE(irt.Remove(cookie, iref0)) << "temporal del succeeded";
+ ASSERT_FALSE(irt.Remove(iref0)) << "temporal del succeeded";
}
- ASSERT_TRUE(irt.Remove(cookie, iref1)) << "temporal cleanup failed";
+ ASSERT_TRUE(irt.Remove(iref1)) << "temporal cleanup failed";
ASSERT_EQ(0U, irt.Capacity()) << "temporal del not empty";
CheckDump(&irt, 0, 0);
// Stale reference is not valid.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
EXPECT_FALSE(irt.IsValidReference(iref0, &error_msg)) << "stale lookup succeeded";
CheckDump(&irt, 0, 0);
- // Test table resizing.
- // These ones fit...
+ // Test deleting all but the last entry.
+ // We shall delete these.
static const size_t kTableInitial = kTableMax / 2;
IndirectRef manyRefs[kTableInitial];
for (size_t i = 0; i < kTableInitial; i++) {
- manyRefs[i] = irt.Add(cookie, obj0.Get(), &error_msg);
+ manyRefs[i] = irt.Add(obj0.Get(), &error_msg);
ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
CheckDump(&irt, i + 1, 1);
}
- // ...this one causes overflow.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ // We shall keep this one.
+ iref0 = irt.Add(obj0.Get(), &error_msg);
ASSERT_TRUE(iref0 != nullptr);
ASSERT_EQ(kTableInitial + 1, irt.Capacity());
CheckDump(&irt, kTableInitial + 1, 1);
-
+ // Delete all but the last entry.
for (size_t i = 0; i < kTableInitial; i++) {
- ASSERT_TRUE(irt.Remove(cookie, manyRefs[i])) << "failed removing " << i;
+ ASSERT_TRUE(irt.Remove(manyRefs[i])) << "failed removing " << i;
CheckDump(&irt, kTableInitial - i, 1);
}
// Because of removal order, should have 11 entries, 10 of them holes.
ASSERT_EQ(kTableInitial + 1, irt.Capacity());
- ASSERT_TRUE(irt.Remove(cookie, iref0)) << "multi-remove final failed";
+ ASSERT_TRUE(irt.Remove(iref0)) << "multi-remove final failed";
ASSERT_EQ(0U, irt.Capacity()) << "multi-del not empty";
CheckDump(&irt, 0, 0);
}
-TEST_F(IndirectReferenceTableTest, Holes) {
- // Test the explicitly named cases from the IRT implementation:
- //
- // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
- // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
- // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
- // reference
- // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
- // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
- // reference
-
- ScopedObjectAccess soa(Thread::Current());
- static const size_t kTableMax = 10;
-
- StackHandleScope<6> hs(soa.Self());
- Handle<mirror::Class> c = hs.NewHandle(
- class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
- ASSERT_TRUE(c != nullptr);
- Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj0 != nullptr);
- Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj1 != nullptr);
- Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj2 != nullptr);
- Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj3 != nullptr);
- Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj4 != nullptr);
-
- std::string error_msg;
-
- // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference.
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
- IndirectRef iref1 = irt.Add(cookie0, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie0, obj2.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie0, iref1));
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref3 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- // Must not have filled the previous hole.
- EXPECT_EQ(irt.Capacity(), 4u);
- EXPECT_FALSE(irt.IsValidReference(iref1, &error_msg));
- CheckDump(&irt, 3, 3);
-
- UNUSED(iref0, iref1, iref2, iref3);
- }
-
- // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie1, obj2.Get(), &error_msg);
- IndirectRef iref3 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref2));
-
- // Pop segment.
- irt.SetSegmentState(cookie1);
-
- IndirectRef iref4 = irt.Add(cookie1, obj4.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 2u);
- EXPECT_FALSE(irt.IsValidReference(iref2, &error_msg));
- CheckDump(&irt, 2, 2);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4);
- }
-
- // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
- // reference.
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie1, obj2.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref1));
-
- // New segment.
- const IRTSegmentState cookie2 = irt.GetSegmentState();
-
- IndirectRef iref3 = irt.Add(cookie2, obj3.Get(), &error_msg);
-
- // Pop segment.
- irt.SetSegmentState(cookie2);
-
- IndirectRef iref4 = irt.Add(cookie1, obj4.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 3u);
- EXPECT_FALSE(irt.IsValidReference(iref1, &error_msg));
- CheckDump(&irt, 3, 3);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4);
- }
-
- // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference.
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- EXPECT_TRUE(irt.Remove(cookie1, iref1));
-
- // Emptied segment, push new one.
- const IRTSegmentState cookie2 = irt.GetSegmentState();
-
- IndirectRef iref2 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref3 = irt.Add(cookie1, obj2.Get(), &error_msg);
- IndirectRef iref4 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref3));
-
- // Pop segment.
- UNUSED(cookie2);
- irt.SetSegmentState(cookie1);
-
- IndirectRef iref5 = irt.Add(cookie1, obj4.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 2u);
- EXPECT_FALSE(irt.IsValidReference(iref3, &error_msg));
- CheckDump(&irt, 2, 2);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4, iref5);
- }
-
- // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
- // reference
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref3 = irt.Add(cookie1, obj2.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref2));
-
- // Pop segment.
- irt.SetSegmentState(cookie1);
-
- // Push segment.
- const IRTSegmentState cookie1_second = irt.GetSegmentState();
- UNUSED(cookie1_second);
-
- IndirectRef iref4 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 2u);
- EXPECT_FALSE(irt.IsValidReference(iref3, &error_msg));
- CheckDump(&irt, 2, 2);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4);
- }
-}
-
-TEST_F(IndirectReferenceTableTest, Resize) {
- ScopedObjectAccess soa(Thread::Current());
- static const size_t kTableMax = 512;
-
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> c = hs.NewHandle(
- class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
- ASSERT_TRUE(c != nullptr);
- Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj0 != nullptr);
-
- std::string error_msg;
- IndirectReferenceTable irt(kTableMax,
- kLocal,
- IndirectReferenceTable::ResizableCapacity::kYes,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- CheckDump(&irt, 0, 0);
- const IRTSegmentState cookie = kIRTFirstSegment;
-
- for (size_t i = 0; i != kTableMax + 1; ++i) {
- irt.Add(cookie, obj0.Get(), &error_msg);
- }
-
- EXPECT_EQ(irt.Capacity(), kTableMax + 1);
-}
-
} // namespace art
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 32a55e2..45143e4 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -55,6 +55,9 @@
#include "thread_list.h"
namespace art {
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks);
+extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self);
+
namespace instrumentation {
constexpr bool kVerboseInstrumentation = false;
@@ -104,72 +107,8 @@
Instrumentation* const instrumentation_;
};
-InstrumentationStackPopper::InstrumentationStackPopper(Thread* self)
- : self_(self),
- instrumentation_(Runtime::Current()->GetInstrumentation()),
- pop_until_(0u) {}
-
-InstrumentationStackPopper::~InstrumentationStackPopper() {
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- self_->GetInstrumentationStack();
- for (auto i = stack->begin(); i != stack->end() && i->first <= pop_until_;) {
- i = stack->erase(i);
- }
-}
-
-bool InstrumentationStackPopper::PopFramesTo(uintptr_t stack_pointer,
- MutableHandle<mirror::Throwable>& exception) {
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- self_->GetInstrumentationStack();
- DCHECK(!self_->IsExceptionPending());
- if (!instrumentation_->HasMethodUnwindListeners()) {
- pop_until_ = stack_pointer;
- return true;
- }
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Popping frames for exception " << exception->Dump();
- }
- // The instrumentation events expect the exception to be set.
- self_->SetException(exception.Get());
- bool new_exception_thrown = false;
- auto i = stack->upper_bound(pop_until_);
-
- // Now pop all frames until reaching stack_pointer, or a new exception is
- // thrown. Note that `stack_pointer` doesn't need to be a return PC address
- // (in fact the exception handling code passes the start of the frame where
- // the catch handler is).
- for (; i != stack->end() && i->first <= stack_pointer; i++) {
- const InstrumentationStackFrame& frame = i->second;
- ArtMethod* method = frame.method_;
- // Notify listeners of method unwind.
- // TODO: improve the dex_pc information here.
- uint32_t dex_pc = dex::kDexNoIndex;
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Popping for unwind " << method->PrettyMethod();
- }
- if (!method->IsRuntimeMethod() && !frame.interpreter_entry_) {
- instrumentation_->MethodUnwindEvent(self_, frame.this_object_, method, dex_pc);
- new_exception_thrown = self_->GetException() != exception.Get();
- if (new_exception_thrown) {
- pop_until_ = i->first;
- break;
- }
- }
- }
- if (!new_exception_thrown) {
- pop_until_ = stack_pointer;
- }
- exception.Assign(self_->GetException());
- self_->ClearException();
- if (kVerboseInstrumentation && new_exception_thrown) {
- LOG(INFO) << "Did partial pop of frames due to new exception";
- }
- return !new_exception_thrown;
-}
-
Instrumentation::Instrumentation()
- : current_force_deopt_id_(0),
- instrumentation_stubs_installed_(false),
+ : run_exit_hooks_(false),
instrumentation_level_(InstrumentationLevel::kInstrumentNothing),
forced_interpret_only_(false),
have_method_entry_listeners_(false),
@@ -182,10 +121,51 @@
have_watched_frame_pop_listeners_(false),
have_branch_listeners_(false),
have_exception_handled_listeners_(false),
- deoptimized_methods_lock_(new ReaderWriterMutex("deoptimized methods lock",
- kGenericBottomLock)),
quick_alloc_entry_points_instrumentation_counter_(0),
- alloc_entrypoints_instrumented_(false) {
+ alloc_entrypoints_instrumented_(false) {}
+
+bool Instrumentation::ProcessMethodUnwindCallbacks(Thread* self,
+ std::queue<ArtMethod*>& methods,
+ MutableHandle<mirror::Throwable>& exception) {
+ DCHECK(!self->IsExceptionPending());
+ if (!HasMethodUnwindListeners()) {
+ return true;
+ }
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Popping frames for exception " << exception->Dump();
+ }
+ // The instrumentation events expect the exception to be set.
+ self->SetException(exception.Get());
+ bool new_exception_thrown = false;
+
+ // Process callbacks for all methods that would be unwound until a new exception is thrown.
+ while (!methods.empty()) {
+ ArtMethod* method = methods.front();
+ methods.pop();
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Popping for unwind " << method->PrettyMethod();
+ }
+
+ if (method->IsRuntimeMethod()) {
+ continue;
+ }
+
+ // Notify listeners of method unwind.
+ // TODO: improve the dex_pc information here.
+ uint32_t dex_pc = dex::kDexNoIndex;
+ MethodUnwindEvent(self, method, dex_pc);
+ new_exception_thrown = self->GetException() != exception.Get();
+ if (new_exception_thrown) {
+ break;
+ }
+ }
+
+ exception.Assign(self->GetException());
+ self->ClearException();
+ if (kVerboseInstrumentation && new_exception_thrown) {
+ LOG(INFO) << "Did partial pop of frames due to new exception";
+ }
+ return !new_exception_thrown;
}
void Instrumentation::InstallStubsForClass(ObjPtr<mirror::Class> klass) {
@@ -206,15 +186,14 @@
return class_linker->IsQuickResolutionStub(code) ||
class_linker->IsQuickToInterpreterBridge(code) ||
class_linker->IsQuickGenericJniStub(code) ||
- (code == GetQuickInstrumentationEntryPoint());
+ (code == interpreter::GetNterpWithClinitEntryPoint());
}
static bool IsProxyInit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
// Annoyingly this can be called before we have actually initialized WellKnownClasses so therefore
// we also need to check this based on the declaring-class descriptor. The check is valid because
// Proxy only has a single constructor.
- ArtMethod* well_known_proxy_init = jni::DecodeArtMethod(
- WellKnownClasses::java_lang_reflect_Proxy_init);
+ ArtMethod* well_known_proxy_init = WellKnownClasses::java_lang_reflect_Proxy_init;
if (well_known_proxy_init == method) {
return true;
}
@@ -227,11 +206,53 @@
method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;");
}
+// Returns true if we need entry exit stub to call entry hooks. JITed code
+// directly call entry / exit hooks and don't need the stub.
+static bool CodeSupportsEntryExitHooks(const void* entry_point, ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Proxy.init should always run with the switch interpreter where entry / exit hooks are
+ // supported.
+ if (IsProxyInit(method)) {
+ return true;
+ }
+
+ // In some tests runtime isn't setup fully and hence the entry points could be nullptr.
+ // just be conservative and return false here.
+ if (entry_point == nullptr) {
+ return false;
+ }
+
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ // Interpreter supports entry / exit hooks. Resolution stubs fetch code that supports entry / exit
+ // hooks when required. So return true for both cases.
+ if (linker->IsQuickToInterpreterBridge(entry_point) ||
+ linker->IsQuickResolutionStub(entry_point)) {
+ return true;
+ }
+
+ // When jiting code for debuggable runtimes / instrumentation is active we generate the code to
+ // call method entry / exit hooks when required.
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr && jit->GetCodeCache()->ContainsPc(entry_point)) {
+ // If JITed code was compiled with instrumentation support we support entry / exit hooks.
+ OatQuickMethodHeader* header = OatQuickMethodHeader::FromEntryPoint(entry_point);
+ return CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr());
+ }
+
+ // GenericJni trampoline can handle entry / exit hooks.
+ if (linker->IsQuickGenericJniStub(entry_point)) {
+ return true;
+ }
+
+ // The remaining cases are nterp / oat code / JIT code that isn't compiled with instrumentation
+ // support.
+ return false;
+}
+
static void UpdateEntryPoints(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
- if (NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ if (method->StillNeedsClinitCheckMayBeDead()) {
CHECK(CanHandleInitializationCheck(quick_code));
}
jit::Jit* jit = Runtime::Current()->GetJit();
@@ -241,8 +262,9 @@
CHECK_EQ(reinterpret_cast<uintptr_t>(quick_code) & 1, 1u);
}
}
- if (IsProxyInit(method)) {
- CHECK_NE(quick_code, GetQuickInstrumentationEntryPoint());
+ const Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ if (instr->EntryExitStubsInstalled()) {
+ DCHECK(CodeSupportsEntryExitHooks(quick_code, method));
}
}
// If the method is from a boot image, don't dirty it if the entrypoint
@@ -252,64 +274,22 @@
}
}
-bool Instrumentation::CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Proxy.init should never have entry/exit stubs.
- if (IsProxyInit(method)) {
- return false;
- }
-
- // In some tests runtime isn't setup fully and hence the entry points could
- // be nullptr.
- if (code == nullptr) {
- return true;
- }
-
- // Code running in the interpreter doesn't need entry/exit stubs.
- if (Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code)) {
- return false;
- }
-
- // When jiting code for debuggable apps we generate the code to call method
- // entry / exit hooks when required. Hence it is not required to update
- // to instrumentation entry point for JITed code in debuggable mode.
- if (!Runtime::Current()->IsJavaDebuggable()) {
- return true;
- }
-
- // Native functions can have JITed entry points but we don't include support
- // for calling entry / exit hooks directly from the JITed code for native
- // functions. So we still have to install entry exit stubs for such cases.
- if (method->IsNative()) {
- return true;
- }
-
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
- return false;
- }
- return true;
+bool Instrumentation::NeedsDexPcEvents(ArtMethod* method, Thread* thread) {
+ return (InterpretOnly(method) || thread->IsForceInterpreter()) && HasDexPcListeners();
}
bool Instrumentation::InterpretOnly(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
if (method->IsNative()) {
return false;
}
- return InterpretOnly() ||
- IsDeoptimized(method) ||
- Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method);
+ return InterpretOnly() || IsDeoptimized(method);
}
-static bool CanUseAotCode(ArtMethod* method, const void* quick_code)
+static bool CanUseAotCode(const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (quick_code == nullptr) {
return false;
}
- if (method->IsNative()) {
- // AOT code for native methods can always be used.
- return true;
- }
-
Runtime* runtime = Runtime::Current();
// For simplicity, we never use AOT code for debuggable.
if (runtime->IsJavaDebuggable()) {
@@ -332,7 +312,7 @@
static bool CanUseNterp(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
return interpreter::CanRuntimeUseNterp() &&
CanMethodUseNterp(method) &&
- method->GetDeclaringClass()->IsVerified();
+ method->IsDeclaringClassVerifiedMayBeDead();
}
static const void* GetOptimizedCodeFor(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -345,7 +325,7 @@
// In debuggable mode, we can only use AOT code for native methods.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const void* aot_code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize());
- if (CanUseAotCode(method, aot_code)) {
+ if (CanUseAotCode(aot_code)) {
return aot_code;
}
@@ -370,24 +350,24 @@
void Instrumentation::InitializeMethodsCode(ArtMethod* method, const void* aot_code)
REQUIRES_SHARED(Locks::mutator_lock_) {
- // Use instrumentation entrypoints if instrumentation is installed.
- if (UNLIKELY(EntryExitStubsInstalled()) && !IsProxyInit(method)) {
- if (!method->IsNative() && InterpretOnly(method)) {
- UpdateEntryPoints(method, GetQuickToInterpreterBridge());
- } else {
- UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
- }
+ if (!method->IsInvokable()) {
+ DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr ||
+ Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(
+ method->GetEntryPointFromQuickCompiledCode()));
+ UpdateEntryPoints(method, GetQuickToInterpreterBridge());
return;
}
- if (UNLIKELY(IsForcedInterpretOnly() || IsDeoptimized(method))) {
+ // Use instrumentation entrypoints if instrumentation is installed.
+ if (UNLIKELY(EntryExitStubsInstalled() || IsForcedInterpretOnly() || IsDeoptimized(method))) {
UpdateEntryPoints(
method, method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
return;
}
// Special case if we need an initialization check.
- if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ // The method and its declaring class may be dead when starting JIT GC during managed heap GC.
+ if (method->StillNeedsClinitCheckMayBeDead()) {
// If we have code but the method needs a class initialization check before calling
// that code, install the resolution stub that will perform the check.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
@@ -396,7 +376,12 @@
// stub only if we have compiled code or we can execute nterp, and the method needs a class
// initialization check.
if (aot_code != nullptr || method->IsNative() || CanUseNterp(method)) {
- UpdateEntryPoints(method, GetQuickResolutionStub());
+ if (kIsDebugBuild && CanUseNterp(method)) {
+ // Adds some test coverage for the nterp clinit entrypoint.
+ UpdateEntryPoints(method, interpreter::GetNterpWithClinitEntryPoint());
+ } else {
+ UpdateEntryPoints(method, GetQuickResolutionStub());
+ }
} else {
UpdateEntryPoints(method, GetQuickToInterpreterBridge());
}
@@ -404,7 +389,7 @@
}
// Use the provided AOT code if possible.
- if (CanUseAotCode(method, aot_code)) {
+ if (CanUseAotCode(aot_code)) {
UpdateEntryPoints(method, aot_code);
return;
}
@@ -442,9 +427,11 @@
}
if (EntryExitStubsInstalled()) {
- // Install the instrumentation entry point if needed.
- if (CodeNeedsEntryExitStub(method->GetEntryPointFromQuickCompiledCode(), method)) {
- UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
+ // Install interpreter bridge / GenericJni stub if the existing code doesn't support
+ // entry / exit hooks.
+ if (!CodeSupportsEntryExitHooks(method->GetEntryPointFromQuickCompiledCode(), method)) {
+ UpdateEntryPoints(
+ method, method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
}
return;
}
@@ -452,13 +439,45 @@
// We're being asked to restore the entrypoints after instrumentation.
CHECK_EQ(instrumentation_level_, InstrumentationLevel::kInstrumentNothing);
// We need to have the resolution stub still if the class is not initialized.
- if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ if (method->StillNeedsClinitCheck()) {
UpdateEntryPoints(method, GetQuickResolutionStub());
return;
}
UpdateEntryPoints(method, GetOptimizedCodeFor(method));
}
+void Instrumentation::UpdateEntrypointsForDebuggable() {
+ Runtime* runtime = Runtime::Current();
+ // If we are transitioning from non-debuggable to debuggable, we patch
+ // entry points of methods to remove any aot / JITed entry points.
+ InstallStubsClassVisitor visitor(this);
+ runtime->GetClassLinker()->VisitClasses(&visitor);
+}
+
+bool Instrumentation::MethodSupportsExitEvents(ArtMethod* method,
+ const OatQuickMethodHeader* header) {
+ if (header == nullptr) {
+ // Header can be a nullptr for runtime / proxy methods that doesn't support method exit hooks
+ // or for native methods that use generic jni stubs. Generic jni stubs support method exit
+ // hooks.
+ return method->IsNative();
+ }
+
+ if (header->IsNterpMethodHeader()) {
+ // Nterp doesn't support method exit events
+ return false;
+ }
+
+ DCHECK(header->IsOptimized());
+ if (CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr())) {
+ // For optimized code, we only support method entry / exit hooks if they are compiled as
+ // debuggable.
+ return true;
+ }
+
+ return false;
+}
+
// Places the instrumentation exit pc as the return PC for every quick frame. This also allows
// deoptimization of quick frames to interpreter frames. When force_deopt is
// true the frames have to be deoptimized. If the frame has a deoptimization
@@ -474,120 +493,71 @@
struct InstallStackVisitor final : public StackVisitor {
InstallStackVisitor(Thread* thread_in,
Context* context,
- uintptr_t instrumentation_exit_pc,
- uint64_t force_deopt_id,
bool deopt_all_frames)
: StackVisitor(thread_in, context, kInstrumentationStackWalk),
- instrumentation_stack_(thread_in->GetInstrumentationStack()),
- instrumentation_exit_pc_(instrumentation_exit_pc),
- reached_existing_instrumentation_frames_(false),
- force_deopt_id_(force_deopt_id),
- deopt_all_frames_(deopt_all_frames) {}
+ deopt_all_frames_(deopt_all_frames),
+ runtime_methods_need_deopt_check_(false) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* m = GetMethod();
- if (m == nullptr) {
+ if (m == nullptr || m->IsRuntimeMethod()) {
if (kVerboseInstrumentation) {
- LOG(INFO) << " Skipping upcall. Frame " << GetFrameId();
+ LOG(INFO) << " Skipping upcall / runtime method. Frame " << GetFrameId();
}
- return true; // Ignore upcalls.
+ return true; // Ignore upcalls and runtime methods.
}
- if (GetCurrentQuickFrame() == nullptr) {
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Pushing shadow frame method " << m->PrettyMethod();
- }
+
+ bool is_shadow_frame = GetCurrentQuickFrame() == nullptr;
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Processing frame: method: " << m->PrettyMethod()
+ << " is_shadow_frame: " << is_shadow_frame;
+ }
+
+ // Handle interpreter frame.
+ if (is_shadow_frame) {
+ // Since we are updating the instrumentation related information we have to recalculate
+ // NeedsDexPcEvents. For example, when a new method or thread is deoptimized / interpreter
+ // stubs are installed the NeedsDexPcEvents could change for the shadow frames on the stack.
+ // If we don't update it here we would miss reporting dex pc events which is incorrect.
+ ShadowFrame* shadow_frame = GetCurrentShadowFrame();
+ DCHECK(shadow_frame != nullptr);
+ shadow_frame->SetNotifyDexPcMoveEvents(
+ Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(GetMethod(), GetThread()));
stack_methods_.push_back(m);
return true; // Continue.
}
- uintptr_t return_pc = GetReturnPc();
- if (kVerboseInstrumentation) {
- LOG(INFO) << " Installing exit stub in " << DescribeLocation();
+
+ DCHECK(!m->IsRuntimeMethod());
+ const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ if (Runtime::Current()->GetInstrumentation()->MethodSupportsExitEvents(m, method_header)) {
+ // It is unexpected to see a method enter event but not a method exit event so record stack
+ // methods only for frames that support method exit events. Even if we deoptimize we make
+ // sure that we only call method exit event if the frame supported it in the first place.
+ // For ex: deoptimizing from JITed code with debug support calls a method exit hook but
+ // deoptimizing from nterp doesn't.
+ stack_methods_.push_back(m);
}
- if (return_pc == instrumentation_exit_pc_) {
- auto it = instrumentation_stack_->find(GetReturnPcAddr());
- CHECK(it != instrumentation_stack_->end());
- const InstrumentationStackFrame& frame = it->second;
- if (m->IsRuntimeMethod()) {
- if (frame.interpreter_entry_) {
- return true;
- }
- }
- // We've reached a frame which has already been installed with instrumentation exit stub.
- // We should have already installed instrumentation or be interpreter on previous frames.
- reached_existing_instrumentation_frames_ = true;
-
- // Trampolines get replaced with their actual method in the stack,
- // so don't do the check below for runtime methods.
- if (!frame.method_->IsRuntimeMethod()) {
- CHECK_EQ(m->GetNonObsoleteMethod(), frame.method_->GetNonObsoleteMethod())
- << "Expected " << ArtMethod::PrettyMethod(m)
- << ", Found " << ArtMethod::PrettyMethod(frame.method_);
+ // If it is a JITed frame then just set the deopt bit if required otherwise continue.
+ // We need kForceDeoptForRedefinition to ensure we don't use any JITed code after a
+ // redefinition. We support redefinition only if the runtime has started off as a
+ // debuggable runtime which makes sure we don't use any AOT or Nterp code.
+ // The CheckCallerForDeopt is an optimization which we only do for non-native JITed code for
+ // now. We can extend it to native methods but that needs reserving an additional stack slot.
+ // We don't do it currently since that wasn't important for debugger performance.
+ if (method_header != nullptr && method_header->HasShouldDeoptimizeFlag()) {
+ if (deopt_all_frames_) {
+ runtime_methods_need_deopt_check_ = true;
+ SetShouldDeoptimizeFlag(DeoptimizeFlagValue::kForceDeoptForRedefinition);
}
- return_pc = frame.return_pc_;
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Ignoring already instrumented " << frame.Dump();
- }
- } else {
- if (!m->IsRuntimeMethod()) {
- // Record the method so we can call method entry callbacks for all non-runtime methods on
- // the stack. Runtime methods don't need method entry callbacks.
- // TODO(232212577): Add tests to check the validity of the tracefiles generated.
- // Currently the tracing tests only check a trace file is generated.
- stack_methods_.push_back(m);
- }
-
- // If it is a JITed frame then just set the deopt bit if required
- // otherwise continue
- const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
- if (method_header != nullptr && method_header->HasShouldDeoptimizeFlag()) {
- if (deopt_all_frames_) {
- SetShouldDeoptimizeFlag(DeoptimizeFlagValue::kDebug);
- }
- return true;
- }
- CHECK_NE(return_pc, 0U);
- if (UNLIKELY(reached_existing_instrumentation_frames_ && !m->IsRuntimeMethod())) {
- // We already saw an existing instrumentation frame so this should be a runtime-method
- // inserted by the interpreter or runtime.
- std::string thread_name;
- GetThread()->GetThreadName(thread_name);
- LOG(FATAL) << "While walking " << thread_name << " found unexpected non-runtime method"
- << " without instrumentation exit return or interpreter frame."
- << " method is " << GetMethod()->PrettyMethod()
- << " return_pc is " << std::hex << return_pc;
- UNREACHABLE();
- }
- if (m->IsRuntimeMethod()) {
- size_t frame_size = GetCurrentQuickFrameInfo().FrameSizeInBytes();
- ArtMethod** caller_frame = reinterpret_cast<ArtMethod**>(
- reinterpret_cast<uint8_t*>(GetCurrentQuickFrame()) + frame_size);
- if (*caller_frame != nullptr && (*caller_frame)->IsNative()) {
- // Do not install instrumentation exit on return to JNI stubs.
- return true;
- }
- }
- InstrumentationStackFrame instrumentation_frame(
- m->IsRuntimeMethod() ? nullptr : GetThisObject().Ptr(),
- m,
- return_pc,
- false,
- force_deopt_id_);
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump();
- }
-
- instrumentation_stack_->insert({GetReturnPcAddr(), instrumentation_frame});
- SetReturnPc(instrumentation_exit_pc_);
+ SetShouldDeoptimizeFlag(DeoptimizeFlagValue::kCheckCallerForDeopt);
}
+
return true; // Continue.
}
- std::map<uintptr_t, InstrumentationStackFrame>* const instrumentation_stack_;
std::vector<ArtMethod*> stack_methods_;
- const uintptr_t instrumentation_exit_pc_;
- bool reached_existing_instrumentation_frames_;
- uint64_t force_deopt_id_;
bool deopt_all_frames_;
+ bool runtime_methods_need_deopt_check_;
};
if (kVerboseInstrumentation) {
std::string thread_name;
@@ -597,14 +567,15 @@
Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
std::unique_ptr<Context> context(Context::Create());
- uintptr_t instrumentation_exit_pc = reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc());
InstallStackVisitor visitor(thread,
context.get(),
- instrumentation_exit_pc,
- instrumentation->current_force_deopt_id_,
deopt_all_frames);
visitor.WalkStack(true);
+ if (visitor.runtime_methods_need_deopt_check_) {
+ thread->SetDeoptCheckRequired(true);
+ }
+
if (instrumentation->ShouldNotifyMethodEnterExitEvents()) {
// Create method enter events for all methods currently on the thread's stack. We only do this
// if we haven't already processed the method enter events.
@@ -615,107 +586,120 @@
thread->VerifyStack();
}
+void UpdateNeedsDexPcEventsOnStack(Thread* thread) REQUIRES(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+
+ struct InstallStackVisitor final : public StackVisitor {
+ InstallStackVisitor(Thread* thread_in, Context* context)
+ : StackVisitor(thread_in, context, kInstrumentationStackWalk) {}
+
+ bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
+ ShadowFrame* shadow_frame = GetCurrentShadowFrame();
+ if (shadow_frame != nullptr) {
+ shadow_frame->SetNotifyDexPcMoveEvents(
+ Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(GetMethod(), GetThread()));
+ }
+ return true;
+ }
+ };
+
+ if (kVerboseInstrumentation) {
+ std::string thread_name;
+ thread->GetThreadName(thread_name);
+ LOG(INFO) << "Updating DexPcMoveEvents on shadow frames on stack " << thread_name;
+ }
+
+ std::unique_ptr<Context> context(Context::Create());
+ InstallStackVisitor visitor(thread, context.get());
+ visitor.WalkStack(true);
+}
+
void Instrumentation::InstrumentThreadStack(Thread* thread, bool force_deopt) {
- instrumentation_stubs_installed_ = true;
+ run_exit_hooks_ = true;
InstrumentationInstallStack(thread, this, force_deopt);
}
-// Removes the instrumentation exit pc as the return PC for every quick frame.
-static void InstrumentationRestoreStack(Thread* thread, void* arg)
- REQUIRES(Locks::mutator_lock_) {
+void Instrumentation::InstrumentAllThreadStacks(bool force_deopt) {
+ run_exit_hooks_ = true;
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
+ InstrumentThreadStack(thread, force_deopt);
+ }
+}
+
+static void InstrumentationRestoreStack(Thread* thread) REQUIRES(Locks::mutator_lock_) {
Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
struct RestoreStackVisitor final : public StackVisitor {
- RestoreStackVisitor(Thread* thread_in, uintptr_t instrumentation_exit_pc,
- Instrumentation* instrumentation)
- : StackVisitor(thread_in, nullptr, kInstrumentationStackWalk),
- thread_(thread_in),
- instrumentation_exit_pc_(instrumentation_exit_pc),
- instrumentation_(instrumentation),
- instrumentation_stack_(thread_in->GetInstrumentationStack()),
- frames_removed_(0) {}
+ RestoreStackVisitor(Thread* thread)
+ : StackVisitor(thread, nullptr, kInstrumentationStackWalk), thread_(thread) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
- if (instrumentation_stack_->size() == 0) {
- return false; // Stop.
- }
- ArtMethod* m = GetMethod();
if (GetCurrentQuickFrame() == nullptr) {
- if (kVerboseInstrumentation) {
- LOG(INFO) << " Ignoring a shadow frame. Frame " << GetFrameId()
- << " Method=" << ArtMethod::PrettyMethod(m);
- }
- return true; // Ignore shadow frames.
+ return true;
}
- if (m == nullptr) {
- if (kVerboseInstrumentation) {
- LOG(INFO) << " Skipping upcall. Frame " << GetFrameId();
- }
- return true; // Ignore upcalls.
+
+ const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ if (method_header != nullptr && method_header->HasShouldDeoptimizeFlag()) {
+ // We shouldn't restore stack if any of the frames need a force deopt
+ DCHECK(!ShouldForceDeoptForRedefinition());
+ UnsetShouldDeoptimizeFlag(DeoptimizeFlagValue::kCheckCallerForDeopt);
}
- auto it = instrumentation_stack_->find(GetReturnPcAddr());
- if (it != instrumentation_stack_->end()) {
- const InstrumentationStackFrame& instrumentation_frame = it->second;
- if (kVerboseInstrumentation) {
- LOG(INFO) << " Removing exit stub in " << DescribeLocation();
- }
- if (instrumentation_frame.interpreter_entry_) {
- CHECK(m == Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
- } else {
- CHECK_EQ(m->GetNonObsoleteMethod(),
- instrumentation_frame.method_->GetNonObsoleteMethod())
- << ArtMethod::PrettyMethod(m)
- << " and " << instrumentation_frame.method_->GetNonObsoleteMethod()->PrettyMethod();
- }
- SetReturnPc(instrumentation_frame.return_pc_);
- if (instrumentation_->ShouldNotifyMethodEnterExitEvents() &&
- !m->IsRuntimeMethod()) {
- // Create the method exit events. As the methods didn't really exit the result is 0.
- // We only do this if no debugger is attached to prevent from posting events twice.
- JValue val;
- instrumentation_->MethodExitEvent(thread_, m, OptionalFrame{}, val);
- }
- frames_removed_++;
- } else {
- if (kVerboseInstrumentation) {
- LOG(INFO) << " No exit stub in " << DescribeLocation();
+ return true; // Continue.
+ }
+ Thread* const thread_;
+ };
+
+ if (kVerboseInstrumentation) {
+ std::string thread_name;
+ thread->GetThreadName(thread_name);
+ LOG(INFO) << "Restoring stack for " << thread_name;
+ }
+ DCHECK(!thread->IsDeoptCheckRequired());
+ RestoreStackVisitor visitor(thread);
+ visitor.WalkStack(true);
+}
+
+static bool HasFramesNeedingForceDeopt(Thread* thread) REQUIRES(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+
+ struct CheckForForceDeoptStackVisitor final : public StackVisitor {
+ CheckForForceDeoptStackVisitor(Thread* thread)
+ : StackVisitor(thread, nullptr, kInstrumentationStackWalk),
+ thread_(thread),
+ force_deopt_check_needed_(false) {}
+
+ bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (GetCurrentQuickFrame() == nullptr) {
+ return true;
+ }
+
+ const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ if (method_header != nullptr && method_header->HasShouldDeoptimizeFlag()) {
+ if (ShouldForceDeoptForRedefinition()) {
+ force_deopt_check_needed_ = true;
+ return false;
}
}
return true; // Continue.
}
Thread* const thread_;
- const uintptr_t instrumentation_exit_pc_;
- Instrumentation* const instrumentation_;
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* const instrumentation_stack_;
- size_t frames_removed_;
+ bool force_deopt_check_needed_;
};
- if (kVerboseInstrumentation) {
- std::string thread_name;
- thread->GetThreadName(thread_name);
- LOG(INFO) << "Removing exit stubs in " << thread_name;
- }
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- thread->GetInstrumentationStack();
- if (stack->size() > 0) {
- Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
- uintptr_t instrumentation_exit_pc =
- reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc());
- RestoreStackVisitor visitor(thread, instrumentation_exit_pc, instrumentation);
- visitor.WalkStack(true);
- CHECK_EQ(visitor.frames_removed_, stack->size());
- stack->clear();
- }
+
+ CheckForForceDeoptStackVisitor visitor(thread);
+ visitor.WalkStack(true);
+ // If there is a frame that requires a force deopt we should have set the IsDeoptCheckRequired
+ // bit. We don't check if the bit needs to be reset on every method exit / deoptimization. We
+ // only check when we no longer need instrumentation support. So it is possible that the bit is
+ // set but we don't find any frames that need a force deopt on the stack so reverse implication
+ // doesn't hold.
+ DCHECK_IMPLIES(visitor.force_deopt_check_needed_, thread->IsDeoptCheckRequired());
+ return visitor.force_deopt_check_needed_;
}
void Instrumentation::DeoptimizeAllThreadFrames() {
- Thread* self = Thread::Current();
- MutexLock mu(self, *Locks::thread_list_lock_);
- ThreadList* tl = Runtime::Current()->GetThreadList();
- tl->ForEach([&](Thread* t) {
- Locks::mutator_lock_->AssertExclusiveHeld(self);
- InstrumentThreadStack(t, /* deopt_all_frames= */ true);
- });
- current_force_deopt_id_++;
+ InstrumentAllThreadStacks(/* force_deopt= */ true);
}
static bool HasEvent(Instrumentation::InstrumentationEvent expected, uint32_t events) {
@@ -795,6 +779,12 @@
exception_handled_listeners_,
listener,
&have_exception_handled_listeners_);
+ if (HasEvent(kDexPcMoved, events)) {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
+ UpdateNeedsDexPcEventsOnStack(thread);
+ }
+ }
}
static void PotentiallyRemoveListenerFrom(Instrumentation::InstrumentationEvent event,
@@ -876,6 +866,12 @@
exception_handled_listeners_,
listener,
&have_exception_handled_listeners_);
+ if (HasEvent(kDexPcMoved, events)) {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
+ UpdateNeedsDexPcEventsOnStack(thread);
+ }
+ }
}
Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentationLevel() const {
@@ -904,6 +900,11 @@
instrumentation_level_ = requested_level;
}
+void Instrumentation::EnableEntryExitHooks(const char* key) {
+ DCHECK(Runtime::Current()->IsJavaDebuggable());
+ ConfigureStubs(key, InstrumentationLevel::kInstrumentWithEntryExitHooks);
+}
+
void Instrumentation::MaybeRestoreInstrumentationStack() {
// Restore stack only if there is no method currently deoptimized.
if (!IsDeoptimizedMethodsEmpty()) {
@@ -920,21 +921,21 @@
// exclusively at this point.
Locks::mutator_lock_->AssertExclusiveHeld(self);
Runtime::Current()->GetThreadList()->ForEach([&](Thread* t) NO_THREAD_SAFETY_ANALYSIS {
+ bool has_force_deopt_frames = HasFramesNeedingForceDeopt(t);
+ if (!has_force_deopt_frames) {
+ // We no longer have any frames that require a force deopt check. If the bit was true then we
+ // had some frames earlier but they already got deoptimized and are no longer on stack.
+ t->SetDeoptCheckRequired(false);
+ }
no_remaining_deopts =
no_remaining_deopts &&
!t->IsForceInterpreter() &&
!t->HasDebuggerShadowFrames() &&
- std::all_of(t->GetInstrumentationStack()->cbegin(),
- t->GetInstrumentationStack()->cend(),
- [&](const auto& frame) REQUIRES_SHARED(Locks::mutator_lock_) {
- return frame.second.force_deopt_id_ == current_force_deopt_id_;
- });
+ !has_force_deopt_frames;
});
if (no_remaining_deopts) {
- Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this);
- // Only do this after restoring, as walking the stack when restoring will see
- // the instrumentation exit pc.
- instrumentation_stubs_installed_ = false;
+ Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack);
+ run_exit_hooks_ = false;
}
}
@@ -954,17 +955,11 @@
Locks::mutator_lock_->AssertExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
UpdateInstrumentationLevel(requested_level);
+ InstallStubsClassVisitor visitor(this);
+ runtime->GetClassLinker()->VisitClasses(&visitor);
if (requested_level > InstrumentationLevel::kInstrumentNothing) {
- InstallStubsClassVisitor visitor(this);
- runtime->GetClassLinker()->VisitClasses(&visitor);
- instrumentation_stubs_installed_ = true;
- MutexLock mu(self, *Locks::thread_list_lock_);
- for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
- InstrumentThreadStack(thread, /* deopt_all_frames= */ false);
- }
+ InstrumentAllThreadStacks(/* force_deopt= */ false);
} else {
- InstallStubsClassVisitor visitor(this);
- runtime->GetClassLinker()->VisitClasses(&visitor);
MaybeRestoreInstrumentationStack();
}
}
@@ -1042,14 +1037,14 @@
return "interpreter";
} else if (class_linker->IsQuickResolutionStub(code)) {
return "resolution";
- } else if (code == GetQuickInstrumentationEntryPoint()) {
- return "instrumentation";
} else if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
return "jit";
} else if (code == GetInvokeObsoleteMethodStub()) {
return "obsolete";
} else if (code == interpreter::GetNterpEntryPoint()) {
return "nterp";
+ } else if (code == interpreter::GetNterpWithClinitEntryPoint()) {
+ return "nterp with clinit";
} else if (class_linker->IsQuickGenericJniStub(code)) {
return "generic jni";
} else if (Runtime::Current()->GetOatFileManager().ContainsPc(code)) {
@@ -1059,7 +1054,7 @@
}
void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code) {
- if (!AreExitStubsInstalled()) {
+ if (!EntryExitStubsInstalled()) {
// Fast path: no instrumentation.
DCHECK(!IsDeoptimized(method));
UpdateEntryPoints(method, new_code);
@@ -1080,12 +1075,11 @@
return;
}
- if (EntryExitStubsInstalled() && CodeNeedsEntryExitStub(new_code, method)) {
- DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickInstrumentationEntryPoint() ||
- class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()))
- << EntryPointString(method->GetEntryPointFromQuickCompiledCode())
- << " " << method->PrettyMethod();
- // If the code we want to update the method with still needs entry/exit stub, just skip.
+ if (EntryExitStubsInstalled() && !CodeSupportsEntryExitHooks(new_code, method)) {
+ DCHECK(CodeSupportsEntryExitHooks(method->GetEntryPointFromQuickCompiledCode(), method))
+ << EntryPointString(method->GetEntryPointFromQuickCompiledCode()) << " "
+ << method->PrettyMethod();
+ // If we need entry / exit stubs but the new_code doesn't support entry / exit hooks just skip.
return;
}
@@ -1097,8 +1091,9 @@
// We don't do any read barrier on `method`'s declaring class in this code, as the JIT might
// enter here on a soon-to-be deleted ArtMethod. Updating the entrypoint is OK though, as
// the ArtMethod is still in memory.
- if (EntryExitStubsInstalled()) {
- // If stubs are installed don't update.
+ if (EntryExitStubsInstalled() && !CodeSupportsEntryExitHooks(new_code, method)) {
+ // If the new code doesn't support entry exit hooks but we need them don't update with the new
+ // code.
return;
}
UpdateEntryPoints(method, new_code);
@@ -1123,14 +1118,6 @@
return deoptimized_methods_.find(method) != deoptimized_methods_.end();
}
-ArtMethod* Instrumentation::BeginDeoptimizedMethod() {
- if (deoptimized_methods_.empty()) {
- // Empty.
- return nullptr;
- }
- return *deoptimized_methods_.begin();
-}
-
bool Instrumentation::RemoveDeoptimizedMethod(ArtMethod* method) {
auto it = deoptimized_methods_.find(method);
if (it == deoptimized_methods_.end()) {
@@ -1140,18 +1127,13 @@
return true;
}
-bool Instrumentation::IsDeoptimizedMethodsEmptyLocked() const {
- return deoptimized_methods_.empty();
-}
-
void Instrumentation::Deoptimize(ArtMethod* method) {
CHECK(!method->IsNative());
CHECK(!method->IsProxyMethod());
CHECK(method->IsInvokable());
- Thread* self = Thread::Current();
{
- WriterMutexLock mu(self, *GetDeoptimizedMethodsLock());
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
bool has_not_been_deoptimized = AddDeoptimizedMethod(method);
CHECK(has_not_been_deoptimized) << "Method " << ArtMethod::PrettyMethod(method)
<< " is already deoptimized";
@@ -1159,16 +1141,10 @@
if (!InterpreterStubsInstalled()) {
UpdateEntryPoints(method, GetQuickToInterpreterBridge());
- // Install instrumentation exit stub and instrumentation frames. We may already have installed
- // these previously so it will only cover the newly created frames.
- instrumentation_stubs_installed_ = true;
- MutexLock mu(self, *Locks::thread_list_lock_);
- for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
- // This isn't a strong deopt. We deopt this method if it is still in the
- // deopt methods list. If by the time we hit this frame we no longer need
- // a deopt it is safe to continue. So we don't mark the frame.
- InstrumentThreadStack(thread, /* deopt_all_frames= */ false);
- }
+ // Instrument thread stacks to request a check if the caller needs a deoptimization.
+ // This isn't a strong deopt. We deopt this method if it is still in the deopt methods list.
+ // If by the time we hit this frame we no longer need a deopt it is safe to continue.
+ InstrumentAllThreadStacks(/* force_deopt= */ false);
}
}
@@ -1177,9 +1153,8 @@
CHECK(!method->IsProxyMethod());
CHECK(method->IsInvokable());
- Thread* self = Thread::Current();
{
- WriterMutexLock mu(self, *GetDeoptimizedMethodsLock());
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
bool found_and_erased = RemoveDeoptimizedMethod(method);
CHECK(found_and_erased) << "Method " << ArtMethod::PrettyMethod(method)
<< " is not deoptimized";
@@ -1190,12 +1165,19 @@
return;
}
+ if (method->IsObsolete()) {
+ // Don't update entry points for obsolete methods. The entrypoint should
+ // have been set to InvokeObsoleteMethoStub.
+ DCHECK_EQ(method->GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize),
+ GetInvokeObsoleteMethodStub());
+ return;
+ }
+
// We are not using interpreter stubs for deoptimization. Restore the code of the method.
// We still retain interpreter bridge if we need it for other reasons.
if (InterpretOnly(method)) {
UpdateEntryPoints(method, GetQuickToInterpreterBridge());
- } else if (NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ } else if (method->StillNeedsClinitCheck()) {
UpdateEntryPoints(method, GetQuickResolutionStub());
} else {
UpdateEntryPoints(method, GetMaybeInstrumentedCodeForInvoke(method));
@@ -1208,35 +1190,53 @@
}
bool Instrumentation::IsDeoptimizedMethodsEmpty() const {
- ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
return deoptimized_methods_.empty();
}
bool Instrumentation::IsDeoptimized(ArtMethod* method) {
DCHECK(method != nullptr);
- ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
return IsDeoptimizedMethod(method);
}
-
void Instrumentation::DisableDeoptimization(const char* key) {
// Remove any instrumentation support added for deoptimization.
ConfigureStubs(key, InstrumentationLevel::kInstrumentNothing);
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
// Undeoptimized selected methods.
while (true) {
ArtMethod* method;
{
- ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
- if (IsDeoptimizedMethodsEmptyLocked()) {
+ if (deoptimized_methods_.empty()) {
break;
}
- method = BeginDeoptimizedMethod();
+ method = *deoptimized_methods_.begin();
CHECK(method != nullptr);
}
Undeoptimize(method);
}
}
+void Instrumentation::MaybeSwitchRuntimeDebugState(Thread* self) {
+ Runtime* runtime = Runtime::Current();
+ // Return early if runtime is shutting down.
+ if (runtime->IsShuttingDown(self)) {
+ return;
+ }
+
+ // Don't switch the state if we started off as JavaDebuggable or if we still need entry / exit
+ // hooks for other reasons.
+ if (EntryExitStubsInstalled() || runtime->IsJavaDebuggableAtInit()) {
+ return;
+ }
+
+ art::jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr) {
+ jit->GetCodeCache()->InvalidateAllCompiledCode();
+ jit->GetJitCompiler()->SetDebuggableCompilerOption(false);
+ }
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kNonJavaDebuggable);
+}
+
// Indicates if instrumentation should notify method enter/exit events to the listeners.
bool Instrumentation::ShouldNotifyMethodEnterExitEvents() const {
if (!HasMethodEntryListeners() && !HasMethodExitListeners()) {
@@ -1259,7 +1259,7 @@
if (needs_interpreter) {
level = InstrumentationLevel::kInstrumentWithInterpreter;
} else {
- level = InstrumentationLevel::kInstrumentWithInstrumentationStubs;
+ level = InstrumentationLevel::kInstrumentWithEntryExitHooks;
}
ConfigureStubs(key, level);
}
@@ -1275,10 +1275,9 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const void* code = method->GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize);
// If we don't have the instrumentation, the resolution stub, or the
- // interpreter as entrypoint, just return the current entrypoint, assuming
- // it's the most optimized.
- if (code != GetQuickInstrumentationEntryPoint() &&
- !class_linker->IsQuickResolutionStub(code) &&
+ // interpreter, just return the current entrypoint,
+ // assuming it's the most optimized.
+ if (!class_linker->IsQuickResolutionStub(code) &&
!class_linker->IsQuickToInterpreterBridge(code)) {
return code;
}
@@ -1295,8 +1294,8 @@
// This is called by resolution trampolines and that should never be getting proxy methods.
DCHECK(!method->IsProxyMethod()) << method->PrettyMethod();
const void* code = GetCodeForInvoke(method);
- if (EntryExitStubsInstalled() && CodeNeedsEntryExitStub(code, method)) {
- return GetQuickInstrumentationEntryPoint();
+ if (EntryExitStubsInstalled() && !CodeSupportsEntryExitHooks(code, method)) {
+ return method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge();
}
return code;
}
@@ -1349,16 +1348,12 @@
}
void Instrumentation::MethodUnwindEvent(Thread* thread,
- ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const {
if (HasMethodUnwindListeners()) {
- Thread* self = Thread::Current();
- StackHandleScope<1> hs(self);
- Handle<mirror::Object> thiz(hs.NewHandle(this_object));
for (InstrumentationListener* listener : method_unwind_listeners_) {
if (listener != nullptr) {
- listener->MethodUnwind(thread, thiz, method, dex_pc);
+ listener->MethodUnwind(thread, method, dex_pc);
}
}
}
@@ -1472,40 +1467,6 @@
}
}
-void Instrumentation::PushInstrumentationStackFrame(Thread* self,
- ObjPtr<mirror::Object> this_object,
- ArtMethod* method,
- uintptr_t stack_ptr,
- uintptr_t lr,
- bool interpreter_entry) {
- DCHECK(!self->IsExceptionPending());
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- self->GetInstrumentationStack();
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Entering " << ArtMethod::PrettyMethod(method) << " from PC "
- << reinterpret_cast<void*>(lr);
- }
-
- // We send the enter event before pushing the instrumentation frame to make cleanup easier. If the
- // event causes an exception we can simply send the unwind event and return.
- StackHandleScope<1> hs(self);
- Handle<mirror::Object> h_this(hs.NewHandle(this_object));
- if (!interpreter_entry) {
- MethodEnterEvent(self, method);
- if (self->IsExceptionPending()) {
- MethodUnwindEvent(self, h_this.Get(), method, 0);
- return;
- }
- }
-
- // We have a callee-save frame meaning this value is guaranteed to never be 0.
- DCHECK(!self->IsExceptionPending());
-
- instrumentation::InstrumentationStackFrame instrumentation_frame(
- h_this.Get(), method, lr, interpreter_entry, current_force_deopt_id_);
- stack->insert({stack_ptr, instrumentation_frame});
-}
-
DeoptimizationMethodType Instrumentation::GetDeoptimizationMethodType(ArtMethod* method) {
if (method->IsRuntimeMethod()) {
// Certain methods have strict requirement on whether the dex instruction
@@ -1522,83 +1483,18 @@
return DeoptimizationMethodType::kDefault;
}
-// Try to get the shorty of a runtime method if it's an invocation stub.
-static char GetRuntimeMethodShorty(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_) {
- char shorty = 'V';
- StackVisitor::WalkStack(
- [&shorty](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtMethod* m = stack_visitor->GetMethod();
- if (m == nullptr || m->IsRuntimeMethod()) {
- return true;
- }
- // The first Java method.
- if (m->IsNative()) {
- // Use JNI method's shorty for the jni stub.
- shorty = m->GetShorty()[0];
- } else if (m->IsProxyMethod()) {
- // Proxy method just invokes its proxied method via
- // art_quick_proxy_invoke_handler.
- shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty()[0];
- } else {
- const Instruction& instr = m->DexInstructions().InstructionAt(stack_visitor->GetDexPc());
- if (instr.IsInvoke()) {
- uint16_t method_index = static_cast<uint16_t>(instr.VRegB());
- const DexFile* dex_file = m->GetDexFile();
- if (interpreter::IsStringInit(dex_file, method_index)) {
- // Invoking string init constructor is turned into invoking
- // StringFactory.newStringFromChars() which returns a string.
- shorty = 'L';
- } else {
- shorty = dex_file->GetMethodShorty(method_index)[0];
- }
-
- } else {
- // It could be that a non-invoke opcode invokes a stub, which in turn
- // invokes Java code. In such cases, we should never expect a return
- // value from the stub.
- }
- }
- // Stop stack walking since we've seen a Java frame.
- return false;
- },
- thread,
- /* context= */ nullptr,
- art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
- return shorty;
-}
-
-JValue Instrumentation::GetReturnValue(
- Thread* self, ArtMethod* method, bool* is_ref, uint64_t* gpr_result, uint64_t* fpr_result) {
+JValue Instrumentation::GetReturnValue(ArtMethod* method,
+ bool* is_ref,
+ uint64_t* gpr_result,
+ uint64_t* fpr_result) {
uint32_t length;
const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- char return_shorty;
// Runtime method does not call into MethodExitEvent() so there should not be
// suspension point below.
ScopedAssertNoThreadSuspension ants(__FUNCTION__, method->IsRuntimeMethod());
- if (method->IsRuntimeMethod()) {
- Runtime* runtime = Runtime::Current();
- if (method != runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit) &&
- method != runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck)) {
- // If the caller is at an invocation point and the runtime method is not
- // for clinit, we need to pass return results to the caller.
- // We need the correct shorty to decide whether we need to pass the return
- // result for deoptimization below.
- return_shorty = GetRuntimeMethodShorty(self);
- } else {
- // Some runtime methods such as allocations, unresolved field getters, etc.
- // have return value. We don't need to set return_value since MethodExitEvent()
- // below isn't called for runtime methods. Deoptimization doesn't need the
- // value either since the dex instruction will be re-executed by the
- // interpreter, except these two cases:
- // (1) For an invoke, which is handled above to get the correct shorty.
- // (2) For MONITOR_ENTER/EXIT, which cannot be re-executed since it's not
- // idempotent. However there is no return value for it anyway.
- return_shorty = 'V';
- }
- } else {
- return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
- }
+ DCHECK(!method->IsRuntimeMethod());
+ char return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
*is_ref = return_shorty == '[' || return_shorty == 'L';
JValue return_value;
@@ -1612,136 +1508,121 @@
return return_value;
}
-bool Instrumentation::ShouldDeoptimizeMethod(Thread* self, const NthCallerVisitor& visitor) {
- bool should_deoptimize_frame = false;
- const OatQuickMethodHeader* header = visitor.GetCurrentOatQuickMethodHeader();
- if (header != nullptr && header->HasShouldDeoptimizeFlag()) {
- uint8_t should_deopt_flag = visitor.GetShouldDeoptimizeFlag();
- // DeoptimizeFlag could be set for debugging or for CHA invalidations.
- // Deoptimize here only if it was requested for debugging. CHA
- // invalidations are handled in the JITed code.
- if ((should_deopt_flag & static_cast<uint8_t>(DeoptimizeFlagValue::kDebug)) != 0) {
- should_deoptimize_frame = true;
- }
+bool Instrumentation::PushDeoptContextIfNeeded(Thread* self,
+ DeoptimizationMethodType deopt_type,
+ bool is_ref,
+ const JValue& return_value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (self->IsExceptionPending()) {
+ return false;
}
- return (visitor.caller != nullptr) &&
- (InterpreterStubsInstalled() || IsDeoptimized(visitor.caller) ||
+
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ DCHECK(sp != nullptr && (*sp)->IsRuntimeMethod());
+ if (!ShouldDeoptimizeCaller(self, sp)) {
+ return false;
+ }
+
+ // TODO(mythria): The current deopt behaviour is we just re-execute the
+ // alloc instruction so we don't need the return value. For instrumentation
+ // related deopts, we actually don't need to and can use the result we got
+ // here. Since this is a debug only feature it is not very important but
+ // consider reusing the result in future.
+ self->PushDeoptimizationContext(
+ return_value, is_ref, nullptr, /* from_code= */ false, deopt_type);
+ self->SetException(Thread::GetDeoptimizationException());
+ return true;
+}
+
+void Instrumentation::DeoptimizeIfNeeded(Thread* self,
+ ArtMethod** sp,
+ DeoptimizationMethodType type,
+ JValue return_value,
+ bool is_reference) {
+ if (self->IsAsyncExceptionPending() || ShouldDeoptimizeCaller(self, sp)) {
+ self->PushDeoptimizationContext(return_value,
+ is_reference,
+ nullptr,
+ /* from_code= */ false,
+ type);
+ // This is requested from suspend points or when returning from runtime methods so exit
+ // callbacks wouldn't be run yet. So don't skip method callbacks.
+ artDeoptimize(self, /* skip_method_exit_callbacks= */ false);
+ }
+}
+
+bool Instrumentation::NeedsSlowInterpreterForMethod(Thread* self, ArtMethod* method) {
+ return (method != nullptr) &&
+ (InterpreterStubsInstalled() ||
+ IsDeoptimized(method) ||
self->IsForceInterpreter() ||
// NB Since structurally obsolete compiled methods might have the offsets of
// methods/fields compiled in we need to go back to interpreter whenever we hit
// them.
- visitor.caller->GetDeclaringClass()->IsObsoleteObject() ||
- Dbg::IsForcedInterpreterNeededForUpcall(self, visitor.caller) ||
- should_deoptimize_frame);
+ method->GetDeclaringClass()->IsObsoleteObject() ||
+ Dbg::IsForcedInterpreterNeededForUpcall(self, method));
}
-TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self,
- uintptr_t* return_pc_addr,
- uint64_t* gpr_result,
- uint64_t* fpr_result) {
- DCHECK(gpr_result != nullptr);
- DCHECK(fpr_result != nullptr);
- // Do the pop.
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- self->GetInstrumentationStack();
- CHECK_GT(stack->size(), 0U);
- auto it = stack->find(reinterpret_cast<uintptr_t>(return_pc_addr));
- CHECK(it != stack->end());
- InstrumentationStackFrame instrumentation_frame = it->second;
- stack->erase(it);
-
- // Set return PC and check the consistency of the stack.
- // We don't cache the return pc value in a local as it may change after
- // sending a method exit event.
- *return_pc_addr = instrumentation_frame.return_pc_;
- self->VerifyStack();
-
- ArtMethod* method = instrumentation_frame.method_;
-
- bool is_ref;
- JValue return_value = GetReturnValue(self, method, &is_ref, gpr_result, fpr_result);
- StackHandleScope<1> hs(self);
- MutableHandle<mirror::Object> res(hs.NewHandle<mirror::Object>(nullptr));
- if (is_ref) {
- // Take a handle to the return value so we won't lose it if we suspend.
- // FIXME: The `is_ref` is often guessed wrong, so even object aligment
- // assertion would fail for some tests. See b/204766614 .
- // DCHECK_ALIGNED(return_value.GetL(), kObjectAlignment);
- res.Assign(return_value.GetL());
- }
- if (!method->IsRuntimeMethod() && !instrumentation_frame.interpreter_entry_) {
- // Note that sending the event may change the contents of *return_pc_addr.
- MethodExitEvent(self, instrumentation_frame.method_, OptionalFrame{}, return_value);
+bool Instrumentation::ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp) {
+ // When exit stubs aren't called we don't need to check for any instrumentation related
+ // deoptimizations.
+ if (!RunExitHooks()) {
+ return false;
}
- // Deoptimize if the caller needs to continue execution in the interpreter. Do nothing if we get
- // back to an upcall.
- NthCallerVisitor visitor(self, 1, true);
- visitor.WalkStack(true);
- // Check if we forced all threads to deoptimize in the time between this frame being created and
- // now.
- bool should_deoptimize_frame = instrumentation_frame.force_deopt_id_ != current_force_deopt_id_;
- bool deoptimize = ShouldDeoptimizeMethod(self, visitor) || should_deoptimize_frame;
-
- if (is_ref) {
- // Restore the return value if it's a reference since it might have moved.
- *reinterpret_cast<mirror::Object**>(gpr_result) = res.Get();
- }
- if (deoptimize && Runtime::Current()->IsAsyncDeoptimizeable(*return_pc_addr)) {
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Deoptimizing "
- << visitor.caller->PrettyMethod()
- << " by returning from "
- << method->PrettyMethod()
- << " with result "
- << std::hex << return_value.GetJ() << std::dec
- << " in "
- << *self;
- }
- DeoptimizationMethodType deopt_method_type = GetDeoptimizationMethodType(method);
- self->PushDeoptimizationContext(return_value,
- is_ref,
- /* exception= */ nullptr,
- /* from_code= */ false,
- deopt_method_type);
- return GetTwoWordSuccessValue(*return_pc_addr,
- reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
- } else {
- if (deoptimize && !Runtime::Current()->IsAsyncDeoptimizeable(*return_pc_addr)) {
- VLOG(deopt) << "Got a deoptimization request on un-deoptimizable " << method->PrettyMethod()
- << " at PC " << reinterpret_cast<void*>(*return_pc_addr);
- }
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Returning from " << method->PrettyMethod()
- << " to PC " << reinterpret_cast<void*>(*return_pc_addr);
- }
- return GetTwoWordSuccessValue(0, *return_pc_addr);
- }
+ ArtMethod* runtime_method = *sp;
+ DCHECK(runtime_method->IsRuntimeMethod());
+ QuickMethodFrameInfo frame_info = Runtime::Current()->GetRuntimeMethodFrameInfo(runtime_method);
+ return ShouldDeoptimizeCaller(self, sp, frame_info.FrameSizeInBytes());
}
-uintptr_t Instrumentation::PopFramesForDeoptimization(Thread* self, uintptr_t pop_until) const {
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- self->GetInstrumentationStack();
- // Pop all instrumentation frames below `pop_until`.
- uintptr_t return_pc = 0u;
- for (auto i = stack->begin(); i != stack->end() && i->first <= pop_until;) {
- auto e = i;
- ++i;
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Popping for deoptimization " << e->second.method_->PrettyMethod();
- }
- return_pc = e->second.return_pc_;
- stack->erase(e);
- }
- return return_pc;
-}
+bool Instrumentation::ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp, size_t frame_size) {
+ uintptr_t caller_sp = reinterpret_cast<uintptr_t>(sp) + frame_size;
+ ArtMethod* caller = *(reinterpret_cast<ArtMethod**>(caller_sp));
+ uintptr_t caller_pc_addr = reinterpret_cast<uintptr_t>(sp) + (frame_size - sizeof(void*));
+ uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(caller_pc_addr);
-std::string InstrumentationStackFrame::Dump() const {
- std::ostringstream os;
- os << ArtMethod::PrettyMethod(method_) << ":"
- << reinterpret_cast<void*>(return_pc_) << " this=" << reinterpret_cast<void*>(this_object_)
- << " force_deopt_id=" << force_deopt_id_;
- return os.str();
+ if (caller == nullptr ||
+ caller->IsNative() ||
+ caller->IsRuntimeMethod()) {
+ // We need to check for a deoptimization here because when a redefinition happens it is
+ // not safe to use any compiled code because the field offsets might change. For native
+ // methods, we don't embed any field offsets so no need to check for a deoptimization.
+ // If the caller is null we don't need to do anything. This can happen when the caller
+ // is being interpreted by the switch interpreter (when called from
+ // artQuickToInterpreterBridge) / during shutdown / early startup.
+ return false;
+ }
+
+ bool needs_deopt = NeedsSlowInterpreterForMethod(self, caller);
+
+ // Non java debuggable apps don't support redefinition and hence it isn't required to check if
+ // frame needs to be deoptimized. Even in debuggable apps, we only need this check when a
+ // redefinition has actually happened. This is indicated by IsDeoptCheckRequired flag. We also
+ // want to avoid getting method header when we need a deopt anyway.
+ if (Runtime::Current()->IsJavaDebuggable() && !needs_deopt && self->IsDeoptCheckRequired()) {
+ const OatQuickMethodHeader* header = caller->GetOatQuickMethodHeader(caller_pc);
+ if (header != nullptr && header->HasShouldDeoptimizeFlag()) {
+ DCHECK(header->IsOptimized());
+ uint8_t* should_deopt_flag_addr =
+ reinterpret_cast<uint8_t*>(caller_sp) + header->GetShouldDeoptimizeFlagOffset();
+ if ((*should_deopt_flag_addr &
+ static_cast<uint8_t>(DeoptimizeFlagValue::kForceDeoptForRedefinition)) != 0) {
+ needs_deopt = true;
+ }
+ }
+ }
+
+ if (needs_deopt) {
+ if (!Runtime::Current()->IsAsyncDeoptimizeable(caller, caller_pc)) {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << caller->PrettyMethod();
+ return false;
+ }
+ return true;
+ }
+
+ return false;
}
} // namespace instrumentation
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index c811935..364acaf 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -23,6 +23,7 @@
#include <list>
#include <memory>
#include <optional>
+#include <queue>
#include <unordered_set>
#include "arch/instruction_set.h"
@@ -31,6 +32,7 @@
#include "base/macros.h"
#include "base/safe_map.h"
#include "gc_root.h"
+#include "jvalue.h"
#include "offsets.h"
namespace art {
@@ -45,6 +47,7 @@
template <typename T> class MutableHandle;
struct NthCallerVisitor;
union JValue;
+class OatQuickMethodHeader;
class SHARED_LOCKABLE ReaderWriterMutex;
class ShadowFrame;
class Thread;
@@ -92,7 +95,6 @@
// Call-back for when a method is popped due to an exception throw. A method will either cause a
// MethodExited call-back or a MethodUnwind call-back when its activation is removed.
virtual void MethodUnwind(Thread* thread,
- Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
@@ -192,20 +194,37 @@
};
enum class InstrumentationLevel {
- kInstrumentNothing, // execute without instrumentation
- kInstrumentWithInstrumentationStubs, // execute with instrumentation entry/exit stubs
- kInstrumentWithInterpreter // execute with interpreter
+ kInstrumentNothing, // execute without instrumentation
+ kInstrumentWithEntryExitHooks, // execute with entry/exit hooks
+ kInstrumentWithInterpreter // execute with interpreter
};
Instrumentation();
- static constexpr MemberOffset NeedsEntryExitHooksOffset() {
- // Assert that instrumentation_stubs_installed_ is 8bits wide. If the size changes
+ static constexpr MemberOffset RunExitHooksOffset() {
+ // Assert that run_entry_exit_hooks_ is 8bits wide. If the size changes
// update the compare instructions in the code generator when generating checks for
// MethodEntryExitHooks.
- static_assert(sizeof(instrumentation_stubs_installed_) == 1,
- "instrumentation_stubs_installed_ isn't expected size");
- return MemberOffset(OFFSETOF_MEMBER(Instrumentation, instrumentation_stubs_installed_));
+ static_assert(sizeof(run_exit_hooks_) == 1, "run_exit_hooks_ isn't expected size");
+ return MemberOffset(OFFSETOF_MEMBER(Instrumentation, run_exit_hooks_));
+ }
+
+ static constexpr MemberOffset HaveMethodEntryListenersOffset() {
+ // Assert that have_method_entry_listeners_ is 8bits wide. If the size changes
+ // update the compare instructions in the code generator when generating checks for
+ // MethodEntryExitHooks.
+ static_assert(sizeof(have_method_entry_listeners_) == 1,
+ "have_method_entry_listeners_ isn't expected size");
+ return MemberOffset(OFFSETOF_MEMBER(Instrumentation, have_method_entry_listeners_));
+ }
+
+ static constexpr MemberOffset HaveMethodExitListenersOffset() {
+ // Assert that have_method_exit_listeners_ is 8bits wide. If the size changes
+ // update the compare instructions in the code generator when generating checks for
+ // MethodEntryExitHooks.
+ static_assert(sizeof(have_method_exit_listeners_) == 1,
+ "have_method_exit_listeners_ isn't expected size");
+ return MemberOffset(OFFSETOF_MEMBER(Instrumentation, have_method_exit_listeners_));
}
// Add a listener to be notified of the masked together sent of instrumentation events. This
@@ -215,14 +234,23 @@
void AddListener(InstrumentationListener* listener, uint32_t events)
REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);
- // Removes a listener possibly removing instrumentation stubs.
+ // Removes listeners for the specified events.
void RemoveListener(InstrumentationListener* listener, uint32_t events)
REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);
// Calls UndeoptimizeEverything which may visit class linker classes through ConfigureStubs.
void DisableDeoptimization(const char* key)
- REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);
+
+ // Enables entry exit hooks support. This is called in preparation for debug requests that require
+ // calling method entry / exit hooks.
+ void EnableEntryExitHooks(const char* key)
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);
+
+ // Switches the runtime state to non-java debuggable if entry / exit hooks are no longer required
+ // and the runtime did not start off as java debuggable.
+ void MaybeSwitchRuntimeDebugState(Thread* self)
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);
bool AreAllMethodsDeoptimized() const {
return InterpreterStubsInstalled();
@@ -233,52 +261,44 @@
void DeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
// Executes everything with compiled code (or interpreter if there is no code). May visit class
// linker classes through ConfigureStubs.
void UndeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
// Deoptimize a method by forcing its execution with the interpreter. Nevertheless, a static
// method (except a class initializer) set to the resolution trampoline will be deoptimized only
// once its declaring class is initialized.
- void Deoptimize(ArtMethod* method)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
+ void Deoptimize(ArtMethod* method) REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_);
// Undeoptimze the method by restoring its entrypoints. Nevertheless, a static method
// (except a class initializer) set to the resolution trampoline will be updated only once its
// declaring class is initialized.
- void Undeoptimize(ArtMethod* method)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
+ void Undeoptimize(ArtMethod* method) REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_);
// Indicates whether the method has been deoptimized so it is executed with the interpreter.
- bool IsDeoptimized(ArtMethod* method)
- REQUIRES(!GetDeoptimizedMethodsLock()) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsDeoptimized(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
// Indicates if any method needs to be deoptimized. This is used to avoid walking the stack to
// determine if a deoptimization is required.
- bool IsDeoptimizedMethodsEmpty() const
- REQUIRES(!GetDeoptimizedMethodsLock()) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsDeoptimizedMethodsEmpty() const REQUIRES_SHARED(Locks::mutator_lock_);
// Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
void EnableMethodTracing(const char* key,
bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
// Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
void DisableMethodTracing(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
void InstrumentQuickAllocEntryPoints() REQUIRES(!Locks::instrument_entrypoints_lock_);
@@ -300,11 +320,11 @@
// Update the code of a method respecting any installed stubs.
void UpdateMethodsCode(ArtMethod* method, const void* new_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Update the code of a native method to a JITed stub.
void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* new_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Return the code that we can execute for an invoke including from the JIT.
const void* GetCodeForInvoke(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -321,7 +341,7 @@
}
bool EntryExitStubsInstalled() const {
- return instrumentation_level_ == InstrumentationLevel::kInstrumentWithInstrumentationStubs ||
+ return instrumentation_level_ == InstrumentationLevel::kInstrumentWithEntryExitHooks ||
instrumentation_level_ == InstrumentationLevel::kInstrumentWithInterpreter;
}
@@ -339,8 +359,8 @@
return forced_interpret_only_;
}
- bool AreExitStubsInstalled() const {
- return instrumentation_stubs_installed_;
+ bool RunExitHooks() const {
+ return run_exit_hooks_;
}
bool HasMethodEntryListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -383,6 +403,20 @@
return have_exception_handled_listeners_;
}
+ // Returns if dex pc events need to be reported for the specified method.
+ // These events are reported when DexPCListeners are installed and at least one of the
+ // following conditions hold:
+ // 1. The method is deoptimized. This is done when there is a breakpoint on method.
+ // 2. When the thread is deoptimized. This is used when single stepping a single thread.
+ // 3. When interpreter stubs are installed. In this case no additional information is maintained
+ // about which methods need dex pc move events. This is usually used for features which need
+ // them for several methods across threads or need expensive processing. So it is OK to not
+ // further optimize this case.
+ // DexPCListeners are installed when there is a breakpoint on any method / single stepping
+ // on any of thread. These are removed when the last breakpoint was removed. See AddListener and
+ // RemoveListener for more details.
+ bool NeedsDexPcEvents(ArtMethod* method, Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_);
+
bool NeedsSlowInterpreterForListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
return have_field_read_listeners_ ||
have_field_write_listeners_ ||
@@ -413,7 +447,6 @@
// Inform listeners that a method has been exited due to an exception.
void MethodUnwindEvent(Thread* thread,
- ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -479,50 +512,37 @@
void ExceptionHandledEvent(Thread* thread, ObjPtr<mirror::Throwable> exception_object) const
REQUIRES_SHARED(Locks::mutator_lock_);
- JValue GetReturnValue(Thread* self,
- ArtMethod* method,
- bool* is_ref,
- uint64_t* gpr_result,
- uint64_t* fpr_result) REQUIRES_SHARED(Locks::mutator_lock_);
- bool ShouldDeoptimizeMethod(Thread* self, const NthCallerVisitor& visitor)
+ JValue GetReturnValue(ArtMethod* method, bool* is_ref, uint64_t* gpr_result, uint64_t* fpr_result)
REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Called when an instrumented method is entered. The intended link register (lr) is saved so
- // that returning causes a branch to the method exit stub. Generates method enter events.
- void PushInstrumentationStackFrame(Thread* self,
- ObjPtr<mirror::Object> this_object,
- ArtMethod* method,
- uintptr_t stack_pointer,
- uintptr_t lr,
- bool interpreter_entry)
+ bool PushDeoptContextIfNeeded(Thread* self,
+ DeoptimizationMethodType deopt_type,
+ bool is_ref,
+ const JValue& result) REQUIRES_SHARED(Locks::mutator_lock_);
+ void DeoptimizeIfNeeded(Thread* self,
+ ArtMethod** sp,
+ DeoptimizationMethodType type,
+ JValue result,
+ bool is_ref) REQUIRES_SHARED(Locks::mutator_lock_);
+ // This returns if the caller of runtime method requires a deoptimization. This checks both if the
+ // method requires a deopt or if this particular frame needs a deopt because of a class
+ // redefinition.
+ bool ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp, size_t frame_size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // This returns if the specified method requires a deoptimization. This doesn't account if a stack
+ // frame involving this method requires a deoptimization.
+ bool NeedsSlowInterpreterForMethod(Thread* self, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
DeoptimizationMethodType GetDeoptimizationMethodType(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Called when an instrumented method is exited. Removes the pushed instrumentation frame
- // returning the intended link register. Generates method exit events. The gpr_result and
- // fpr_result pointers are pointers to the locations where the integer/pointer and floating point
- // result values of the function are stored. Both pointers must always be valid but the values
- // held there will only be meaningful if interpreted as the appropriate type given the function
- // being returned from.
- TwoWordReturn PopInstrumentationStackFrame(Thread* self,
- uintptr_t* return_pc_addr,
- uint64_t* gpr_result,
- uint64_t* fpr_result)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
-
- // Pops nframes instrumentation frames from the current thread. Returns the return pc for the last
- // instrumentation frame that's popped.
- uintptr_t PopFramesForDeoptimization(Thread* self, uintptr_t stack_pointer) const
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Call back for configure stubs.
- void InstallStubsForClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!GetDeoptimizedMethodsLock());
+ void InstallStubsForClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
- void InstallStubsForMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ void InstallStubsForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void UpdateEntrypointsForDebuggable() REQUIRES(art::Locks::mutator_lock_);
// Install instrumentation exit stub on every method of the stack of the given thread.
// This is used by:
@@ -532,6 +552,8 @@
// the stack frame to run entry / exit hooks but we don't need to deoptimize.
// deopt_all_frames indicates whether the frames need to deoptimize or not.
void InstrumentThreadStack(Thread* thread, bool deopt_all_frames) REQUIRES(Locks::mutator_lock_);
+ void InstrumentAllThreadStacks(bool deopt_all_frames) REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Locks::thread_list_lock_);
// Force all currently running frames to be deoptimized back to interpreter. This should only be
// used in cases where basically all compiled code has been invalidated.
@@ -548,18 +570,21 @@
return alloc_entrypoints_instrumented_;
}
+ bool ProcessMethodUnwindCallbacks(Thread* self,
+ std::queue<ArtMethod*>& methods,
+ MutableHandle<mirror::Throwable>& exception)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
InstrumentationLevel GetCurrentInstrumentationLevel() const;
+ bool MethodSupportsExitEvents(ArtMethod* method, const OatQuickMethodHeader* header)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
// Returns true if moving to the given instrumentation level requires the installation of stubs.
// False otherwise.
bool RequiresInstrumentationInstallation(InstrumentationLevel new_level) const;
- // Returns true if we need entry exit stub to call entry hooks. JITed code
- // directly call entry / exit hooks and don't need the stub.
- static bool CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Update the current instrumentation_level_.
void UpdateInstrumentationLevel(InstrumentationLevel level);
@@ -570,12 +595,10 @@
// becomes the highest instrumentation level required by a client.
void ConfigureStubs(const char* key, InstrumentationLevel desired_instrumentation_level)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!GetDeoptimizedMethodsLock(),
- !Locks::thread_list_lock_,
+ REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
void UpdateStubs() REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!GetDeoptimizedMethodsLock(),
- !Locks::thread_list_lock_,
+ REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
// If there are no pending deoptimizations restores the stack to the normal state by updating the
@@ -619,34 +642,36 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Read barrier-aware utility functions for accessing deoptimized_methods_
- bool AddDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
- bool IsDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
- bool RemoveDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
- ArtMethod* BeginDeoptimizedMethod()
- REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
- bool IsDeoptimizedMethodsEmptyLocked() const
- REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
+ bool AddDeoptimizedMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
+ bool IsDeoptimizedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool RemoveDeoptimizedMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
void UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES_SHARED(Locks::mutator_lock_);
- ReaderWriterMutex* GetDeoptimizedMethodsLock() const {
- return deoptimized_methods_lock_.get();
- }
-
- // A counter that's incremented every time a DeoptimizeAllFrames. We check each
- // InstrumentationStackFrames creation id against this number and if they differ we deopt even if
- // we could otherwise continue running.
- uint64_t current_force_deopt_id_ GUARDED_BY(Locks::mutator_lock_);
-
- // Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code?
- bool instrumentation_stubs_installed_;
+ // We need to run method exit hooks for two reasons:
+ // 1. When method exit listeners are installed
+ // 2. When we need to check if the caller of this method needs a deoptimization. This is needed
+ // only for deoptimizing the currently active invocations on stack when we deoptimize a method or
+ // invalidate the JITed code when redefining the classes. So future invocations don't need to do
+ // this check.
+ //
+ // For JITed code of non-native methods we already have a stack slot reserved for deoptimizing
+ // on demand and we use that stack slot to check if the caller needs a deoptimization. JITed code
+ // checks if there are any method exit listeners or if the stack slot is set to determine if
+ // method exit hooks need to be executed.
+ //
+ // For JITed JNI stubs there is no reserved stack slot for this and we just use this variable to
+ // check if we need to run method entry / exit hooks. This variable would be set when either of
+ // the above conditions are true. If we need method exit hooks only for case 2, we would call exit
+ // hooks for any future invocations which aren't necessary.
+ // QuickToInterpreterBridge and GenericJniStub also use this for same reasons.
+ // If calling entry / exit hooks becomes expensive we could do the same optimization we did for
+ // JITed code by having a reserved stack slot.
+ bool run_exit_hooks_;
// The required level of instrumentation. This could be one of the following values:
// kInstrumentNothing: no instrumentation support is needed
- // kInstrumentWithInstrumentationStubs: needs support to call method entry/exit stubs.
+ // kInstrumentWithEntryExitHooks: needs support to call method entry/exit stubs.
// kInstrumentWithInterpreter: only execute with interpreter
Instrumentation::InstrumentationLevel instrumentation_level_;
@@ -718,8 +743,7 @@
// The set of methods being deoptimized (by the debugger) which must be executed with interpreter
// only.
- mutable std::unique_ptr<ReaderWriterMutex> deoptimized_methods_lock_ BOTTOM_MUTEX_ACQUIRED_AFTER;
- std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(GetDeoptimizedMethodsLock());
+ std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(Locks::mutator_lock_);
// Current interpreter handler table. This is updated each time the thread state flags are
// modified.
@@ -741,29 +765,6 @@
std::ostream& operator<<(std::ostream& os, Instrumentation::InstrumentationEvent rhs);
std::ostream& operator<<(std::ostream& os, Instrumentation::InstrumentationLevel rhs);
-// An element in the instrumentation side stack maintained in art::Thread.
-struct InstrumentationStackFrame {
- InstrumentationStackFrame(mirror::Object* this_object,
- ArtMethod* method,
- uintptr_t return_pc,
- bool interpreter_entry,
- uint64_t force_deopt_id)
- : this_object_(this_object),
- method_(method),
- return_pc_(return_pc),
- interpreter_entry_(interpreter_entry),
- force_deopt_id_(force_deopt_id) {
- }
-
- std::string Dump() const REQUIRES_SHARED(Locks::mutator_lock_);
-
- mirror::Object* this_object_;
- ArtMethod* method_;
- uintptr_t return_pc_;
- bool interpreter_entry_;
- uint64_t force_deopt_id_;
-};
-
} // namespace instrumentation
} // namespace art
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index b0a81b6..85073ae 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -77,7 +77,6 @@
}
void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
- Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED)
override REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -184,6 +183,10 @@
static constexpr const char* kClientOneKey = "TestClient1";
static constexpr const char* kClientTwoKey = "TestClient2";
+ InstrumentationTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) {
ScopedObjectAccess soa(Thread::Current());
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
@@ -223,7 +226,7 @@
mirror::Object* const event_obj = nullptr;
const uint32_t event_dex_pc = 0;
- ShadowFrameAllocaUniquePtr test_frame = CREATE_SHADOW_FRAME(0, nullptr, event_method, 0);
+ ShadowFrameAllocaUniquePtr test_frame = CREATE_SHADOW_FRAME(0, event_method, 0);
// Check the listener is registered and is notified of the event.
EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
@@ -387,7 +390,7 @@
break;
}
case instrumentation::Instrumentation::kMethodUnwind:
- instr->MethodUnwindEvent(self, obj, method, dex_pc);
+ instr->MethodUnwindEvent(self, method, dex_pc);
break;
case instrumentation::Instrumentation::kDexPcMoved:
instr->DexPcMovedEvent(self, obj, method, dex_pc);
@@ -464,7 +467,8 @@
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
ASSERT_NE(instr, nullptr);
- EXPECT_FALSE(instr->AreExitStubsInstalled());
+ EXPECT_FALSE(instr->RunExitHooks());
+ EXPECT_FALSE(instr->EntryExitStubsInstalled());
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
EXPECT_FALSE(instr->NeedsSlowInterpreterForListeners());
EXPECT_FALSE(instr->ShouldNotifyMethodEnterExitEvents());
@@ -627,7 +631,7 @@
DeoptimizeMethod(soa.Self(), method_to_deoptimize);
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
- EXPECT_TRUE(instr->AreExitStubsInstalled());
+ EXPECT_TRUE(instr->RunExitHooks());
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
constexpr const char* instrumentation_key = "DeoptimizeDirectMethod";
@@ -647,7 +651,8 @@
DeoptimizeEverything(soa.Self(), instrumentation_key);
EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
- EXPECT_TRUE(instr->AreExitStubsInstalled());
+ EXPECT_TRUE(instr->RunExitHooks());
+ EXPECT_TRUE(instr->InterpreterStubsInstalled());
UndeoptimizeEverything(soa.Self(), instrumentation_key, true);
@@ -678,7 +683,7 @@
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
GetCurrentInstrumentationLevel());
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
- EXPECT_TRUE(instr->AreExitStubsInstalled());
+ EXPECT_TRUE(instr->RunExitHooks());
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
constexpr const char* instrumentation_key = "MixedDeoptimization";
@@ -686,14 +691,14 @@
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
GetCurrentInstrumentationLevel());
EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
- EXPECT_TRUE(instr->AreExitStubsInstalled());
+ EXPECT_TRUE(instr->RunExitHooks());
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
UndeoptimizeEverything(soa.Self(), instrumentation_key, false);
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
GetCurrentInstrumentationLevel());
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
- EXPECT_TRUE(instr->AreExitStubsInstalled());
+ EXPECT_TRUE(instr->RunExitHooks());
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
UndeoptimizeMethod(soa.Self(), method_to_deoptimize, instrumentation_key, true);
@@ -714,7 +719,7 @@
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
GetCurrentInstrumentationLevel());
EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
- EXPECT_TRUE(instr->AreExitStubsInstalled());
+ EXPECT_TRUE(instr->RunExitHooks());
DisableMethodTracing(soa.Self(), instrumentation_key);
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
@@ -730,10 +735,10 @@
constexpr const char* instrumentation_key = "MethodTracing";
EnableMethodTracing(soa.Self(), instrumentation_key, false);
- EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
+ EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks,
GetCurrentInstrumentationLevel());
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
- EXPECT_TRUE(instr->AreExitStubsInstalled());
+ EXPECT_TRUE(instr->RunExitHooks());
DisableMethodTracing(soa.Self(), instrumentation_key);
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
@@ -776,9 +781,8 @@
// Check we can switch to instrumentation stubs
CheckConfigureStubs(kClientOneKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// Check we can disable instrumentation.
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
@@ -803,9 +807,8 @@
// Configure stubs with instrumentation stubs.
CheckConfigureStubs(kClientOneKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// Configure stubs with interpreter.
CheckConfigureStubs(kClientOneKey,
@@ -827,9 +830,8 @@
// Configure stubs with instrumentation stubs.
CheckConfigureStubs(kClientOneKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// Check we can disable instrumentation.
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
@@ -842,9 +844,8 @@
// Configure stubs with instrumentation stubs.
CheckConfigureStubs(kClientOneKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// Configure stubs with interpreter.
CheckConfigureStubs(kClientOneKey,
@@ -853,9 +854,8 @@
// Configure stubs with instrumentation stubs again.
CheckConfigureStubs(kClientOneKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// Check we can disable instrumentation.
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
@@ -878,21 +878,18 @@
// Configure stubs with instrumentation stubs for 1st client.
CheckConfigureStubs(kClientOneKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// Configure stubs with instrumentation stubs for 2nd client.
CheckConfigureStubs(kClientTwoKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 2U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 2U);
// 1st client requests instrumentation deactivation but 2nd client still needs
// instrumentation stubs.
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// 2nd client requests instrumentation deactivation
CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
@@ -926,9 +923,8 @@
// Configure stubs with instrumentation stubs for 1st client.
CheckConfigureStubs(kClientOneKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// Configure stubs with interpreter for 2nd client.
CheckConfigureStubs(kClientTwoKey,
@@ -954,14 +950,13 @@
// Configure stubs with instrumentation stubs for 2nd client.
CheckConfigureStubs(kClientTwoKey,
- Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
+ Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks);
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U);
// 1st client requests instrumentation deactivation but 2nd client still needs
// instrumentation stubs.
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
- CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
- 1U);
+ CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithEntryExitHooks, 1U);
// 2nd client requests instrumentation deactivation
CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 9900c2a..d77ffcb 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -27,7 +27,12 @@
namespace art {
-class InternTableTest : public CommonRuntimeTest {};
+class InternTableTest : public CommonRuntimeTest {
+ protected:
+ InternTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
TEST_F(InternTableTest, Intern) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 38c94ab..0dc8d8b 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -237,24 +237,15 @@
JValue result_register,
bool interpret_one_instruction) REQUIRES_SHARED(Locks::mutator_lock_) {
if (Runtime::Current()->IsActiveTransaction()) {
- if (shadow_frame.GetMethod()->SkipAccessChecks()) {
- return ExecuteSwitchImpl<false, true>(
- self, accessor, shadow_frame, result_register, interpret_one_instruction);
- } else {
- return ExecuteSwitchImpl<true, true>(
- self, accessor, shadow_frame, result_register, interpret_one_instruction);
- }
+ return ExecuteSwitchImpl<true>(
+ self, accessor, shadow_frame, result_register, interpret_one_instruction);
} else {
- if (shadow_frame.GetMethod()->SkipAccessChecks()) {
- return ExecuteSwitchImpl<false, false>(
- self, accessor, shadow_frame, result_register, interpret_one_instruction);
- } else {
- return ExecuteSwitchImpl<true, false>(
- self, accessor, shadow_frame, result_register, interpret_one_instruction);
- }
+ return ExecuteSwitchImpl<false>(
+ self, accessor, shadow_frame, result_register, interpret_one_instruction);
}
}
+NO_STACK_PROTECTOR
static inline JValue Execute(
Thread* self,
const CodeItemDataAccessor& accessor,
@@ -265,41 +256,22 @@
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
+ // We cache the result of NeedsDexPcEvents in the shadow frame so we don't need to call
+ // NeedsDexPcEvents on every instruction for better performance. NeedsDexPcEvents only gets
+ // updated asynchronoulsy in a SuspendAll scope and any existing shadow frames are updated with
+ // new value. So it is safe to cache it here.
+ shadow_frame.SetNotifyDexPcMoveEvents(
+ Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(shadow_frame.GetMethod(), self));
+
if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization.
if (kIsDebugBuild) {
CHECK_EQ(shadow_frame.GetDexPC(), 0u);
self->AssertNoPendingException();
}
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
ArtMethod *method = shadow_frame.GetMethod();
- if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
- instrumentation->MethodEnterEvent(self, method);
- if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
- // The caller will retry this invoke or ignore the result. Just return immediately without
- // any value.
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- JValue ret = JValue();
- PerformNonStandardReturn<MonitorState::kNoMonitorsLocked>(
- self, shadow_frame, ret, instrumentation, accessor.InsSize());
- return ret;
- }
- if (UNLIKELY(self->IsExceptionPending())) {
- instrumentation->MethodUnwindEvent(self,
- shadow_frame.GetThisObject(accessor.InsSize()),
- method,
- 0);
- JValue ret = JValue();
- if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- PerformNonStandardReturn<MonitorState::kNoMonitorsLocked>(
- self, shadow_frame, ret, instrumentation, accessor.InsSize());
- }
- return ret;
- }
- }
-
- if (!stay_in_interpreter && !self->IsForceInterpreter()) {
+ // If we can continue in JIT and have JITed code available execute JITed code.
+ if (!stay_in_interpreter && !self->IsForceInterpreter() && !shadow_frame.GetForcePopFrame()) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
jit->MethodEntered(self, shadow_frame.GetMethod());
@@ -320,6 +292,40 @@
}
}
}
+
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodEntryListeners() || shadow_frame.GetForcePopFrame())) {
+ instrumentation->MethodEnterEvent(self, method);
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+ // The caller will retry this invoke or ignore the result. Just return immediately without
+ // any value.
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ JValue ret = JValue();
+ PerformNonStandardReturn(self,
+ shadow_frame,
+ ret,
+ instrumentation,
+ accessor.InsSize(),
+ /* unlock_monitors= */ false);
+ return ret;
+ }
+ if (UNLIKELY(self->IsExceptionPending())) {
+ instrumentation->MethodUnwindEvent(self,
+ method,
+ 0);
+ JValue ret = JValue();
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ PerformNonStandardReturn(self,
+ shadow_frame,
+ ret,
+ instrumentation,
+ accessor.InsSize(),
+ /* unlock_monitors= */ false);
+ }
+ return ret;
+ }
+ }
}
ArtMethod* method = shadow_frame.GetMethod();
@@ -366,7 +372,7 @@
num_ins = accessor.InsSize();
} else if (!method->IsInvokable()) {
self->EndAssertNoThreadSuspension(old_cause);
- method->ThrowInvocationTimeError();
+ method->ThrowInvocationTimeError(receiver);
return;
} else {
DCHECK(method->IsNative()) << method->PrettyMethod();
@@ -377,11 +383,9 @@
}
}
// Set up shadow frame with matching number of reference slots to vregs.
- ShadowFrame* last_shadow_frame = self->GetManagedStack()->GetTopShadowFrame();
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, last_shadow_frame, method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(num_regs, method, /* dex pc */ 0);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
- self->PushShadowFrame(shadow_frame);
size_t cur_reg = num_regs - num_ins;
if (!method->IsStatic()) {
@@ -413,21 +417,10 @@
}
}
self->EndAssertNoThreadSuspension(old_cause);
- // Do this after populating the shadow frame in case EnsureInitialized causes a GC.
- if (method->IsStatic()) {
- ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
- self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
- CHECK(self->IsExceptionPending());
- self->PopShadowFrame();
- return;
- }
- DCHECK(h_class->IsInitializing());
- }
+ if (!EnsureInitialized(self, shadow_frame)) {
+ return;
}
+ self->PushShadowFrame(shadow_frame);
if (LIKELY(!method->IsNative())) {
JValue r = Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter);
if (result != nullptr) {
@@ -476,6 +469,7 @@
const uint32_t dex_pc = shadow_frame->GetDexPC();
uint32_t new_dex_pc = dex_pc;
if (UNLIKELY(self->IsExceptionPending())) {
+ DCHECK(self->GetException() != Thread::GetDeoptimizationException());
// If we deoptimize from the QuickExceptionHandler, we already reported the exception throw
// event to the instrumentation. Skip throw listeners for the first frame. The deopt check
// should happen after the throw listener is called as throw listener can trigger a
@@ -514,7 +508,7 @@
new_dex_pc = dex_pc + instr->SizeInCodeUnits();
} else if (instr->IsInvoke()) {
DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
- if (IsStringInit(instr, shadow_frame->GetMethod())) {
+ if (IsStringInit(*instr, shadow_frame->GetMethod())) {
uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr);
// Move the StringFactory.newStringFromChars() result into the register representing
// "this object" when invoking the string constructor in the original dex instruction.
@@ -569,6 +563,7 @@
ret_val->SetJ(value.GetJ());
}
+NO_STACK_PROTECTOR
JValue EnterInterpreterFromEntryPoint(Thread* self, const CodeItemDataAccessor& accessor,
ShadowFrame* shadow_frame) {
DCHECK_EQ(self, Thread::Current());
@@ -585,6 +580,7 @@
return Execute(self, accessor, *shadow_frame, JValue());
}
+NO_STACK_PROTECTOR
void ArtInterpreterToInterpreterBridge(Thread* self,
const CodeItemDataAccessor& accessor,
ShadowFrame* shadow_frame,
@@ -596,23 +592,6 @@
}
self->PushShadowFrame(shadow_frame);
- ArtMethod* method = shadow_frame->GetMethod();
- // Ensure static methods are initialized.
- const bool is_static = method->IsStatic();
- if (is_static) {
- ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
- self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
- DCHECK(self->IsExceptionPending());
- self->PopShadowFrame();
- return;
- }
- DCHECK(h_class->IsInitializing());
- }
- }
if (LIKELY(!shadow_frame->GetMethod()->IsNative())) {
result->SetJ(Execute(self, accessor, *shadow_frame, JValue()).GetJ());
@@ -620,6 +599,7 @@
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub) except during testing and image writing.
CHECK(!Runtime::Current()->IsStarted());
+ bool is_static = shadow_frame->GetMethod()->IsStatic();
ObjPtr<mirror::Object> receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);
uint32_t* args = shadow_frame->GetVRegArgs(is_static ? 0 : 1);
UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver.Ptr(), args, result);
diff --git a/runtime/interpreter/interpreter_cache.h b/runtime/interpreter/interpreter_cache.h
index c57d023..8714bc6 100644
--- a/runtime/interpreter/interpreter_cache.h
+++ b/runtime/interpreter/interpreter_cache.h
@@ -47,7 +47,7 @@
class ALIGNED(16) InterpreterCache {
public:
// Aligned since we load the whole entry in single assembly instruction.
- typedef std::pair<const void*, size_t> Entry ALIGNED(2 * sizeof(size_t));
+ using Entry ALIGNED(2 * sizeof(size_t)) = std::pair<const void*, size_t>;
// 2x size increase/decrease corresponds to ~0.5% interpreter performance change.
// Value of 256 has around 75% cache hit rate.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index c8a87c1..ac9980f 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -92,10 +92,6 @@
}
const void* code = method->GetEntryPointFromQuickCompiledCode();
- if (code == GetQuickInstrumentationEntryPoint()) {
- code = Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method);
- }
-
return Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code);
}
@@ -185,7 +181,6 @@
// Exception is not caught by the current method. We will unwind to the
// caller. Notify any instrumentation listener.
instrumentation->MethodUnwindEvent(self,
- shadow_frame.GetThisObject(),
shadow_frame.GetMethod(),
shadow_frame.GetDexPC());
}
@@ -236,14 +231,16 @@
// about ALWAYS_INLINE (-Werror, -Wgcc-compat) in definitions.
//
-template <bool is_range, bool do_assignability_check>
+template <bool is_range>
+NO_STACK_PROTECTOR
static ALWAYS_INLINE bool DoCallCommon(ArtMethod* called_method,
Thread* self,
ShadowFrame& shadow_frame,
JValue* result,
uint16_t number_of_inputs,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t vregC,
+ bool string_init) REQUIRES_SHARED(Locks::mutator_lock_);
template <bool is_range>
ALWAYS_INLINE void CopyRegisters(ShadowFrame& caller_frame,
@@ -255,6 +252,7 @@
// END DECLARATIONS.
+NO_STACK_PROTECTOR
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
ShadowFrame* shadow_frame,
@@ -262,25 +260,6 @@
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = shadow_frame->GetMethod();
- // Ensure static methods are initialized.
- if (method->IsStatic()) {
- ObjPtr<mirror::Class> declaringClass = method->GetDeclaringClass();
- if (UNLIKELY(!declaringClass->IsVisiblyInitialized())) {
- self->PushShadowFrame(shadow_frame);
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaringClass));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
- self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
- self->PopShadowFrame();
- DCHECK(self->IsExceptionPending());
- return;
- }
- self->PopShadowFrame();
- DCHECK(h_class->IsInitializing());
- // Reload from shadow frame in case the method moved, this is faster than adding a handle.
- method = shadow_frame->GetMethod();
- }
- }
// Basic checks for the arg_offset. If there's no code item, the arg_offset must be 0. Otherwise,
// check that the arg_offset isn't greater than the number of registers. A stronger check is
// difficult since the frame may contain space for all the registers in the method, or only enough
@@ -1018,11 +997,9 @@
// Set-up a shadow frame for invoking the bootstrap method handle.
ShadowFrameAllocaUniquePtr bootstrap_frame =
CREATE_SHADOW_FRAME(call_site_type->NumberOfVRegs(),
- nullptr,
referrer,
shadow_frame.GetDexPC());
- ScopedStackedShadowFramePusher pusher(
- self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher pusher(self, bootstrap_frame.get());
ShadowFrameSetter setter(bootstrap_frame.get(), 0u);
// The first parameter is a MethodHandles lookup instance.
@@ -1205,23 +1182,15 @@
}
}
-template <bool is_range,
- bool do_assignability_check>
+template <bool is_range>
static inline bool DoCallCommon(ArtMethod* called_method,
Thread* self,
ShadowFrame& shadow_frame,
JValue* result,
uint16_t number_of_inputs,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) {
- bool string_init = false;
- // Replace calls to String.<init> with equivalent StringFactory call.
- if (UNLIKELY(called_method->GetDeclaringClass()->IsStringClass()
- && called_method->IsConstructor())) {
- called_method = WellKnownClasses::StringInitToStringFactory(called_method);
- string_init = true;
- }
-
+ uint32_t vregC,
+ bool string_init) {
// Compute method information.
CodeItemDataAccessor accessor(called_method->DexInstructionData());
// Number of registers for the callee's call frame.
@@ -1288,16 +1257,15 @@
// Allocate shadow frame on the stack.
const char* old_cause = self->StartAssertNoThreadSuspension("DoCallCommon");
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(num_regs, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
// Initialize new shadow frame by copying the registers from the callee shadow frame.
- if (do_assignability_check) {
+ if (!shadow_frame.GetMethod()->SkipAccessChecks()) {
// Slow path.
// We might need to do class loading, which incurs a thread state change to kNative. So
// register the shadow frame as under construction and allow suspension again.
- ScopedStackedShadowFramePusher pusher(
- self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher pusher(self, new_shadow_frame);
self->EndAssertNoThreadSuspension(old_cause);
// ArtMethod here is needed to check type information of the call site against the callee.
@@ -1336,7 +1304,7 @@
// Handle Object references. 1 virtual register slot.
case 'L': {
ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
- if (do_assignability_check && o != nullptr) {
+ if (o != nullptr) {
const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_;
ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx);
if (arg_type == nullptr) {
@@ -1410,9 +1378,15 @@
return !self->IsExceptionPending();
}
-template<bool is_range, bool do_assignability_check>
-bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, uint16_t inst_data, JValue* result) {
+template<bool is_range>
+NO_STACK_PROTECTOR
+bool DoCall(ArtMethod* called_method,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ bool is_string_init,
+ JValue* result) {
// Argument word count.
const uint16_t number_of_inputs =
(is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
@@ -1428,12 +1402,18 @@
inst->GetVarArgs(arg, inst_data);
}
- return DoCallCommon<is_range, do_assignability_check>(
- called_method, self, shadow_frame,
- result, number_of_inputs, arg, vregC);
+ return DoCallCommon<is_range>(
+ called_method,
+ self,
+ shadow_frame,
+ result,
+ number_of_inputs,
+ arg,
+ vregC,
+ is_string_init);
}
-template <bool is_range, bool do_access_check, bool transaction_active>
+template <bool is_range, bool transaction_active>
bool DoFilledNewArray(const Instruction* inst,
const ShadowFrame& shadow_frame,
Thread* self,
@@ -1450,6 +1430,7 @@
return false;
}
uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ bool do_access_check = !shadow_frame.GetMethod()->SkipAccessChecks();
ObjPtr<mirror::Class> array_class = ResolveVerifyAndClinit(dex::TypeIndex(type_idx),
shadow_frame.GetMethod(),
self,
@@ -1554,17 +1535,50 @@
}
}
+void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(shadow_frame->GetForcePopFrame() || Runtime::Current()->IsTransactionAborted());
+ // Unlock all monitors.
+ if (shadow_frame->GetMethod()->MustCountLocks()) {
+ DCHECK(!shadow_frame->GetMethod()->SkipAccessChecks());
+ // Get the monitors from the shadow-frame monitor-count data.
+ shadow_frame->GetLockCountData().VisitMonitors(
+ [&](mirror::Object** obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Since we don't use the 'obj' pointer after the DoMonitorExit everything should be fine
+ // WRT suspension.
+ DoMonitorExit(self, shadow_frame, *obj);
+ });
+ } else {
+ std::vector<verifier::MethodVerifier::DexLockInfo> locks;
+ verifier::MethodVerifier::FindLocksAtDexPc(shadow_frame->GetMethod(),
+ shadow_frame->GetDexPC(),
+ &locks,
+ Runtime::Current()->GetTargetSdkVersion());
+ for (const auto& reg : locks) {
+ if (UNLIKELY(reg.dex_registers.empty())) {
+ LOG(ERROR) << "Unable to determine reference locked by "
+ << shadow_frame->GetMethod()->PrettyMethod() << " at pc "
+ << shadow_frame->GetDexPC();
+ } else {
+ DoMonitorExit(
+ self, shadow_frame, shadow_frame->GetVRegReference(*reg.dex_registers.begin()));
+ }
+ }
+ }
+}
+
// Explicit DoCall template function declarations.
-#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \
- template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Thread* self, \
- ShadowFrame& shadow_frame, \
- const Instruction* inst, uint16_t inst_data, \
- JValue* result)
-EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false);
-EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true);
-EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false);
-EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
+#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoCall<_is_range>(ArtMethod* method, \
+ Thread* self, \
+ ShadowFrame& shadow_frame, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ bool string_init, \
+ JValue* result)
+EXPLICIT_DO_CALL_TEMPLATE_DECL(false);
+EXPLICIT_DO_CALL_TEMPLATE_DECL(true);
#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
// Explicit DoInvokePolymorphic template function declarations.
@@ -1578,16 +1592,15 @@
#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
// Explicit DoFilledNewArray template function declarations.
-#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _transaction_active) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoFilledNewArray<_is_range_, _check, _transaction_active>(const Instruction* inst, \
- const ShadowFrame& shadow_frame, \
- Thread* self, JValue* result)
-#define EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(_transaction_active) \
- EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false, _transaction_active); \
- EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true, _transaction_active); \
- EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false, _transaction_active); \
- EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true, _transaction_active)
+ bool DoFilledNewArray<_is_range_, _transaction_active>(const Instruction* inst, \
+ const ShadowFrame& shadow_frame, \
+ Thread* self, \
+ JValue* result)
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(_transaction_active) \
+ EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, _transaction_active); \
+ EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, _transaction_active)
EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(false);
EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(true);
#undef EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 0b91120..b8d6817 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -20,7 +20,6 @@
#include "android-base/macros.h"
#include "instrumentation.h"
#include "interpreter.h"
-#include "interpreter_intrinsics.h"
#include "transaction.h"
#include <math.h>
@@ -71,7 +70,6 @@
void ThrowNullPointerExceptionFromInterpreter()
REQUIRES_SHARED(Locks::mutator_lock_);
-template <bool kMonitorCounting>
static inline void DoMonitorEnter(Thread* self, ShadowFrame* frame, ObjPtr<mirror::Object> ref)
NO_THREAD_SAFETY_ANALYSIS
REQUIRES(!Roles::uninterruptible_) {
@@ -85,28 +83,29 @@
DCHECK(unlocked);
return;
}
- if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+ if (frame->GetMethod()->MustCountLocks()) {
+ DCHECK(!frame->GetMethod()->SkipAccessChecks());
frame->GetLockCountData().AddMonitor(self, h_ref.Get());
}
}
-template <bool kMonitorCounting>
static inline void DoMonitorExit(Thread* self, ShadowFrame* frame, ObjPtr<mirror::Object> ref)
NO_THREAD_SAFETY_ANALYSIS
REQUIRES(!Roles::uninterruptible_) {
StackHandleScope<1> hs(self);
Handle<mirror::Object> h_ref(hs.NewHandle(ref));
h_ref->MonitorExit(self);
- if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+ if (frame->GetMethod()->MustCountLocks()) {
+ DCHECK(!frame->GetMethod()->SkipAccessChecks());
frame->GetLockCountData().RemoveMonitorOrThrow(self, h_ref.Get());
}
}
-template <bool kMonitorCounting>
static inline bool DoMonitorCheckOnExit(Thread* self, ShadowFrame* frame)
NO_THREAD_SAFETY_ANALYSIS
REQUIRES(!Roles::uninterruptible_) {
- if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+ if (frame->GetMethod()->MustCountLocks()) {
+ DCHECK(!frame->GetMethod()->SkipAccessChecks());
return frame->GetLockCountData().CheckAllMonitorsReleasedOrThrow(self);
}
return true;
@@ -125,9 +124,14 @@
// Invokes the given method. This is part of the invocation support and is used by DoInvoke,
// DoFastInvoke and DoInvokeVirtualQuick functions.
// Returns true on success, otherwise throws an exception and returns false.
-template<bool is_range, bool do_assignability_check>
-bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, uint16_t inst_data, JValue* result);
+template<bool is_range>
+bool DoCall(ArtMethod* called_method,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ bool string_init,
+ JValue* result);
// Called by the switch interpreter to know if we can stay in it.
bool ShouldStayInSwitchInterpreter(ArtMethod* method)
@@ -153,54 +157,16 @@
return ins->HasMethodExitListeners() || ins->HasWatchedFramePopListeners();
}
-// NO_INLINE so we won't bloat the interpreter with this very cold lock-release code.
-template <bool kMonitorCounting>
-static NO_INLINE void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(shadow_frame->GetForcePopFrame() ||
- Runtime::Current()->IsTransactionAborted());
- // Unlock all monitors.
- if (kMonitorCounting && shadow_frame->GetMethod()->MustCountLocks()) {
- // Get the monitors from the shadow-frame monitor-count data.
- shadow_frame->GetLockCountData().VisitMonitors(
- [&](mirror::Object** obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- // Since we don't use the 'obj' pointer after the DoMonitorExit everything should be fine
- // WRT suspension.
- DoMonitorExit<kMonitorCounting>(self, shadow_frame, *obj);
- });
- } else {
- std::vector<verifier::MethodVerifier::DexLockInfo> locks;
- verifier::MethodVerifier::FindLocksAtDexPc(shadow_frame->GetMethod(),
- shadow_frame->GetDexPC(),
- &locks,
- Runtime::Current()->GetTargetSdkVersion());
- for (const auto& reg : locks) {
- if (UNLIKELY(reg.dex_registers.empty())) {
- LOG(ERROR) << "Unable to determine reference locked by "
- << shadow_frame->GetMethod()->PrettyMethod() << " at pc "
- << shadow_frame->GetDexPC();
- } else {
- DoMonitorExit<kMonitorCounting>(
- self, shadow_frame, shadow_frame->GetVRegReference(*reg.dex_registers.begin()));
- }
- }
- }
-}
+COLD_ATTR void UnlockHeldMonitors(Thread* self, ShadowFrame* shadow_frame)
+ REQUIRES_SHARED(Locks::mutator_lock_);
-enum class MonitorState {
- kNoMonitorsLocked,
- kCountingMonitors,
- kNormalMonitors,
-};
-
-template<MonitorState kMonitorState>
static inline ALWAYS_INLINE void PerformNonStandardReturn(
Thread* self,
ShadowFrame& frame,
JValue& result,
const instrumentation::Instrumentation* instrumentation,
- uint16_t num_dex_inst) REQUIRES_SHARED(Locks::mutator_lock_) {
- static constexpr bool kMonitorCounting = (kMonitorState == MonitorState::kCountingMonitors);
+ uint16_t num_dex_inst,
+ bool unlock_monitors = true) REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Object> thiz(frame.GetThisObject(num_dex_inst));
StackHandleScope<1u> hs(self);
if (UNLIKELY(self->IsExceptionPending())) {
@@ -208,10 +174,10 @@
<< self->GetException()->Dump();
self->ClearException();
}
- if (kMonitorState != MonitorState::kNoMonitorsLocked) {
- UnlockHeldMonitors<kMonitorCounting>(self, &frame);
+ if (unlock_monitors) {
+ UnlockHeldMonitors(self, &frame);
+ DoMonitorCheckOnExit(self, &frame);
}
- DoMonitorCheckOnExit<kMonitorCounting>(self, &frame);
result = JValue();
if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) {
SendMethodExitEvents(self, instrumentation, frame, frame.GetMethod(), result);
@@ -220,7 +186,7 @@
// Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
// Returns true on success, otherwise throws an exception and returns false.
-template<InvokeType type, bool is_range, bool do_access_check, bool is_mterp>
+template<InvokeType type, bool is_range>
static ALWAYS_INLINE bool DoInvoke(Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
@@ -231,63 +197,20 @@
if (UNLIKELY(self->ObserveAsyncException())) {
return false;
}
- const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ const uint32_t vregC = is_range ? inst->VRegC_3rc() : inst->VRegC_35c();
+ ObjPtr<mirror::Object> obj = type == kStatic ? nullptr : shadow_frame.GetVRegReference(vregC);
ArtMethod* sf_method = shadow_frame.GetMethod();
-
- // Try to find the method in small thread-local cache first (only used when
- // nterp is not used as mterp and nterp use the cache in an incompatible way).
- InterpreterCache* tls_cache = self->GetInterpreterCache();
- size_t tls_value;
- ArtMethod* resolved_method;
- if (!IsNterpSupported() && LIKELY(tls_cache->Get(self, inst, &tls_value))) {
- resolved_method = reinterpret_cast<ArtMethod*>(tls_value);
- } else {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- constexpr ClassLinker::ResolveMode resolve_mode =
- do_access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
- : ClassLinker::ResolveMode::kNoChecks;
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, sf_method, type);
- if (UNLIKELY(resolved_method == nullptr)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- }
- if (!IsNterpSupported()) {
- tls_cache->Set(self, inst, reinterpret_cast<size_t>(resolved_method));
- }
- }
-
- // Null pointer check and virtual method resolution.
- ObjPtr<mirror::Object> receiver =
- (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
- ArtMethod* called_method;
- called_method = FindMethodToCall<type, do_access_check>(
- method_idx, resolved_method, &receiver, sf_method, self);
- if (UNLIKELY(called_method == nullptr)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- }
- if (UNLIKELY(!called_method->IsInvokable())) {
- called_method->ThrowInvocationTimeError();
+ bool string_init = false;
+ ArtMethod* called_method = FindMethodToCall<type>(
+ self, sf_method, &obj, *inst, /* only_lookup_tls_cache= */ false, &string_init);
+ if (called_method == nullptr) {
+ DCHECK(self->IsExceptionPending());
result->SetJ(0);
return false;
}
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (is_mterp && !is_range && called_method->IsIntrinsic()) {
- if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data,
- shadow_frame.GetResultRegister())) {
- if (jit != nullptr && sf_method != nullptr) {
- jit->NotifyInterpreterToCompiledCodeTransition(self, sf_method);
- }
- return !self->IsExceptionPending();
- }
- }
-
- return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data,
- result);
+ return DoCall<is_range>(
+ called_method, self, shadow_frame, inst, inst_data, string_init, result);
}
static inline ObjPtr<mirror::MethodHandle> ResolveMethodHandle(Thread* self,
@@ -386,24 +309,77 @@
return field_value;
}
+extern "C" size_t NterpGetStaticField(Thread* self,
+ ArtMethod* caller,
+ const uint16_t* dex_pc_ptr,
+ size_t resolve_field_type);
+
+extern "C" uint32_t NterpGetInstanceFieldOffset(Thread* self,
+ ArtMethod* caller,
+ const uint16_t* dex_pc_ptr,
+ size_t resolve_field_type);
+
+static inline void GetFieldInfo(Thread* self,
+ ArtMethod* caller,
+ const uint16_t* dex_pc_ptr,
+ bool is_static,
+ bool resolve_field_type,
+ ArtField** field,
+ bool* is_volatile,
+ MemberOffset* offset) {
+ size_t tls_value = 0u;
+ if (!self->GetInterpreterCache()->Get(self, dex_pc_ptr, &tls_value)) {
+ if (is_static) {
+ tls_value = NterpGetStaticField(self, caller, dex_pc_ptr, resolve_field_type);
+ } else {
+ tls_value = NterpGetInstanceFieldOffset(self, caller, dex_pc_ptr, resolve_field_type);
+ }
+
+ if (self->IsExceptionPending()) {
+ return;
+ }
+ }
+
+ if (is_static) {
+ DCHECK_NE(tls_value, 0u);
+ *is_volatile = ((tls_value & 1) != 0);
+ *field = reinterpret_cast<ArtField*>(tls_value & ~static_cast<size_t>(1u));
+ *offset = (*field)->GetOffset();
+ } else {
+ *is_volatile = (static_cast<int32_t>(tls_value) < 0);
+ *offset = MemberOffset(std::abs(static_cast<int32_t>(tls_value)));
+ }
+}
+
// Handles iget-XXX and sget-XXX instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
+template<FindFieldType find_type,
+ Primitive::Type field_type,
bool transaction_active = false>
-ALWAYS_INLINE bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
+ALWAYS_INLINE bool DoFieldGet(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
uint16_t inst_data) REQUIRES_SHARED(Locks::mutator_lock_) {
const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
- const uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtMethod* method = shadow_frame.GetMethod();
- ArtField* f = FindFieldFromCode<find_type, do_access_check>(
- field_idx, method, self, Primitive::ComponentSize(field_type));
- if (UNLIKELY(f == nullptr)) {
- CHECK(self->IsExceptionPending());
+ bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldReadListeners();
+ ArtField* field = nullptr;
+ MemberOffset offset(0u);
+ bool is_volatile;
+ GetFieldInfo(self,
+ shadow_frame.GetMethod(),
+ reinterpret_cast<const uint16_t*>(inst),
+ is_static,
+ /*resolve_field_type=*/ false,
+ &field,
+ &is_volatile,
+ &offset);
+ if (self->IsExceptionPending()) {
return false;
}
+
ObjPtr<mirror::Object> obj;
if (is_static) {
- obj = f->GetDeclaringClass();
+ obj = field->GetDeclaringClass();
if (transaction_active) {
if (Runtime::Current()->GetTransaction()->ReadConstraint(obj)) {
Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Can't read static fields of "
@@ -413,40 +389,57 @@
}
} else {
obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(f, method, true);
+ if (should_report || obj == nullptr) {
+ field = ResolveFieldWithAccessChecks(self,
+ Runtime::Current()->GetClassLinker(),
+ inst->VRegC_22c(),
+ shadow_frame.GetMethod(),
+ /* is_static= */ false,
+ /* is_put= */ false,
+ /* resolve_field_type= */ false);
+ if (obj == nullptr) {
+ ThrowNullPointerExceptionForFieldAccess(
+ field, shadow_frame.GetMethod(), /* is_read= */ true);
+ return false;
+ }
+ // Reload in case suspension happened during field resolution.
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ }
+ }
+
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ JValue result;
+ if (should_report) {
+ DCHECK(field != nullptr);
+ if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, field, &result))) {
+ // Instrumentation threw an error!
+ CHECK(self->IsExceptionPending());
return false;
}
}
- JValue result;
- if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, f, &result))) {
- // Instrumentation threw an error!
- CHECK(self->IsExceptionPending());
- return false;
- }
- uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+#define FIELD_GET(prim, type, jtype, vreg) \
+ case Primitive::kPrim ##prim: \
+ shadow_frame.SetVReg ##vreg(vregA, \
+ should_report ? result.Get ##jtype() \
+ : is_volatile ? obj->GetField ## type ## Volatile(offset) \
+ : obj->GetField ##type(offset)); \
+ break;
+
switch (field_type) {
- case Primitive::kPrimBoolean:
- shadow_frame.SetVReg(vregA, result.GetZ());
- break;
- case Primitive::kPrimByte:
- shadow_frame.SetVReg(vregA, result.GetB());
- break;
- case Primitive::kPrimChar:
- shadow_frame.SetVReg(vregA, result.GetC());
- break;
- case Primitive::kPrimShort:
- shadow_frame.SetVReg(vregA, result.GetS());
- break;
- case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, result.GetI());
- break;
- case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, result.GetJ());
- break;
+ FIELD_GET(Boolean, Boolean, Z, )
+ FIELD_GET(Byte, Byte, B, )
+ FIELD_GET(Char, Char, C, )
+ FIELD_GET(Short, Short, S, )
+ FIELD_GET(Int, 32, I, )
+ FIELD_GET(Long, 64, J, Long)
+#undef FIELD_GET
case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, result.GetL());
+ shadow_frame.SetVRegReference(
+ vregA,
+ should_report ? result.GetL()
+ : is_volatile ? obj->GetFieldObjectVolatile<mirror::Object>(offset)
+ : obj->GetFieldObject<mirror::Object>(offset));
break;
default:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -485,36 +478,57 @@
// Handles iput-XXX and sput-XXX instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
- bool transaction_active>
-ALWAYS_INLINE bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
- const Instruction* inst, uint16_t inst_data)
+template<FindFieldType find_type, Primitive::Type field_type, bool transaction_active>
+ALWAYS_INLINE bool DoFieldPut(Thread* self,
+ const ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const bool do_assignability_check = do_access_check;
+ bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldWriteListeners();
bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtMethod* method = shadow_frame.GetMethod();
- ArtField* f = FindFieldFromCode<find_type, do_access_check>(
- field_idx, method, self, Primitive::ComponentSize(field_type));
- if (UNLIKELY(f == nullptr)) {
- CHECK(self->IsExceptionPending());
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ bool resolve_field_type = (shadow_frame.GetVRegReference(vregA) != nullptr);
+ ArtField* field = nullptr;
+ MemberOffset offset(0u);
+ bool is_volatile;
+ GetFieldInfo(self,
+ shadow_frame.GetMethod(),
+ reinterpret_cast<const uint16_t*>(inst),
+ is_static,
+ resolve_field_type,
+ &field,
+ &is_volatile,
+ &offset);
+ if (self->IsExceptionPending()) {
return false;
}
+
ObjPtr<mirror::Object> obj;
if (is_static) {
- obj = f->GetDeclaringClass();
+ obj = field->GetDeclaringClass();
} else {
obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(f, method, false);
- return false;
+ if (should_report || obj == nullptr) {
+ field = ResolveFieldWithAccessChecks(self,
+ Runtime::Current()->GetClassLinker(),
+ inst->VRegC_22c(),
+ shadow_frame.GetMethod(),
+ /* is_static= */ false,
+ /* is_put= */ true,
+ resolve_field_type);
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(
+ field, shadow_frame.GetMethod(), /* is_read= */ false);
+ return false;
+ }
+ // Reload in case suspension happened during field resolution.
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
}
}
if (transaction_active && !CheckWriteConstraint(self, obj)) {
return false;
}
- uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
JValue value = GetFieldValue<field_type>(shadow_frame, vregA);
if (transaction_active &&
@@ -522,12 +536,43 @@
!CheckWriteValueConstraint(self, value.GetL())) {
return false;
}
+ if (should_report) {
+ return DoFieldPutCommon<field_type, transaction_active>(self,
+ shadow_frame,
+ obj,
+ field,
+ value);
+ }
+#define FIELD_SET(prim, type, jtype) \
+ case Primitive::kPrim ## prim: \
+ if (is_volatile) { \
+ obj->SetField ## type ## Volatile<transaction_active>(offset, value.Get ## jtype()); \
+ } else { \
+ obj->SetField ## type<transaction_active>(offset, value.Get ## jtype()); \
+ } \
+ break;
- return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
- shadow_frame,
- obj,
- f,
- value);
+ switch (field_type) {
+ FIELD_SET(Boolean, Boolean, Z)
+ FIELD_SET(Byte, Byte, B)
+ FIELD_SET(Char, Char, C)
+ FIELD_SET(Short, Short, S)
+ FIELD_SET(Int, 32, I)
+ FIELD_SET(Long, 64, J)
+ FIELD_SET(Not, Object, L)
+ case Primitive::kPrimVoid: {
+ LOG(FATAL) << "Unreachable " << field_type;
+ break;
+ }
+ }
+#undef FIELD_SET
+
+ if (transaction_active) {
+ if (UNLIKELY(self->IsExceptionPending())) {
+ return false;
+ }
+ }
+ return true;
}
// Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the
@@ -631,13 +676,16 @@
// Handles filled-new-array and filled-new-array-range instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template <bool is_range, bool do_access_check, bool transaction_active>
-bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
- Thread* self, JValue* result);
+template <bool is_range, bool transaction_active>
+bool DoFilledNewArray(const Instruction* inst,
+ const ShadowFrame& shadow_frame,
+ Thread* self,
+ JValue* result);
// Handles packed-switch instruction.
// Returns the branch offset to the next instruction to execute.
-static inline int32_t DoPackedSwitch(const Instruction* inst, const ShadowFrame& shadow_frame,
+static inline int32_t DoPackedSwitch(const Instruction* inst,
+ const ShadowFrame& shadow_frame,
uint16_t inst_data)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(inst->Opcode() == Instruction::PACKED_SWITCH);
@@ -755,34 +803,6 @@
uint16_t arg_offset,
JValue* result);
-static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- const dex::MethodId& method_id = dex_file->GetMethodId(method_idx);
- const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_);
- const char* method_name = dex_file->GetMethodName(method_id);
- // Instead of calling ResolveMethod() which has suspend point and can trigger
- // GC, look up the method symbolically.
- // Compare method's class name and method name against string init.
- // It's ok since it's not allowed to create your own java/lang/String.
- // TODO: verify that assumption.
- if ((strcmp(class_name, "Ljava/lang/String;") == 0) &&
- (strcmp(method_name, "<init>") == 0)) {
- return true;
- }
- return false;
-}
-
-static inline bool IsStringInit(const Instruction* instr, ArtMethod* caller)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (instr->Opcode() == Instruction::INVOKE_DIRECT ||
- instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) {
- uint16_t callee_method_idx = (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ?
- instr->VRegB_3rc() : instr->VRegB_35c();
- return IsStringInit(caller->GetDexFile(), callee_method_idx);
- }
- return false;
-}
-
// Set string value created from StringFactory.newStringFromXXX() into all aliases of
// StringFactory.newEmptyString().
void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
deleted file mode 100644
index c8344bc..0000000
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ /dev/null
@@ -1,678 +0,0 @@
-/*
- * 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.
- */
-
-#include "interpreter/interpreter_intrinsics.h"
-
-#include "dex/dex_instruction.h"
-#include "intrinsics_enum.h"
-#include "interpreter/interpreter_common.h"
-
-namespace art {
-namespace interpreter {
-
-
-#define BINARY_INTRINSIC(name, op, get1, get2, set) \
-static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- result_register->set(op(shadow_frame->get1, shadow_frame->get2)); \
- return true; \
-}
-
-#define BINARY_II_INTRINSIC(name, op, set) \
- BINARY_INTRINSIC(name, op, GetVReg(arg[0]), GetVReg(arg[1]), set)
-
-#define BINARY_JJ_INTRINSIC(name, op, set) \
- BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVRegLong(arg[2]), set)
-
-#define BINARY_JI_INTRINSIC(name, op, set) \
- BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVReg(arg[2]), set)
-
-#define UNARY_INTRINSIC(name, op, get, set) \
-static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- result_register->set(op(shadow_frame->get(arg[0]))); \
- return true; \
-}
-
-
-// java.lang.Integer.reverse(I)I
-UNARY_INTRINSIC(MterpIntegerReverse, ReverseBits32, GetVReg, SetI);
-
-// java.lang.Integer.reverseBytes(I)I
-UNARY_INTRINSIC(MterpIntegerReverseBytes, BSWAP, GetVReg, SetI);
-
-// java.lang.Integer.bitCount(I)I
-UNARY_INTRINSIC(MterpIntegerBitCount, POPCOUNT, GetVReg, SetI);
-
-// java.lang.Integer.compare(II)I
-BINARY_II_INTRINSIC(MterpIntegerCompare, Compare, SetI);
-
-// java.lang.Integer.highestOneBit(I)I
-UNARY_INTRINSIC(MterpIntegerHighestOneBit, HighestOneBitValue, GetVReg, SetI);
-
-// java.lang.Integer.LowestOneBit(I)I
-UNARY_INTRINSIC(MterpIntegerLowestOneBit, LowestOneBitValue, GetVReg, SetI);
-
-// java.lang.Integer.numberOfLeadingZeros(I)I
-UNARY_INTRINSIC(MterpIntegerNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVReg, SetI);
-
-// java.lang.Integer.numberOfTrailingZeros(I)I
-UNARY_INTRINSIC(MterpIntegerNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVReg, SetI);
-
-// java.lang.Integer.rotateRight(II)I
-BINARY_II_INTRINSIC(MterpIntegerRotateRight, (Rot<int32_t, false>), SetI);
-
-// java.lang.Integer.rotateLeft(II)I
-BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot<int32_t, true>), SetI);
-
-// java.lang.Integer.signum(I)I
-UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI);
-
-// java.lang.Long.reverse(J)J
-UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ);
-
-// java.lang.Long.reverseBytes(J)J
-UNARY_INTRINSIC(MterpLongReverseBytes, BSWAP, GetVRegLong, SetJ);
-
-// java.lang.Long.bitCount(J)I
-UNARY_INTRINSIC(MterpLongBitCount, POPCOUNT, GetVRegLong, SetI);
-
-// java.lang.Long.compare(JJ)I
-BINARY_JJ_INTRINSIC(MterpLongCompare, Compare, SetI);
-
-// java.lang.Long.highestOneBit(J)J
-UNARY_INTRINSIC(MterpLongHighestOneBit, HighestOneBitValue, GetVRegLong, SetJ);
-
-// java.lang.Long.lowestOneBit(J)J
-UNARY_INTRINSIC(MterpLongLowestOneBit, LowestOneBitValue, GetVRegLong, SetJ);
-
-// java.lang.Long.numberOfLeadingZeros(J)I
-UNARY_INTRINSIC(MterpLongNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVRegLong, SetJ);
-
-// java.lang.Long.numberOfTrailingZeros(J)I
-UNARY_INTRINSIC(MterpLongNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVRegLong, SetJ);
-
-// java.lang.Long.rotateRight(JI)J
-BINARY_JI_INTRINSIC(MterpLongRotateRight, (Rot<int64_t, false>), SetJ);
-
-// java.lang.Long.rotateLeft(JI)J
-BINARY_JI_INTRINSIC(MterpLongRotateLeft, (Rot<int64_t, true>), SetJ);
-
-// java.lang.Long.signum(J)I
-UNARY_INTRINSIC(MterpLongSignum, Signum, GetVRegLong, SetI);
-
-// java.lang.Short.reverseBytes(S)S
-UNARY_INTRINSIC(MterpShortReverseBytes, BSWAP, GetVRegShort, SetS);
-
-// java.lang.Math.min(II)I
-BINARY_II_INTRINSIC(MterpMathMinIntInt, std::min, SetI);
-
-// java.lang.Math.min(JJ)J
-BINARY_JJ_INTRINSIC(MterpMathMinLongLong, std::min, SetJ);
-
-// java.lang.Math.max(II)I
-BINARY_II_INTRINSIC(MterpMathMaxIntInt, std::max, SetI);
-
-// java.lang.Math.max(JJ)J
-BINARY_JJ_INTRINSIC(MterpMathMaxLongLong, std::max, SetJ);
-
-// java.lang.Math.abs(I)I
-UNARY_INTRINSIC(MterpMathAbsInt, std::abs, GetVReg, SetI);
-
-// java.lang.Math.abs(J)J
-UNARY_INTRINSIC(MterpMathAbsLong, std::abs, GetVRegLong, SetJ);
-
-// java.lang.Math.abs(F)F
-UNARY_INTRINSIC(MterpMathAbsFloat, 0x7fffffff&, GetVReg, SetI);
-
-// java.lang.Math.abs(D)D
-UNARY_INTRINSIC(MterpMathAbsDouble, INT64_C(0x7fffffffffffffff)&, GetVRegLong, SetJ);
-
-// java.lang.Math.sqrt(D)D
-UNARY_INTRINSIC(MterpMathSqrt, std::sqrt, GetVRegDouble, SetD);
-
-// java.lang.Math.ceil(D)D
-UNARY_INTRINSIC(MterpMathCeil, std::ceil, GetVRegDouble, SetD);
-
-// java.lang.Math.floor(D)D
-UNARY_INTRINSIC(MterpMathFloor, std::floor, GetVRegDouble, SetD);
-
-// java.lang.Math.sin(D)D
-UNARY_INTRINSIC(MterpMathSin, std::sin, GetVRegDouble, SetD);
-
-// java.lang.Math.cos(D)D
-UNARY_INTRINSIC(MterpMathCos, std::cos, GetVRegDouble, SetD);
-
-// java.lang.Math.tan(D)D
-UNARY_INTRINSIC(MterpMathTan, std::tan, GetVRegDouble, SetD);
-
-// java.lang.Math.asin(D)D
-UNARY_INTRINSIC(MterpMathAsin, std::asin, GetVRegDouble, SetD);
-
-// java.lang.Math.acos(D)D
-UNARY_INTRINSIC(MterpMathAcos, std::acos, GetVRegDouble, SetD);
-
-// java.lang.Math.atan(D)D
-UNARY_INTRINSIC(MterpMathAtan, std::atan, GetVRegDouble, SetD);
-
-// java.lang.String.charAt(I)C
-static ALWAYS_INLINE bool MterpStringCharAt(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- int length = str->GetLength();
- int index = shadow_frame->GetVReg(arg[1]);
- uint16_t res;
- if (UNLIKELY(index < 0) || (index >= length)) {
- return false; // Punt and let non-intrinsic version deal with the throw.
- }
- if (str->IsCompressed()) {
- res = str->GetValueCompressed()[index];
- } else {
- res = str->GetValue()[index];
- }
- result_register->SetC(res);
- return true;
-}
-
-// java.lang.String.compareTo(Ljava/lang/string)I
-static ALWAYS_INLINE bool MterpStringCompareTo(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- ObjPtr<mirror::Object> arg1 = shadow_frame->GetVRegReference(arg[1]);
- if (arg1 == nullptr) {
- return false;
- }
- result_register->SetI(str->CompareTo(arg1->AsString()));
- return true;
-}
-
-#define STRING_INDEXOF_INTRINSIC(name, starting_pos) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
- int ch = shadow_frame->GetVReg(arg[1]); \
- if (ch >= 0x10000) { \
- /* Punt if supplementary char. */ \
- return false; \
- } \
- result_register->SetI(str->FastIndexOf(ch, starting_pos)); \
- return true; \
-}
-
-// java.lang.String.indexOf(I)I
-STRING_INDEXOF_INTRINSIC(StringIndexOf, 0);
-
-// java.lang.String.indexOf(II)I
-STRING_INDEXOF_INTRINSIC(StringIndexOfAfter, shadow_frame->GetVReg(arg[2]));
-
-#define SIMPLE_STRING_INTRINSIC(name, operation) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
- result_register->operation; \
- return true; \
-}
-
-// java.lang.String.isEmpty()Z
-SIMPLE_STRING_INTRINSIC(StringIsEmpty, SetZ(str->GetLength() == 0))
-
-// java.lang.String.length()I
-SIMPLE_STRING_INTRINSIC(StringLength, SetI(str->GetLength()))
-
-// java.lang.String.getCharsNoCheck(II[CI)V
-static ALWAYS_INLINE bool MterpStringGetCharsNoCheck(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register ATTRIBUTE_UNUSED)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Start, end & index already checked by caller - won't throw. Destination is uncompressed.
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- int32_t start = shadow_frame->GetVReg(arg[1]);
- int32_t end = shadow_frame->GetVReg(arg[2]);
- int32_t index = shadow_frame->GetVReg(arg[4]);
- ObjPtr<mirror::CharArray> array = shadow_frame->GetVRegReference(arg[3])->AsCharArray();
- uint16_t* dst = array->GetData() + index;
- int32_t len = (end - start);
- if (str->IsCompressed()) {
- const uint8_t* src_8 = str->GetValueCompressed() + start;
- for (int i = 0; i < len; i++) {
- dst[i] = src_8[i];
- }
- } else {
- uint16_t* src_16 = str->GetValue() + start;
- memcpy(dst, src_16, len * sizeof(uint16_t));
- }
- return true;
-}
-
-// java.lang.String.equalsLjava/lang/Object;)Z
-static ALWAYS_INLINE bool MterpStringEquals(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- ObjPtr<mirror::Object> obj = shadow_frame->GetVRegReference(arg[1]);
- bool res = false; // Assume not equal.
- if ((obj != nullptr) && obj->IsString()) {
- ObjPtr<mirror::String> str2 = obj->AsString();
- if (str->GetCount() == str2->GetCount()) {
- // Length & compression status are same. Can use block compare.
- void* bytes1;
- void* bytes2;
- int len = str->GetLength();
- if (str->IsCompressed()) {
- bytes1 = str->GetValueCompressed();
- bytes2 = str2->GetValueCompressed();
- } else {
- len *= sizeof(uint16_t);
- bytes1 = str->GetValue();
- bytes2 = str2->GetValue();
- }
- res = (memcmp(bytes1, bytes2, len) == 0);
- }
- }
- result_register->SetZ(res);
- return true;
-}
-
-#define VARHANDLE_FENCE_INTRINSIC(name, std_memory_operation) \
-static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, \
- const Instruction* inst ATTRIBUTE_UNUSED, \
- uint16_t inst_data ATTRIBUTE_UNUSED, \
- JValue* result_register ATTRIBUTE_UNUSED) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- std::atomic_thread_fence(std_memory_operation); \
- return true; \
-}
-
-// The VarHandle fence methods are static (unlike jdk.internal.misc.Unsafe versions).
-// The fences for the LoadLoadFence and StoreStoreFence are stronger
-// than strictly required, but the impact should be marginal.
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleFullFence, std::memory_order_seq_cst)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleAcquireFence, std::memory_order_acquire)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleReleaseFence, std::memory_order_release)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleLoadLoadFence, std::memory_order_acquire)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleStoreStoreFence, std::memory_order_release)
-
-#define METHOD_HANDLE_INVOKE_INTRINSIC(name) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- if (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC) { \
- return DoInvokePolymorphic<false>(Thread::Current(), *shadow_frame, inst, inst_data, result); \
- } else { \
- return DoInvokePolymorphic<true>(Thread::Current(), *shadow_frame, inst, inst_data, result); \
- } \
-}
-
-METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvokeExact)
-METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvoke)
-
-#define VAR_HANDLE_ACCESSOR_INTRINSIC(name) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- return Do##name(Thread::Current(), *shadow_frame, inst, inst_data, result); \
-}
-
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchange)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGet);
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAdd)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAnd)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOr)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXor)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetOpaque)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetVolatile)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetOpaque)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetVolatile)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease)
-
-static ALWAYS_INLINE bool MterpReachabilityFence(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
- const Instruction* inst ATTRIBUTE_UNUSED,
- uint16_t inst_data ATTRIBUTE_UNUSED,
- JValue* result_register ATTRIBUTE_UNUSED)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Do nothing; Its only purpose is to keep the argument reference live
- // at preceding suspend points. That's automatic in the interpreter.
- return true;
-}
-
-// Macro to help keep track of what's left to implement.
-#define UNIMPLEMENTED_CASE(name) \
- case Intrinsics::k##name: \
- res = false; \
- break;
-
-#define INTRINSIC_CASE(name) \
- case Intrinsics::k##name: \
- res = Mterp##name(shadow_frame, inst, inst_data, result_register); \
- break;
-
-bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
- ArtMethod* const called_method,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- Intrinsics intrinsic = static_cast<Intrinsics>(called_method->GetIntrinsic());
- bool res = false; // Assume failure
- switch (intrinsic) {
- UNIMPLEMENTED_CASE(DoubleDoubleToRawLongBits /* (D)J */)
- UNIMPLEMENTED_CASE(DoubleDoubleToLongBits /* (D)J */)
- UNIMPLEMENTED_CASE(DoubleIsInfinite /* (D)Z */)
- UNIMPLEMENTED_CASE(DoubleIsNaN /* (D)Z */)
- UNIMPLEMENTED_CASE(DoubleLongBitsToDouble /* (J)D */)
- UNIMPLEMENTED_CASE(FloatFloatToRawIntBits /* (F)I */)
- UNIMPLEMENTED_CASE(FloatFloatToIntBits /* (F)I */)
- UNIMPLEMENTED_CASE(FloatIsInfinite /* (F)Z */)
- UNIMPLEMENTED_CASE(FloatIsNaN /* (F)Z */)
- UNIMPLEMENTED_CASE(FloatIntBitsToFloat /* (I)F */)
- UNIMPLEMENTED_CASE(IntegerDivideUnsigned /* (II)I */)
- UNIMPLEMENTED_CASE(LongDivideUnsigned /* (JJ)J */)
- INTRINSIC_CASE(IntegerReverse)
- INTRINSIC_CASE(IntegerReverseBytes)
- INTRINSIC_CASE(IntegerBitCount)
- INTRINSIC_CASE(IntegerCompare)
- INTRINSIC_CASE(IntegerHighestOneBit)
- INTRINSIC_CASE(IntegerLowestOneBit)
- INTRINSIC_CASE(IntegerNumberOfLeadingZeros)
- INTRINSIC_CASE(IntegerNumberOfTrailingZeros)
- INTRINSIC_CASE(IntegerRotateRight)
- INTRINSIC_CASE(IntegerRotateLeft)
- INTRINSIC_CASE(IntegerSignum)
- INTRINSIC_CASE(LongReverse)
- INTRINSIC_CASE(LongReverseBytes)
- INTRINSIC_CASE(LongBitCount)
- INTRINSIC_CASE(LongCompare)
- INTRINSIC_CASE(LongHighestOneBit)
- INTRINSIC_CASE(LongLowestOneBit)
- INTRINSIC_CASE(LongNumberOfLeadingZeros)
- INTRINSIC_CASE(LongNumberOfTrailingZeros)
- INTRINSIC_CASE(LongRotateRight)
- INTRINSIC_CASE(LongRotateLeft)
- INTRINSIC_CASE(LongSignum)
- INTRINSIC_CASE(ShortReverseBytes)
- INTRINSIC_CASE(MathAbsDouble)
- INTRINSIC_CASE(MathAbsFloat)
- INTRINSIC_CASE(MathAbsLong)
- INTRINSIC_CASE(MathAbsInt)
- UNIMPLEMENTED_CASE(MathFmaDouble /* (DDD)D */)
- UNIMPLEMENTED_CASE(MathFmaFloat /* (FFF)F */)
- UNIMPLEMENTED_CASE(MathMinDoubleDouble /* (DD)D */)
- UNIMPLEMENTED_CASE(MathMinFloatFloat /* (FF)F */)
- INTRINSIC_CASE(MathMinLongLong)
- INTRINSIC_CASE(MathMinIntInt)
- UNIMPLEMENTED_CASE(MathMaxDoubleDouble /* (DD)D */)
- UNIMPLEMENTED_CASE(MathMaxFloatFloat /* (FF)F */)
- INTRINSIC_CASE(MathMaxLongLong)
- INTRINSIC_CASE(MathMaxIntInt)
- INTRINSIC_CASE(MathCos)
- INTRINSIC_CASE(MathSin)
- INTRINSIC_CASE(MathAcos)
- INTRINSIC_CASE(MathAsin)
- INTRINSIC_CASE(MathAtan)
- UNIMPLEMENTED_CASE(MathAtan2 /* (DD)D */)
- UNIMPLEMENTED_CASE(MathCbrt /* (D)D */)
- UNIMPLEMENTED_CASE(MathCosh /* (D)D */)
- UNIMPLEMENTED_CASE(MathExp /* (D)D */)
- UNIMPLEMENTED_CASE(MathExpm1 /* (D)D */)
- UNIMPLEMENTED_CASE(MathHypot /* (DD)D */)
- UNIMPLEMENTED_CASE(MathLog /* (D)D */)
- UNIMPLEMENTED_CASE(MathLog10 /* (D)D */)
- UNIMPLEMENTED_CASE(MathNextAfter /* (DD)D */)
- UNIMPLEMENTED_CASE(MathPow /* (DD)D */)
- UNIMPLEMENTED_CASE(MathSinh /* (D)D */)
- INTRINSIC_CASE(MathTan)
- UNIMPLEMENTED_CASE(MathTanh /* (D)D */)
- INTRINSIC_CASE(MathSqrt)
- INTRINSIC_CASE(MathCeil)
- INTRINSIC_CASE(MathFloor)
- UNIMPLEMENTED_CASE(MathRint /* (D)D */)
- UNIMPLEMENTED_CASE(MathRoundDouble /* (D)J */)
- UNIMPLEMENTED_CASE(MathRoundFloat /* (F)I */)
- UNIMPLEMENTED_CASE(MathMultiplyHigh /* (JJ)J */)
- UNIMPLEMENTED_CASE(SystemArrayCopyByte /* ([BI[BII)V */)
- UNIMPLEMENTED_CASE(SystemArrayCopyChar /* ([CI[CII)V */)
- UNIMPLEMENTED_CASE(SystemArrayCopyInt /* ([II[III)V */)
- UNIMPLEMENTED_CASE(SystemArrayCopy /* (Ljava/lang/Object;ILjava/lang/Object;II)V */)
- UNIMPLEMENTED_CASE(ThreadCurrentThread /* ()Ljava/lang/Thread; */)
- UNIMPLEMENTED_CASE(MemoryPeekByte /* (J)B */)
- UNIMPLEMENTED_CASE(MemoryPeekIntNative /* (J)I */)
- UNIMPLEMENTED_CASE(MemoryPeekLongNative /* (J)J */)
- UNIMPLEMENTED_CASE(MemoryPeekShortNative /* (J)S */)
- UNIMPLEMENTED_CASE(MemoryPokeByte /* (JB)V */)
- UNIMPLEMENTED_CASE(MemoryPokeIntNative /* (JI)V */)
- UNIMPLEMENTED_CASE(MemoryPokeLongNative /* (JJ)V */)
- UNIMPLEMENTED_CASE(MemoryPokeShortNative /* (JS)V */)
- INTRINSIC_CASE(ReachabilityFence /* (Ljava/lang/Object;)V */)
- INTRINSIC_CASE(StringCharAt)
- INTRINSIC_CASE(StringCompareTo)
- INTRINSIC_CASE(StringEquals)
- INTRINSIC_CASE(StringGetCharsNoCheck)
- INTRINSIC_CASE(StringIndexOf)
- INTRINSIC_CASE(StringIndexOfAfter)
- UNIMPLEMENTED_CASE(StringStringIndexOf /* (Ljava/lang/String;)I */)
- UNIMPLEMENTED_CASE(StringStringIndexOfAfter /* (Ljava/lang/String;I)I */)
- INTRINSIC_CASE(StringIsEmpty)
- INTRINSIC_CASE(StringLength)
- UNIMPLEMENTED_CASE(StringNewStringFromBytes /* ([BIII)Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(StringNewStringFromChars /* (II[C)Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(StringNewStringFromString /* (Ljava/lang/String;)Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(StringBufferAppend /* (Ljava/lang/String;)Ljava/lang/StringBuffer; */)
- UNIMPLEMENTED_CASE(StringBufferLength /* ()I */)
- UNIMPLEMENTED_CASE(StringBufferToString /* ()Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(
- StringBuilderAppendObject /* (Ljava/lang/Object;)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(
- StringBuilderAppendString /* (Ljava/lang/String;)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(
- StringBuilderAppendCharSequence /* (Ljava/lang/CharSequence;)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendCharArray /* ([C)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendBoolean /* (Z)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendChar /* (C)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendInt /* (I)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendLong /* (J)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendFloat /* (F)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendDouble /* (D)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderLength /* ()I */)
- UNIMPLEMENTED_CASE(StringBuilderToString /* ()Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(UnsafeCASInt /* (Ljava/lang/Object;JII)Z */)
- UNIMPLEMENTED_CASE(UnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */)
- UNIMPLEMENTED_CASE(UnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(UnsafeGet /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(UnsafeGetVolatile /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(UnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(UnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(UnsafeGetLong /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(UnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(UnsafePut /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(UnsafePutOrdered /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(UnsafePutVolatile /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(UnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(UnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(UnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(UnsafePutLong /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(UnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(UnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(UnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(UnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(UnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(UnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(UnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(UnsafeLoadFence /* ()V */)
- UNIMPLEMENTED_CASE(UnsafeStoreFence /* ()V */)
- UNIMPLEMENTED_CASE(UnsafeFullFence /* ()V */)
- UNIMPLEMENTED_CASE(JdkUnsafeCASInt /* (Ljava/lang/Object;JII)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetInt /* (Ljava/lang/Object;JII)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetLong /* (Ljava/lang/Object;JJJ)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeGet /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetVolatile /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAcquire /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetObjectAcquire /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetLong /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetLongAcquire /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(JdkUnsafePut /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutOrdered /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutVolatile /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutRelease /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObjectRelease /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLong /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLongRelease /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeLoadFence /* ()V */)
- UNIMPLEMENTED_CASE(JdkUnsafeStoreFence /* ()V */)
- UNIMPLEMENTED_CASE(JdkUnsafeFullFence /* ()V */)
- UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(ReferenceRefersTo /* (Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
- UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
- UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
- UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */)
- UNIMPLEMENTED_CASE(CRC32UpdateByteBuffer /* (IJII)I */)
- UNIMPLEMENTED_CASE(FP16Compare /* (SS)I */)
- UNIMPLEMENTED_CASE(FP16ToFloat /* (S)F */)
- UNIMPLEMENTED_CASE(FP16ToHalf /* (F)S */)
- UNIMPLEMENTED_CASE(FP16Floor /* (S)S */)
- UNIMPLEMENTED_CASE(FP16Ceil /* (S)S */)
- UNIMPLEMENTED_CASE(FP16Rint /* (S)S */)
- UNIMPLEMENTED_CASE(FP16Greater /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16GreaterEquals /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16Less /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16LessEquals /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16Min /* (SS)S */)
- UNIMPLEMENTED_CASE(FP16Max /* (SS)S */)
- INTRINSIC_CASE(VarHandleFullFence)
- INTRINSIC_CASE(VarHandleAcquireFence)
- INTRINSIC_CASE(VarHandleReleaseFence)
- INTRINSIC_CASE(VarHandleLoadLoadFence)
- INTRINSIC_CASE(VarHandleStoreStoreFence)
- INTRINSIC_CASE(MethodHandleInvokeExact)
- INTRINSIC_CASE(MethodHandleInvoke)
- INTRINSIC_CASE(VarHandleCompareAndExchange)
- INTRINSIC_CASE(VarHandleCompareAndExchangeAcquire)
- INTRINSIC_CASE(VarHandleCompareAndExchangeRelease)
- INTRINSIC_CASE(VarHandleCompareAndSet)
- INTRINSIC_CASE(VarHandleGet)
- INTRINSIC_CASE(VarHandleGetAcquire)
- INTRINSIC_CASE(VarHandleGetAndAdd)
- INTRINSIC_CASE(VarHandleGetAndAddAcquire)
- INTRINSIC_CASE(VarHandleGetAndAddRelease)
- INTRINSIC_CASE(VarHandleGetAndBitwiseAnd)
- INTRINSIC_CASE(VarHandleGetAndBitwiseAndAcquire)
- INTRINSIC_CASE(VarHandleGetAndBitwiseAndRelease)
- INTRINSIC_CASE(VarHandleGetAndBitwiseOr)
- INTRINSIC_CASE(VarHandleGetAndBitwiseOrAcquire)
- INTRINSIC_CASE(VarHandleGetAndBitwiseOrRelease)
- INTRINSIC_CASE(VarHandleGetAndBitwiseXor)
- INTRINSIC_CASE(VarHandleGetAndBitwiseXorAcquire)
- INTRINSIC_CASE(VarHandleGetAndBitwiseXorRelease)
- INTRINSIC_CASE(VarHandleGetAndSet)
- INTRINSIC_CASE(VarHandleGetAndSetAcquire)
- INTRINSIC_CASE(VarHandleGetAndSetRelease)
- INTRINSIC_CASE(VarHandleGetOpaque)
- INTRINSIC_CASE(VarHandleGetVolatile)
- INTRINSIC_CASE(VarHandleSet)
- INTRINSIC_CASE(VarHandleSetOpaque)
- INTRINSIC_CASE(VarHandleSetRelease)
- INTRINSIC_CASE(VarHandleSetVolatile)
- INTRINSIC_CASE(VarHandleWeakCompareAndSet)
- INTRINSIC_CASE(VarHandleWeakCompareAndSetAcquire)
- INTRINSIC_CASE(VarHandleWeakCompareAndSetPlain)
- INTRINSIC_CASE(VarHandleWeakCompareAndSetRelease)
- case Intrinsics::kNone:
- res = false;
- break;
- // Note: no default case to ensure we catch any newly added intrinsics.
- }
- return res;
-}
-
-} // namespace interpreter
-} // namespace art
diff --git a/runtime/interpreter/interpreter_intrinsics.h b/runtime/interpreter/interpreter_intrinsics.h
deleted file mode 100644
index 2a23002..0000000
--- a/runtime/interpreter/interpreter_intrinsics.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
-#define ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
-
-#include "jvalue.h"
-
-namespace art {
-
-class ArtMethod;
-class Instruction;
-class ShadowFrame;
-
-namespace interpreter {
-
-// Invokes to methods identified as intrinics are routed here. If there is
-// no interpreter implementation, return false and a normal invoke will proceed.
-bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
- ArtMethod* const called_method,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register);
-
-} // namespace interpreter
-} // namespace art
-
-#endif // ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index d95c507..ddde26d 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -50,7 +50,7 @@
// The function names must match the names from dex_instruction_list.h and have no arguments.
// Return value: The handlers must return false if the instruction throws or returns (exits).
//
-template<bool do_access_check, bool transaction_active, Instruction::Format kFormat>
+template<bool transaction_active, Instruction::Format kFormat>
class InstructionHandler {
public:
#define HANDLER_ATTRIBUTES ALWAYS_INLINE FLATTEN WARN_UNUSED REQUIRES_SHARED(Locks::mutator_lock_)
@@ -64,7 +64,7 @@
DCHECK(abort_exception != nullptr);
DCHECK(abort_exception->GetClass()->DescriptorEquals(Transaction::kAbortExceptionDescriptor));
Self()->ClearException();
- PerformNonStandardReturn<kMonitorState>(
+ PerformNonStandardReturn(
Self(), shadow_frame_, ctx_->result, Instrumentation(), Accessor().InsSize());
Self()->SetException(abort_exception.Get());
ExitInterpreterLoop();
@@ -76,7 +76,7 @@
HANDLER_ATTRIBUTES bool CheckForceReturn() {
if (shadow_frame_.GetForcePopFrame()) {
DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- PerformNonStandardReturn<kMonitorState>(
+ PerformNonStandardReturn(
Self(), shadow_frame_, ctx_->result, Instrumentation(), Accessor().InsSize());
ExitInterpreterLoop();
return false;
@@ -100,7 +100,7 @@
/* skip_listeners= */ skip_event,
/* skip_throw_listener= */ skip_event)) {
// Structured locking is to be enforced for abnormal termination, too.
- DoMonitorCheckOnExit<do_assignability_check>(Self(), &shadow_frame_);
+ DoMonitorCheckOnExit(Self(), &shadow_frame_);
ctx_->result = JValue(); /* Handled in caller. */
ExitInterpreterLoop();
return false; // Return to caller.
@@ -144,7 +144,7 @@
if (!CheckForceReturn()) {
return false;
}
- if (UNLIKELY(Instrumentation()->HasDexPcListeners())) {
+ if (UNLIKELY(shadow_frame_.GetNotifyDexPcMoveEvents())) {
uint8_t opcode = inst_->Opcode(inst_data_);
bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT);
JValue* save_ref = is_move_result_object ? &ctx_->result_register : nullptr;
@@ -204,7 +204,7 @@
HANDLER_ATTRIBUTES bool HandleReturn(JValue result) {
Self()->AllowThreadSuspension();
- if (!DoMonitorCheckOnExit<do_assignability_check>(Self(), &shadow_frame_)) {
+ if (!DoMonitorCheckOnExit(Self(), &shadow_frame_)) {
return false;
}
if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()) &&
@@ -341,19 +341,19 @@
template<FindFieldType find_type, Primitive::Type field_type>
HANDLER_ATTRIBUTES bool HandleGet() {
- return DoFieldGet<find_type, field_type, do_access_check, transaction_active>(
+ return DoFieldGet<find_type, field_type, transaction_active>(
Self(), shadow_frame_, inst_, inst_data_);
}
template<FindFieldType find_type, Primitive::Type field_type>
HANDLER_ATTRIBUTES bool HandlePut() {
- return DoFieldPut<find_type, field_type, do_access_check, transaction_active>(
+ return DoFieldPut<find_type, field_type, transaction_active>(
Self(), shadow_frame_, inst_, inst_data_);
}
template<InvokeType type, bool is_range>
HANDLER_ATTRIBUTES bool HandleInvoke() {
- bool success = DoInvoke<type, is_range, do_access_check, /*is_mterp=*/ false>(
+ bool success = DoInvoke<type, is_range>(
Self(), shadow_frame_, inst_, inst_data_, ResultRegister());
return PossiblyHandlePendingExceptionOnInvoke(!success);
}
@@ -457,12 +457,12 @@
HANDLER_ATTRIBUTES bool RETURN_OBJECT() {
JValue result;
Self()->AllowThreadSuspension();
- if (!DoMonitorCheckOnExit<do_assignability_check>(Self(), &shadow_frame_)) {
+ if (!DoMonitorCheckOnExit(Self(), &shadow_frame_)) {
return false;
}
const size_t ref_idx = A();
ObjPtr<mirror::Object> obj_result = GetVRegReference(ref_idx);
- if (do_assignability_check && obj_result != nullptr) {
+ if (obj_result != nullptr && UNLIKELY(DoAssignabilityChecks())) {
ObjPtr<mirror::Class> return_type = shadow_frame_.GetMethod()->ResolveReturnType();
// Re-load since it might have moved.
obj_result = GetVRegReference(ref_idx);
@@ -481,22 +481,23 @@
return false; // Pending exception.
}
}
- StackHandleScope<1> hs(Self());
- MutableHandle<mirror::Object> h_result(hs.NewHandle(obj_result));
result.SetL(obj_result);
- if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()) &&
- !SendMethodExitEvents(Self(),
- Instrumentation(),
- shadow_frame_,
- shadow_frame_.GetMethod(),
- h_result))) {
- DCHECK(Self()->IsExceptionPending());
- // Do not raise exception event if it is caused by other instrumentation event.
- shadow_frame_.SetSkipNextExceptionEvent(true);
- return false; // Pending exception.
+ if (UNLIKELY(NeedsMethodExitEvent(Instrumentation()))) {
+ StackHandleScope<1> hs(Self());
+ MutableHandle<mirror::Object> h_result(hs.NewHandle(obj_result));
+ if (!SendMethodExitEvents(Self(),
+ Instrumentation(),
+ shadow_frame_,
+ shadow_frame_.GetMethod(),
+ h_result)) {
+ DCHECK(Self()->IsExceptionPending());
+ // Do not raise exception event if it is caused by other instrumentation event.
+ shadow_frame_.SetSkipNextExceptionEvent(true);
+ return false; // Pending exception.
+ }
+ // Re-load since it might have moved or been replaced during the MethodExitEvent.
+ result.SetL(h_result.Get());
}
- // Re-load since it might have moved or been replaced during the MethodExitEvent.
- result.SetL(h_result.Get());
ctx_->result = result;
ExitInterpreterLoop();
return false;
@@ -551,11 +552,12 @@
}
HANDLER_ATTRIBUTES bool CONST_CLASS() {
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(B()),
- shadow_frame_.GetMethod(),
- Self(),
- false,
- do_access_check);
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(dex::TypeIndex(B()),
+ shadow_frame_.GetMethod(),
+ Self(),
+ false,
+ !shadow_frame_.GetMethod()->SkipAccessChecks());
if (UNLIKELY(c == nullptr)) {
return false; // Pending exception.
}
@@ -596,7 +598,7 @@
ThrowNullPointerExceptionFromInterpreter();
return false; // Pending exception.
}
- DoMonitorEnter<do_assignability_check>(Self(), &shadow_frame_, obj);
+ DoMonitorEnter(Self(), &shadow_frame_, obj);
return !Self()->IsExceptionPending();
}
@@ -609,16 +611,17 @@
ThrowNullPointerExceptionFromInterpreter();
return false; // Pending exception.
}
- DoMonitorExit<do_assignability_check>(Self(), &shadow_frame_, obj);
+ DoMonitorExit(Self(), &shadow_frame_, obj);
return !Self()->IsExceptionPending();
}
HANDLER_ATTRIBUTES bool CHECK_CAST() {
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(B()),
- shadow_frame_.GetMethod(),
- Self(),
- false,
- do_access_check);
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(dex::TypeIndex(B()),
+ shadow_frame_.GetMethod(),
+ Self(),
+ false,
+ !shadow_frame_.GetMethod()->SkipAccessChecks());
if (UNLIKELY(c == nullptr)) {
return false; // Pending exception.
}
@@ -631,11 +634,12 @@
}
HANDLER_ATTRIBUTES bool INSTANCE_OF() {
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(C()),
- shadow_frame_.GetMethod(),
- Self(),
- false,
- do_access_check);
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(dex::TypeIndex(C()),
+ shadow_frame_.GetMethod(),
+ Self(),
+ false,
+ !shadow_frame_.GetMethod()->SkipAccessChecks());
if (UNLIKELY(c == nullptr)) {
return false; // Pending exception.
}
@@ -656,11 +660,12 @@
HANDLER_ATTRIBUTES bool NEW_INSTANCE() {
ObjPtr<mirror::Object> obj = nullptr;
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(B()),
- shadow_frame_.GetMethod(),
- Self(),
- false,
- do_access_check);
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(dex::TypeIndex(B()),
+ shadow_frame_.GetMethod(),
+ Self(),
+ false,
+ !shadow_frame_.GetMethod()->SkipAccessChecks());
if (LIKELY(c != nullptr)) {
// Don't allow finalizable objects to be allocated during a transaction since these can't
// be finalized without a started runtime.
@@ -687,7 +692,7 @@
HANDLER_ATTRIBUTES bool NEW_ARRAY() {
int32_t length = GetVReg(B());
- ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check>(
+ ObjPtr<mirror::Object> obj = AllocArrayFromCode(
dex::TypeIndex(C()),
length,
shadow_frame_.GetMethod(),
@@ -701,12 +706,12 @@
}
HANDLER_ATTRIBUTES bool FILLED_NEW_ARRAY() {
- return DoFilledNewArray<false, do_access_check, transaction_active>(
+ return DoFilledNewArray<false, transaction_active>(
inst_, shadow_frame_, Self(), ResultRegister());
}
HANDLER_ATTRIBUTES bool FILLED_NEW_ARRAY_RANGE() {
- return DoFilledNewArray<true, do_access_check, transaction_active>(
+ return DoFilledNewArray<true, transaction_active>(
inst_, shadow_frame_, Self(), ResultRegister());
}
@@ -731,7 +736,7 @@
ObjPtr<mirror::Object> exception = GetVRegReference(A());
if (UNLIKELY(exception == nullptr)) {
ThrowNullPointerException();
- } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+ } else if (DoAssignabilityChecks() && !exception->GetClass()->IsThrowableClass()) {
// This should never happen.
std::string temp;
Self()->ThrowNewExceptionF("Ljava/lang/InternalError;",
@@ -1741,9 +1746,9 @@
}
private:
- static constexpr bool do_assignability_check = do_access_check;
- static constexpr MonitorState kMonitorState =
- do_assignability_check ? MonitorState::kCountingMonitors : MonitorState::kNormalMonitors;
+ bool DoAssignabilityChecks() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return !shadow_frame_.GetMethod()->SkipAccessChecks();
+ }
ALWAYS_INLINE const CodeItemDataAccessor& Accessor() { return ctx_->accessor; }
ALWAYS_INLINE const uint16_t* Insns() { return ctx_->accessor.Insns(); }
@@ -1815,8 +1820,8 @@
#endif
#define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v) \
-template<bool do_access_check, bool transaction_active> \
-ASAN_NO_INLINE static bool OP_##OPCODE_NAME( \
+template<bool transaction_active> \
+ASAN_NO_INLINE NO_STACK_PROTECTOR static bool OP_##OPCODE_NAME( \
SwitchImplContext* ctx, \
const instrumentation::Instrumentation* instrumentation, \
Thread* self, \
@@ -1826,14 +1831,15 @@
uint16_t inst_data, \
const Instruction*& next, \
bool& exit) REQUIRES_SHARED(Locks::mutator_lock_) { \
- InstructionHandler<do_access_check, transaction_active, Instruction::FORMAT> handler( \
+ InstructionHandler<transaction_active, Instruction::FORMAT> handler( \
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit); \
return LIKELY(handler.OPCODE_NAME()); \
}
DEX_INSTRUCTION_LIST(OPCODE_CASE)
#undef OPCODE_CASE
-template<bool do_access_check, bool transaction_active>
+template<bool transaction_active>
+NO_STACK_PROTECTOR
void ExecuteSwitchImplCpp(SwitchImplContext* ctx) {
Thread* self = ctx->self;
const CodeItemDataAccessor& accessor = ctx->accessor;
@@ -1857,7 +1863,7 @@
uint16_t inst_data = inst->Fetch16(0);
bool exit = false;
bool success; // Moved outside to keep frames small under asan.
- if (InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat>(
+ if (InstructionHandler<transaction_active, Instruction::kInvalidFormat>(
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit).
Preamble()) {
DCHECK_EQ(self->IsExceptionPending(), inst->Opcode(inst_data) == Instruction::MOVE_EXCEPTION);
@@ -1865,7 +1871,7 @@
#define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v) \
case OPCODE: { \
next = inst->RelativeAt(Instruction::SizeInCodeUnits(Instruction::FORMAT)); \
- success = OP_##OPCODE_NAME<do_access_check, transaction_active>( \
+ success = OP_##OPCODE_NAME<transaction_active>( \
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit); \
if (success && LIKELY(!interpret_one_instruction)) { \
continue; \
@@ -1881,7 +1887,7 @@
return; // Return statement or debugger forced exit.
}
if (self->IsExceptionPending()) {
- if (!InstructionHandler<do_access_check, transaction_active, Instruction::kInvalidFormat>(
+ if (!InstructionHandler<transaction_active, Instruction::kInvalidFormat>(
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit).
HandlePendingException()) {
shadow_frame.SetDexPC(dex::kDexNoIndex);
diff --git a/runtime/interpreter/interpreter_switch_impl.h b/runtime/interpreter/interpreter_switch_impl.h
index d4dca11..3a42c21 100644
--- a/runtime/interpreter/interpreter_switch_impl.h
+++ b/runtime/interpreter/interpreter_switch_impl.h
@@ -45,7 +45,7 @@
};
// The actual internal implementation of the switch interpreter.
-template<bool do_access_check, bool transaction_active>
+template<bool transaction_active>
void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -55,9 +55,11 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Wrapper around the switch interpreter which ensures we can unwind through it.
-template<bool do_access_check, bool transaction_active>
-ALWAYS_INLINE JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor,
- ShadowFrame& shadow_frame, JValue result_register,
+template<bool transaction_active>
+ALWAYS_INLINE JValue ExecuteSwitchImpl(Thread* self,
+ const CodeItemDataAccessor& accessor,
+ ShadowFrame& shadow_frame,
+ JValue result_register,
bool interpret_one_instruction)
REQUIRES_SHARED(Locks::mutator_lock_) {
SwitchImplContext ctx {
@@ -68,7 +70,7 @@
.interpret_one_instruction = interpret_one_instruction,
.result = JValue(),
};
- void* impl = reinterpret_cast<void*>(&ExecuteSwitchImplCpp<do_access_check, transaction_active>);
+ void* impl = reinterpret_cast<void*>(&ExecuteSwitchImplCpp<transaction_active>);
const uint16_t* dex_pc = ctx.accessor.Insns();
ExecuteSwitchImplAsm(&ctx, impl, dex_pc);
return ctx.result;
diff --git a/runtime/interpreter/interpreter_switch_impl0.cc b/runtime/interpreter/interpreter_switch_impl0.cc
index 00159ec..b4e5f50 100644
--- a/runtime/interpreter/interpreter_switch_impl0.cc
+++ b/runtime/interpreter/interpreter_switch_impl0.cc
@@ -24,7 +24,7 @@
// Explicit definition of ExecuteSwitchImplCpp.
template HOT_ATTR
-void ExecuteSwitchImplCpp<false, false>(SwitchImplContext* ctx);
+void ExecuteSwitchImplCpp<false>(SwitchImplContext* ctx);
} // namespace interpreter
} // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl1.cc b/runtime/interpreter/interpreter_switch_impl1.cc
index 3a86765..f8f9fcc 100644
--- a/runtime/interpreter/interpreter_switch_impl1.cc
+++ b/runtime/interpreter/interpreter_switch_impl1.cc
@@ -24,7 +24,7 @@
// Explicit definition of ExecuteSwitchImplCpp.
template
-void ExecuteSwitchImplCpp<false, true>(SwitchImplContext* ctx);
+void ExecuteSwitchImplCpp<true>(SwitchImplContext* ctx);
} // namespace interpreter
} // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl2.cc b/runtime/interpreter/interpreter_switch_impl2.cc
deleted file mode 100644
index c2739c1..0000000
--- a/runtime/interpreter/interpreter_switch_impl2.cc
+++ /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.
- */
-
-// The interpreter function takes considerable time to compile and link.
-// We compile the explicit definitions separately to speed up the build.
-
-#include "interpreter_switch_impl-inl.h"
-
-namespace art {
-namespace interpreter {
-
-// Explicit definition of ExecuteSwitchImplCpp.
-template HOT_ATTR
-void ExecuteSwitchImplCpp<true, false>(SwitchImplContext* ctx);
-
-} // namespace interpreter
-} // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl3.cc b/runtime/interpreter/interpreter_switch_impl3.cc
deleted file mode 100644
index 808e4bc..0000000
--- a/runtime/interpreter/interpreter_switch_impl3.cc
+++ /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.
- */
-
-// The interpreter function takes considerable time to compile and link.
-// We compile the explicit definitions separately to speed up the build.
-
-#include "interpreter_switch_impl-inl.h"
-
-namespace art {
-namespace interpreter {
-
-// Explicit definition of ExecuteSwitchImplCpp.
-template
-void ExecuteSwitchImplCpp<true, true>(SwitchImplContext* ctx);
-
-} // namespace interpreter
-} // namespace art
diff --git a/runtime/interpreter/mterp/arm64ng/array.S b/runtime/interpreter/mterp/arm64ng/array.S
index 6863662..9edbb22 100644
--- a/runtime/interpreter/mterp/arm64ng/array.S
+++ b/runtime/interpreter/mterp/arm64ng/array.S
@@ -179,7 +179,7 @@
mov x0, xSELF
ldr x1, [sp, 0]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/arm64ng/main.S b/runtime/interpreter/mterp/arm64ng/main.S
index 89de81f..99816c6 100644
--- a/runtime/interpreter/mterp/arm64ng/main.S
+++ b/runtime/interpreter/mterp/arm64ng/main.S
@@ -273,7 +273,9 @@
bl \helper
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ ldr xIP0, [xSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field.
+ cbnz xIP0, nterp_deliver_pending_exception
+ ret
END \name
.endm
@@ -1590,6 +1592,29 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ ldr wip, [x0, #ART_METHOD_DECLARING_CLASS_OFFSET]
+ ldrb wip2, [ip, #MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET]
+ cmp ip2, #MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE
+ b.hs ExecuteNterpImpl
+ cmp ip2, #MIRROR_CLASS_IS_INITIALIZED_VALUE
+ b.lo .Linitializing_check
+ dmb ish
+ b ExecuteNterpImpl
+.Linitializing_check:
+ cmp ip2, #MIRROR_CLASS_IS_INITIALIZING_VALUE
+ b.lo .Lresolution_trampoline
+ ldr wip2, [ip, #MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET]
+ ldr wip, [xSELF, #THREAD_TID_OFFSET]
+ cmp wip, wip2
+ b.eq ExecuteNterpImpl
+.Lresolution_trampoline:
+ b art_quick_resolution_trampoline
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
sub x16, sp, #STACK_OVERFLOW_RESERVED_BYTES
@@ -1887,11 +1912,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+ENTRY nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/arm64ng/object.S b/runtime/interpreter/mterp/arm64ng/object.S
index df044d9..b843fcd 100644
--- a/runtime/interpreter/mterp/arm64ng/object.S
+++ b/runtime/interpreter/mterp/arm64ng/object.S
@@ -19,7 +19,7 @@
mov x0, xSELF
ldr x1, [sp]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov x1, x0
b 1b
@@ -90,7 +90,7 @@
mov x0, xSELF
ldr x1, [sp]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov x1, x0
b 1b
@@ -476,7 +476,7 @@
mov x0, xSELF
ldr x1, [sp]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_allocate_object
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/arm64ng/other.S b/runtime/interpreter/mterp/arm64ng/other.S
index 1feafd5..3470ee8 100644
--- a/runtime/interpreter/mterp/arm64ng/other.S
+++ b/runtime/interpreter/mterp/arm64ng/other.S
@@ -66,7 +66,7 @@
b 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/mterp/armng/array.S b/runtime/interpreter/mterp/armng/array.S
index 4ab418c..49c4e38 100644
--- a/runtime/interpreter/mterp/armng/array.S
+++ b/runtime/interpreter/mterp/armng/array.S
@@ -186,7 +186,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/armng/main.S b/runtime/interpreter/mterp/armng/main.S
index 310a3fd..9b739b0 100644
--- a/runtime/interpreter/mterp/armng/main.S
+++ b/runtime/interpreter/mterp/armng/main.S
@@ -284,7 +284,10 @@
bl \helper
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ ldr ip, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field.
+ cmp ip, #0
+ bne nterp_deliver_pending_exception
+ bx lr
END \name
.endm
@@ -1608,6 +1611,28 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ ldr r4, [r0, ART_METHOD_DECLARING_CLASS_OFFSET]
+ ldrb ip, [r4, MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET]
+ cmp ip, #MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE
+ bcs ExecuteNterpImpl
+ cmp ip, #MIRROR_CLASS_IS_INITIALIZED_VALUE
+ blo .Linitializing_check
+ dmb ish
+ b ExecuteNterpImpl
+.Linitializing_check:
+ cmp ip, #MIRROR_CLASS_IS_INITIALIZING_VALUE
+ blo art_quick_resolution_trampoline
+ ldr r4, [r4, #MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET]
+ ldr ip, [rSELF, #THREAD_TID_OFFSET]
+ cmp r4, ip
+ beq ExecuteNterpImpl
+ b art_quick_resolution_trampoline
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
sub ip, sp, #STACK_OVERFLOW_RESERVED_BYTES
@@ -1969,11 +1994,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+ENTRY nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/armng/object.S b/runtime/interpreter/mterp/armng/object.S
index 7deffaf..984565c 100644
--- a/runtime/interpreter/mterp/armng/object.S
+++ b/runtime/interpreter/mterp/armng/object.S
@@ -20,7 +20,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov r1, r0
b 1b
@@ -91,7 +91,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov r1, r0
b 1b
@@ -523,7 +523,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_allocate_object
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/armng/other.S b/runtime/interpreter/mterp/armng/other.S
index 3376808..7dfed62 100644
--- a/runtime/interpreter/mterp/armng/other.S
+++ b/runtime/interpreter/mterp/armng/other.S
@@ -67,7 +67,7 @@
b 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index 7526ea5..a4e64e3 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -26,7 +26,6 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "interpreter/interpreter_cache-inl.h"
#include "interpreter/interpreter_common.h"
-#include "interpreter/interpreter_intrinsics.h"
#include "interpreter/shadow_frame-inl.h"
#include "mirror/string-alloc-inl.h"
#include "nterp_helpers.h"
@@ -45,9 +44,11 @@
// parts of the runtime (like instrumentation) make assumption on an
// interpreter-only runtime to always be in a switch-like interpreter.
return IsNterpSupported() &&
+ !runtime->IsJavaDebuggable() &&
+ !instr->EntryExitStubsInstalled() &&
!instr->InterpretOnly() &&
!runtime->IsAotCompiler() &&
- !runtime->GetInstrumentation()->NeedsSlowInterpreterForListeners() &&
+ !instr->NeedsSlowInterpreterForListeners() &&
// An async exception has been thrown. We need to go to the switch interpreter. nterp doesn't
// know how to deal with these so we could end up never dealing with it if we are in an
// infinite loop.
@@ -58,10 +59,17 @@
// The entrypoint for nterp, which ArtMethods can directly point to.
extern "C" void ExecuteNterpImpl() REQUIRES_SHARED(Locks::mutator_lock_);
+// Another entrypoint, which does a clinit check at entry.
+extern "C" void ExecuteNterpWithClinitImpl() REQUIRES_SHARED(Locks::mutator_lock_);
+
const void* GetNterpEntryPoint() {
return reinterpret_cast<const void*>(interpreter::ExecuteNterpImpl);
}
+const void* GetNterpWithClinitEntryPoint() {
+ return reinterpret_cast<const void*>(interpreter::ExecuteNterpWithClinitImpl);
+}
+
/*
* Verify some constants used by the nterp interpreter.
*/
@@ -89,12 +97,12 @@
}
template<typename T>
-inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T value) {
+inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T value) {
self->GetInterpreterCache()->Set(self, dex_pc_ptr, value);
}
template<typename T>
-inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T* value) {
+inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T* value) {
UpdateCache(self, dex_pc_ptr, reinterpret_cast<size_t>(value));
}
@@ -243,76 +251,57 @@
return dex_file->GetShorty(proto_idx);
}
+static constexpr uint8_t kInvalidInvokeType = 255u;
+static_assert(static_cast<uint8_t>(kMaxInvokeType) < kInvalidInvokeType);
+
+static constexpr uint8_t GetOpcodeInvokeType(uint8_t opcode) {
+ switch (opcode) {
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ return static_cast<uint8_t>(kDirect);
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ return static_cast<uint8_t>(kInterface);
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ return static_cast<uint8_t>(kStatic);
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_SUPER_RANGE:
+ return static_cast<uint8_t>(kSuper);
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ return static_cast<uint8_t>(kVirtual);
+
+ default:
+ return kInvalidInvokeType;
+ }
+}
+
+static constexpr std::array<uint8_t, 256u> GenerateOpcodeInvokeTypes() {
+ std::array<uint8_t, 256u> opcode_invoke_types{};
+ for (size_t opcode = 0u; opcode != opcode_invoke_types.size(); ++opcode) {
+ opcode_invoke_types[opcode] = GetOpcodeInvokeType(opcode);
+ }
+ return opcode_invoke_types;
+}
+
+static constexpr std::array<uint8_t, 256u> kOpcodeInvokeTypes = GenerateOpcodeInvokeTypes();
+
FLATTEN
-extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
+extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
- InvokeType invoke_type = kStatic;
- uint16_t method_index = 0;
- switch (inst->Opcode()) {
- case Instruction::INVOKE_DIRECT: {
- method_index = inst->VRegB_35c();
- invoke_type = kDirect;
- break;
- }
+ Instruction::Code opcode = inst->Opcode();
+ DCHECK(IsUint<8>(static_cast<std::underlying_type_t<Instruction::Code>>(opcode)));
+ uint8_t raw_invoke_type = kOpcodeInvokeTypes[opcode];
+ DCHECK_LE(raw_invoke_type, kMaxInvokeType);
+ InvokeType invoke_type = static_cast<InvokeType>(raw_invoke_type);
- case Instruction::INVOKE_INTERFACE: {
- method_index = inst->VRegB_35c();
- invoke_type = kInterface;
- break;
- }
-
- case Instruction::INVOKE_STATIC: {
- method_index = inst->VRegB_35c();
- invoke_type = kStatic;
- break;
- }
-
- case Instruction::INVOKE_SUPER: {
- method_index = inst->VRegB_35c();
- invoke_type = kSuper;
- break;
- }
- case Instruction::INVOKE_VIRTUAL: {
- method_index = inst->VRegB_35c();
- invoke_type = kVirtual;
- break;
- }
-
- case Instruction::INVOKE_DIRECT_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kDirect;
- break;
- }
-
- case Instruction::INVOKE_INTERFACE_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kInterface;
- break;
- }
-
- case Instruction::INVOKE_STATIC_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kStatic;
- break;
- }
-
- case Instruction::INVOKE_SUPER_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kSuper;
- break;
- }
-
- case Instruction::INVOKE_VIRTUAL_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kVirtual;
- break;
- }
-
- default:
- LOG(FATAL) << "Unknown instruction " << inst->Opcode();
- }
+ // In release mode, this is just a simple load.
+ // In debug mode, this checks that we're using the correct instruction format.
+ uint16_t method_index =
+ (opcode >= Instruction::INVOKE_VIRTUAL_RANGE) ? inst->VRegB_3rc() : inst->VRegB_35c();
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
ArtMethod* resolved_method = caller->SkipAccessChecks()
@@ -354,9 +343,7 @@
}
UpdateCache(self, dex_pc_ptr, result);
return result;
- } else if (resolved_method->GetDeclaringClass()->IsStringClass()
- && !resolved_method->IsStatic()
- && resolved_method->IsConstructor()) {
+ } else if (resolved_method->IsStringConstructor()) {
CHECK_NE(invoke_type, kSuper);
resolved_method = WellKnownClasses::StringInitToStringFactory(resolved_method);
// Or the result with 1 to notify to nterp this is a string init method. We
@@ -373,71 +360,23 @@
}
}
-FLATTEN
-static ArtField* ResolveFieldWithAccessChecks(Thread* self,
- ClassLinker* class_linker,
- uint16_t field_index,
- ArtMethod* caller,
- bool is_static,
- bool is_put,
- size_t resolve_field_type) // Resolve if not zero
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (caller->SkipAccessChecks()) {
- return class_linker->ResolveField(field_index, caller, is_static);
- }
-
- caller = caller->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(caller->GetDexCache()));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(caller->GetClassLoader()));
-
- ArtField* resolved_field = class_linker->ResolveFieldJLS(field_index,
- h_dex_cache,
- h_class_loader);
- if (resolved_field == nullptr) {
- return nullptr;
- }
-
- ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, caller);
- return nullptr;
- }
- ObjPtr<mirror::Class> referring_class = caller->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
- resolved_field,
- caller->GetDexCache(),
- field_index))) {
- return nullptr;
- }
- if (UNLIKELY(is_put && resolved_field->IsFinal() && (fields_class != referring_class))) {
- ThrowIllegalAccessErrorFinalField(caller, resolved_field);
- return nullptr;
- }
- if (resolve_field_type != 0u && resolved_field->ResolveType() == nullptr) {
- DCHECK(self->IsExceptionPending());
- return nullptr;
- }
- return resolved_field;
-}
-
extern "C" size_t NterpGetStaticField(Thread* self,
ArtMethod* caller,
- uint16_t* dex_pc_ptr,
+ const uint16_t* dex_pc_ptr,
size_t resolve_field_type) // Resolve if not zero
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
uint16_t field_index = inst->VRegB_21c();
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Instruction::Code opcode = inst->Opcode();
ArtField* resolved_field = ResolveFieldWithAccessChecks(
self,
class_linker,
field_index,
caller,
- /* is_static */ true,
- /* is_put */ IsInstructionSPut(inst->Opcode()),
+ /*is_static=*/ true,
+ /*is_put=*/ IsInstructionSPut(opcode),
resolve_field_type);
if (resolved_field == nullptr) {
@@ -460,27 +399,39 @@
// check for it.
return reinterpret_cast<size_t>(resolved_field) | 1;
} else {
- UpdateCache(self, dex_pc_ptr, resolved_field);
+ // For sput-object, try to resolve the field type even if we were not requested to.
+ // Only if the field type is successfully resolved can we update the cache. If we
+ // fail to resolve the type, we clear the exception to keep interpreter
+ // semantics of not throwing when null is stored.
+ if (opcode == Instruction::SPUT_OBJECT &&
+ resolve_field_type == 0 &&
+ resolved_field->ResolveType() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ } else {
+ UpdateCache(self, dex_pc_ptr, resolved_field);
+ }
return reinterpret_cast<size_t>(resolved_field);
}
}
extern "C" uint32_t NterpGetInstanceFieldOffset(Thread* self,
ArtMethod* caller,
- uint16_t* dex_pc_ptr,
+ const uint16_t* dex_pc_ptr,
size_t resolve_field_type) // Resolve if not zero
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
uint16_t field_index = inst->VRegC_22c();
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Instruction::Code opcode = inst->Opcode();
ArtField* resolved_field = ResolveFieldWithAccessChecks(
self,
class_linker,
field_index,
caller,
- /* is_static */ false,
- /* is_put */ IsInstructionIPut(inst->Opcode()),
+ /*is_static=*/ false,
+ /*is_put=*/ IsInstructionIPut(opcode),
resolve_field_type);
if (resolved_field == nullptr) {
DCHECK(self->IsExceptionPending());
@@ -491,67 +442,86 @@
// of volatile.
return -resolved_field->GetOffset().Uint32Value();
}
- UpdateCache(self, dex_pc_ptr, resolved_field->GetOffset().Uint32Value());
+ // For iput-object, try to resolve the field type even if we were not requested to.
+ // Only if the field type is successfully resolved can we update the cache. If we
+ // fail to resolve the type, we clear the exception to keep interpreter
+ // semantics of not throwing when null is stored.
+ if (opcode == Instruction::IPUT_OBJECT &&
+ resolve_field_type == 0 &&
+ resolved_field->ResolveType() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ } else {
+ UpdateCache(self, dex_pc_ptr, resolved_field->GetOffset().Uint32Value());
+ }
return resolved_field->GetOffset().Uint32Value();
}
-extern "C" mirror::Object* NterpGetClassOrAllocateObject(Thread* self,
- ArtMethod* caller,
- uint16_t* dex_pc_ptr)
+extern "C" mirror::Object* NterpGetClass(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
- dex::TypeIndex index;
- switch (inst->Opcode()) {
- case Instruction::NEW_INSTANCE:
- index = dex::TypeIndex(inst->VRegB_21c());
- break;
- case Instruction::CHECK_CAST:
- index = dex::TypeIndex(inst->VRegB_21c());
- break;
- case Instruction::INSTANCE_OF:
- index = dex::TypeIndex(inst->VRegC_22c());
- break;
- case Instruction::CONST_CLASS:
- index = dex::TypeIndex(inst->VRegB_21c());
- break;
- case Instruction::NEW_ARRAY:
- index = dex::TypeIndex(inst->VRegC_22c());
- break;
- default:
- LOG(FATAL) << "Unreachable";
- }
+ Instruction::Code opcode = inst->Opcode();
+ DCHECK(opcode == Instruction::CHECK_CAST ||
+ opcode == Instruction::INSTANCE_OF ||
+ opcode == Instruction::CONST_CLASS ||
+ opcode == Instruction::NEW_ARRAY);
+
+ // In release mode, this is just a simple load.
+ // In debug mode, this checks that we're using the correct instruction format.
+ dex::TypeIndex index = dex::TypeIndex(
+ (opcode == Instruction::CHECK_CAST || opcode == Instruction::CONST_CLASS)
+ ? inst->VRegB_21c()
+ : inst->VRegC_22c());
+
ObjPtr<mirror::Class> c =
ResolveVerifyAndClinit(index,
caller,
self,
/* can_run_clinit= */ false,
/* verify_access= */ !caller->SkipAccessChecks());
- if (c == nullptr) {
+ if (UNLIKELY(c == nullptr)) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
- if (inst->Opcode() == Instruction::NEW_INSTANCE) {
- gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
- if (UNLIKELY(c->IsStringClass())) {
- // We don't cache the class for strings as we need to special case their
- // allocation.
- return mirror::String::AllocEmptyString(self, allocator_type).Ptr();
- } else {
- if (!c->IsFinalizable() && c->IsInstantiable()) {
- // Cache non-finalizable classes for next calls.
- UpdateCache(self, dex_pc_ptr, c.Ptr());
- }
- return AllocObjectFromCode(c, self, allocator_type).Ptr();
- }
- } else {
- // For all other cases, cache the class.
- UpdateCache(self, dex_pc_ptr, c.Ptr());
- }
+ UpdateCache(self, dex_pc_ptr, c.Ptr());
return c.Ptr();
}
+extern "C" mirror::Object* NterpAllocateObject(Thread* self,
+ ArtMethod* caller,
+ uint16_t* dex_pc_ptr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ UpdateHotness(caller);
+ const Instruction* inst = Instruction::At(dex_pc_ptr);
+ DCHECK_EQ(inst->Opcode(), Instruction::NEW_INSTANCE);
+ dex::TypeIndex index = dex::TypeIndex(inst->VRegB_21c());
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(index,
+ caller,
+ self,
+ /* can_run_clinit= */ false,
+ /* verify_access= */ !caller->SkipAccessChecks());
+ if (UNLIKELY(c == nullptr)) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ if (UNLIKELY(c->IsStringClass())) {
+ // We don't cache the class for strings as we need to special case their
+ // allocation.
+ return mirror::String::AllocEmptyString(self, allocator_type).Ptr();
+ } else {
+ if (!c->IsFinalizable() && c->IsInstantiable()) {
+ // Cache non-finalizable classes for next calls.
+ UpdateCache(self, dex_pc_ptr, c.Ptr());
+ }
+ return AllocObjectFromCode(c, self, allocator_type).Ptr();
+ }
+}
+
extern "C" mirror::Object* NterpLoadObject(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
const Instruction* inst = Instruction::At(dex_pc_ptr);
diff --git a/runtime/interpreter/mterp/nterp.h b/runtime/interpreter/mterp/nterp.h
index 1590b28..4d5af39 100644
--- a/runtime/interpreter/mterp/nterp.h
+++ b/runtime/interpreter/mterp/nterp.h
@@ -32,6 +32,7 @@
bool IsNterpSupported();
bool CanRuntimeUseNterp();
const void* GetNterpEntryPoint();
+const void* GetNterpWithClinitEntryPoint();
constexpr uint16_t kNterpHotnessValue = 0;
diff --git a/runtime/interpreter/mterp/x86_64ng/main.S b/runtime/interpreter/mterp/x86_64ng/main.S
index bd191c0..a1890a1 100644
--- a/runtime/interpreter/mterp/x86_64ng/main.S
+++ b/runtime/interpreter/mterp/x86_64ng/main.S
@@ -237,7 +237,9 @@
SETUP_SAVE_REFS_ONLY_FRAME
call \helper
RESTORE_SAVE_REFS_ONLY_FRAME
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ cmpq LITERAL(0), %gs:THREAD_EXCEPTION_OFFSET
+ jne nterp_deliver_pending_exception
+ ret
END_FUNCTION \name
.endm
@@ -1694,6 +1696,21 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ movl ART_METHOD_DECLARING_CLASS_OFFSET(%rdi), %r10d
+ cmpb $$(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%r10d)
+ jae ExecuteNterpImpl
+ cmpb $$(MIRROR_CLASS_IS_INITIALIZING_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%r10d)
+ jb art_quick_resolution_trampoline
+ movl MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET(%r10d), %r10d
+ cmpl %r10d, rSELF:THREAD_TID_OFFSET
+ je ExecuteNterpImpl
+ jmp art_quick_resolution_trampoline
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
.cfi_def_cfa rsp, 8
@@ -1923,7 +1940,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_allocate_object
jmp 1b
3:
// 07 is %rdi
@@ -1949,7 +1966,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movq %rax, %rdi
jmp 1b
3:
@@ -2295,11 +2312,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+DEFINE_FUNCTION nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END_FUNCTION nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/x86_64ng/object.S b/runtime/interpreter/mterp/x86_64ng/object.S
index 140ea75..21a6e67 100644
--- a/runtime/interpreter/mterp/x86_64ng/object.S
+++ b/runtime/interpreter/mterp/x86_64ng/object.S
@@ -16,7 +16,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movq %rax, %rsi
jmp 1b
@@ -149,7 +149,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movq %rax, %rsi
jmp .L${opcode}_start
diff --git a/runtime/interpreter/mterp/x86_64ng/other.S b/runtime/interpreter/mterp/x86_64ng/other.S
index a72ee58..f789086 100644
--- a/runtime/interpreter/mterp/x86_64ng/other.S
+++ b/runtime/interpreter/mterp/x86_64ng/other.S
@@ -53,7 +53,7 @@
jmp 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/mterp/x86ng/main.S b/runtime/interpreter/mterp/x86ng/main.S
index db8519b..5b0edd4 100644
--- a/runtime/interpreter/mterp/x86ng/main.S
+++ b/runtime/interpreter/mterp/x86ng/main.S
@@ -275,7 +275,9 @@
RESTORE_IBASE
FETCH_INST_CLEAR_OPCODE
RESTORE_SAVE_REFS_ONLY_FRAME
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ cmpl LITERAL(0), %fs:THREAD_EXCEPTION_OFFSET
+ jne nterp_deliver_pending_exception
+ ret
END_FUNCTION \name
.endm
@@ -1757,6 +1759,27 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ push %esi
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ movl ART_METHOD_DECLARING_CLASS_OFFSET(%eax), %esi
+ cmpb $$(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%esi)
+ jae .Lcontinue_execute_nterp
+ cmpb $$(MIRROR_CLASS_IS_INITIALIZING_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%esi)
+ jb .Linvoke_trampoline
+ movl MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET(%esi), %esi
+ cmpl %esi, rSELF:THREAD_TID_OFFSET
+ je .Lcontinue_execute_nterp
+.Linvoke_trampoline:
+ pop %esi
+ jmp art_quick_resolution_trampoline
+.Lcontinue_execute_nterp:
+ pop %esi
+ jmp ExecuteNterpImpl
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
.cfi_def_cfa esp, 4
@@ -1980,7 +2003,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_allocate_object
jmp 1b
3:
// 00 is %eax
@@ -2008,7 +2031,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
jmp 1b
3:
// 00 is %eax
@@ -2339,11 +2362,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+DEFINE_FUNCTION nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END_FUNCTION nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/x86ng/object.S b/runtime/interpreter/mterp/x86ng/object.S
index 1d11e10..39091ce 100644
--- a/runtime/interpreter/mterp/x86ng/object.S
+++ b/runtime/interpreter/mterp/x86ng/object.S
@@ -16,7 +16,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movl %eax, %ecx
jmp 1b
@@ -58,7 +58,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movl %eax, %ecx
jmp 1b
diff --git a/runtime/interpreter/mterp/x86ng/other.S b/runtime/interpreter/mterp/x86ng/other.S
index 4cf982c..6dd1ce3 100644
--- a/runtime/interpreter/mterp/x86ng/other.S
+++ b/runtime/interpreter/mterp/x86ng/other.S
@@ -53,7 +53,7 @@
jmp 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 8cb2b33..7ca2423 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -54,7 +54,7 @@
// We have been requested to notify when this frame gets popped.
kNotifyFramePop = 1 << 0,
// We have been asked to pop this frame off the stack as soon as possible.
- kForcePopFrame = 1 << 1,
+ kForcePopFrame = 1 << 1,
// We have been asked to re-execute the last instruction.
kForceRetryInst = 1 << 2,
// Mark that we expect the next frame to retry the last instruction (used by instrumentation and
@@ -62,6 +62,9 @@
kSkipMethodExitEvents = 1 << 3,
// Used to suppress exception events caused by other instrumentation events.
kSkipNextExceptionEvent = 1 << 4,
+ // Used to specify if DexPCMoveEvents have to be reported. These events will
+ // only be reported if the method has a breakpoint set.
+ kNotifyDexPcMoveEvents = 1 << 5,
};
public:
@@ -72,10 +75,11 @@
}
// Create ShadowFrame in heap for deoptimization.
- static ShadowFrame* CreateDeoptimizedFrame(uint32_t num_vregs, ShadowFrame* link,
- ArtMethod* method, uint32_t dex_pc) {
+ static ShadowFrame* CreateDeoptimizedFrame(uint32_t num_vregs,
+ ArtMethod* method,
+ uint32_t dex_pc) {
uint8_t* memory = new uint8_t[ComputeSize(num_vregs)];
- return CreateShadowFrameImpl(num_vregs, link, method, dex_pc, memory);
+ return CreateShadowFrameImpl(num_vregs, method, dex_pc, memory);
}
// Delete a ShadowFrame allocated on the heap for deoptimization.
@@ -87,12 +91,11 @@
// Create a shadow frame in a fresh alloca. This needs to be in the context of the caller.
// Inlining doesn't work, the compiler will still undo the alloca. So this needs to be a macro.
-#define CREATE_SHADOW_FRAME(num_vregs, link, method, dex_pc) ({ \
+#define CREATE_SHADOW_FRAME(num_vregs, method, dex_pc) ({ \
size_t frame_size = ShadowFrame::ComputeSize(num_vregs); \
void* alloca_mem = alloca(frame_size); \
ShadowFrameAllocaUniquePtr( \
- ShadowFrame::CreateShadowFrameImpl((num_vregs), (link), (method), (dex_pc), \
- (alloca_mem))); \
+ ShadowFrame::CreateShadowFrameImpl((num_vregs), (method), (dex_pc), (alloca_mem))); \
})
~ShadowFrame() {}
@@ -132,9 +135,14 @@
void SetLink(ShadowFrame* frame) {
DCHECK_NE(this, frame);
+ DCHECK_EQ(link_, nullptr);
link_ = frame;
}
+ void ClearLink() {
+ link_ = nullptr;
+ }
+
int32_t GetVReg(size_t i) const {
DCHECK_LT(i, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
@@ -169,14 +177,14 @@
int64_t GetVRegLong(size_t i) const {
DCHECK_LT(i + 1, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
- typedef const int64_t unaligned_int64 __attribute__ ((aligned (4)));
+ using unaligned_int64 __attribute__((aligned(4))) = const int64_t;
return *reinterpret_cast<unaligned_int64*>(vreg);
}
double GetVRegDouble(size_t i) const {
DCHECK_LT(i + 1, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
- typedef const double unaligned_double __attribute__ ((aligned (4)));
+ using unaligned_double __attribute__((aligned(4))) = const double;
return *reinterpret_cast<unaligned_double*>(vreg);
}
@@ -221,7 +229,7 @@
void SetVRegLong(size_t i, int64_t val) {
DCHECK_LT(i + 1, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
- typedef int64_t unaligned_int64 __attribute__ ((aligned (4)));
+ using unaligned_int64 __attribute__((aligned(4))) = int64_t;
*reinterpret_cast<unaligned_int64*>(vreg) = val;
// This is needed for moving collectors since these can update the vreg references if they
// happen to agree with references in the reference array.
@@ -232,7 +240,7 @@
void SetVRegDouble(size_t i, double val) {
DCHECK_LT(i + 1, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
- typedef double unaligned_double __attribute__ ((aligned (4)));
+ using unaligned_double __attribute__((aligned(4))) = double;
*reinterpret_cast<unaligned_double*>(vreg) = val;
// This is needed for moving collectors since these can update the vreg references if they
// happen to agree with references in the reference array.
@@ -314,11 +322,10 @@
// Create ShadowFrame for interpreter using provided memory.
static ShadowFrame* CreateShadowFrameImpl(uint32_t num_vregs,
- ShadowFrame* link,
ArtMethod* method,
uint32_t dex_pc,
void* memory) {
- return new (memory) ShadowFrame(num_vregs, link, method, dex_pc);
+ return new (memory) ShadowFrame(num_vregs, method, dex_pc);
}
const uint16_t* GetDexPCPtr() {
@@ -373,6 +380,14 @@
UpdateFrameFlag(enable, FrameFlags::kSkipNextExceptionEvent);
}
+ bool GetNotifyDexPcMoveEvents() const {
+ return GetFrameFlag(FrameFlags::kNotifyDexPcMoveEvents);
+ }
+
+ void SetNotifyDexPcMoveEvents(bool enable) {
+ UpdateFrameFlag(enable, FrameFlags::kNotifyDexPcMoveEvents);
+ }
+
void CheckConsistentVRegs() const {
if (kIsDebugBuild) {
// A shadow frame visible to GC requires the following rule: for a given vreg,
@@ -385,8 +400,8 @@
}
private:
- ShadowFrame(uint32_t num_vregs, ShadowFrame* link, ArtMethod* method, uint32_t dex_pc)
- : link_(link),
+ ShadowFrame(uint32_t num_vregs, ArtMethod* method, uint32_t dex_pc)
+ : link_(nullptr),
method_(method),
result_register_(nullptr),
dex_pc_ptr_(nullptr),
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 73058e0..cafd7c4 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -47,6 +47,7 @@
#include "mirror/array-alloc-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class-alloc-inl.h"
+#include "mirror/class.h"
#include "mirror/executable-inl.h"
#include "mirror/field.h"
#include "mirror/method.h"
@@ -61,7 +62,7 @@
#include "thread-inl.h"
#include "transaction.h"
#include "unstarted_runtime_list.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
namespace interpreter {
@@ -138,6 +139,10 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ObjPtr<mirror::Class> found = class_linker->FindClass(self, descriptor.c_str(), class_loader);
+ if (found != nullptr && !found->CheckIsVisibleWithTargetSdk(self)) {
+ CHECK(self->IsExceptionPending());
+ return;
+ }
if (found != nullptr && initialize_class) {
StackHandleScope<1> hs(self);
HandleWrapperObjPtr<mirror::Class> h_class = hs.NewHandleWrapper(&found);
@@ -231,8 +236,7 @@
class_loader = nullptr;
}
- ScopedObjectAccessUnchecked soa(self);
- if (class_loader != nullptr && !ClassLinker::IsBootClassLoader(soa, class_loader)) {
+ if (class_loader != nullptr && !ClassLinker::IsBootClassLoader(class_loader)) {
AbortTransactionOrFail(self,
"Only the boot classloader is supported: %s",
mirror::Object::PrettyTypeOf(class_loader).c_str());
@@ -659,8 +663,7 @@
StackHandleScope<1> hs(self);
Handle<mirror::Class> this_classloader_class(hs.NewHandle(this_obj->GetClass()));
- if (self->DecodeJObject(WellKnownClasses::java_lang_BootClassLoader) !=
- this_classloader_class.Get()) {
+ if (WellKnownClasses::java_lang_BootClassLoader != this_classloader_class.Get()) {
AbortTransactionOrFail(self,
"Unsupported classloader type %s for getResourceAsStream",
mirror::Class::PrettyClass(this_classloader_class.Get()).c_str());
@@ -1113,18 +1116,14 @@
// thread as unstarted to the ThreadGroup. A faked-up main thread peer is good enough for
// these purposes.
Runtime::Current()->InitThreadGroups(self);
- jobject main_peer =
- self->CreateCompileTimePeer(self->GetJniEnv(),
- "main",
- false,
- Runtime::Current()->GetMainThreadGroup());
+ ObjPtr<mirror::Object> main_peer = self->CreateCompileTimePeer(
+ "main", /*as_daemon=*/ false, Runtime::Current()->GetMainThreadGroup());
if (main_peer == nullptr) {
AbortTransactionOrFail(self, "Failed allocating peer");
return;
}
- result->SetL(self->DecodeJObject(main_peer));
- self->GetJniEnv()->DeleteLocalRef(main_peer);
+ result->SetL(main_peer);
} else {
AbortTransactionOrFail(self,
"Thread.currentThread() does not support %s",
@@ -1367,6 +1366,22 @@
}
// This allows creating the new style of String objects during compilation.
+void UnstartedRuntime::UnstartedStringFactoryNewStringFromBytes(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ jint high = shadow_frame->GetVReg(arg_offset + 1);
+ jint offset = shadow_frame->GetVReg(arg_offset + 2);
+ jint byte_count = shadow_frame->GetVReg(arg_offset + 3);
+ DCHECK_GE(byte_count, 0);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ByteArray> h_byte_array(
+ hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsByteArray()));
+ Runtime* runtime = Runtime::Current();
+ gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
+ result->SetL(
+ mirror::String::AllocFromByteArray(self, byte_count, h_byte_array, offset, high, allocator));
+}
+
+// This allows creating the new style of String objects during compilation.
void UnstartedRuntime::UnstartedStringFactoryNewStringFromChars(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
jint offset = shadow_frame->GetVReg(arg_offset);
@@ -1921,6 +1936,30 @@
result->SetI(receiver->AsString()->CompareTo(rhs->AsString()));
}
+void UnstartedRuntime::UnstartedJNIStringFillBytesLatin1(
+ Thread* self, ArtMethod* method ATTRIBUTE_UNUSED,
+ mirror::Object* receiver, uint32_t* args, JValue* ATTRIBUTE_UNUSED) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::String> h_receiver(hs.NewHandle(
+ reinterpret_cast<mirror::String*>(receiver)->AsString()));
+ Handle<mirror::ByteArray> h_buffer(hs.NewHandle(
+ reinterpret_cast<mirror::ByteArray*>(args[0])->AsByteArray()));
+ int32_t index = static_cast<int32_t>(args[1]);
+ h_receiver->FillBytesLatin1(h_buffer, index);
+}
+
+void UnstartedRuntime::UnstartedJNIStringFillBytesUTF16(
+ Thread* self, ArtMethod* method ATTRIBUTE_UNUSED,
+ mirror::Object* receiver, uint32_t* args, JValue* ATTRIBUTE_UNUSED) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::String> h_receiver(hs.NewHandle(
+ reinterpret_cast<mirror::String*>(receiver)->AsString()));
+ Handle<mirror::ByteArray> h_buffer(hs.NewHandle(
+ reinterpret_cast<mirror::ByteArray*>(args[0])->AsByteArray()));
+ int32_t index = static_cast<int32_t>(args[1]);
+ h_receiver->FillBytesUTF16(h_buffer, index);
+}
+
void UnstartedRuntime::UnstartedJNIStringIntern(
Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED, JValue* result) {
@@ -2163,6 +2202,7 @@
uint32_t* args,
JValue* result);
+// NOLINTNEXTLINE
#define ONE_PLUS(ShortNameIgnored, DescriptorIgnored, NameIgnored, SignatureIgnored) 1 +
static constexpr size_t kInvokeHandlersSize = UNSTARTED_RUNTIME_DIRECT_LIST(ONE_PLUS) 0;
static constexpr size_t kJniHandlersSize = UNSTARTED_RUNTIME_JNI_LIST(ONE_PLUS) 0;
@@ -2262,6 +2302,9 @@
const auto& iter = invoke_handlers_.find(shadow_frame->GetMethod());
if (iter != invoke_handlers_.end()) {
+ // Note: When we special case the method, we do not ensure initialization.
+ // This has been the behavior since implementation of this feature.
+
// Clear out the result in case it's not zeroed out.
result->SetL(nullptr);
@@ -2272,6 +2315,9 @@
self->PopShadowFrame();
} else {
+ if (!EnsureInitialized(self, shadow_frame)) {
+ return;
+ }
// Not special, continue with regular interpreter execution.
ArtInterpreterToInterpreterBridge(self, accessor, shadow_frame, result);
}
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 5f8add0..dd2028a 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -67,6 +67,7 @@
V(StringGetCharsNoCheck, "Ljava/lang/String;", "getCharsNoCheck", "(II[CI)V") \
V(StringCharAt, "Ljava/lang/String;", "charAt", "(I)C") \
V(StringDoReplace, "Ljava/lang/String;", "doReplace", "(CC)Ljava/lang/String;") \
+ V(StringFactoryNewStringFromBytes, "Ljava/lang/StringFactory;", "newStringFromBytes", "([BIII)Ljava/lang/String;") \
V(StringFactoryNewStringFromChars, "Ljava/lang/StringFactory;", "newStringFromChars", "(II[C)Ljava/lang/String;") \
V(StringFactoryNewStringFromString, "Ljava/lang/StringFactory;", "newStringFromString", "(Ljava/lang/String;)Ljava/lang/String;") \
V(StringFastSubstring, "Ljava/lang/String;", "fastSubstring", "(II)Ljava/lang/String;") \
@@ -105,6 +106,8 @@
V(ObjectInternalClone, "Ljava/lang/Object;", "internalClone", "()Ljava/lang/Object;") \
V(ObjectNotifyAll, "Ljava/lang/Object;", "notifyAll", "()V") \
V(StringCompareTo, "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I") \
+ V(StringFillBytesLatin1, "Ljava/lang/String;", "fillBytesLatin1", "([BI)V") \
+ V(StringFillBytesUTF16, "Ljava/lang/String;", "fillBytesUTF16", "([BI)V") \
V(StringIntern, "Ljava/lang/String;", "intern", "()Ljava/lang/String;") \
V(ArrayCreateMultiArray, "Ljava/lang/reflect/Array;", "createMultiArray", "(Ljava/lang/Class;[I)Ljava/lang/Object;") \
V(ArrayCreateObjectArray, "Ljava/lang/reflect/Array;", "createObjectArray", "(Ljava/lang/Class;I)Ljava/lang/Object;") \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 75a692e..3227ef7 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -62,11 +62,6 @@
class UnstartedRuntimeTest : public CommonRuntimeTest {
protected:
- void SetUp() override {
- CommonRuntimeTest::SetUp();
- InitializeIntrinsics();
- }
-
// Re-expose all UnstartedRuntime implementations so we don't need to declare a million
// test friends.
@@ -96,11 +91,10 @@
#undef UNSTARTED_JNI
UniqueDeoptShadowFramePtr CreateShadowFrame(uint32_t num_vregs,
- ShadowFrame* link,
ArtMethod* method,
uint32_t dex_pc) {
return UniqueDeoptShadowFramePtr(
- ShadowFrame::CreateDeoptimizedFrame(num_vregs, link, method, dex_pc));
+ ShadowFrame::CreateDeoptimizedFrame(num_vregs, method, dex_pc));
}
// Helpers for ArrayCopy.
@@ -237,7 +231,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
for (int32_t i = 0; i < kBaseLen; ++i) {
tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
@@ -257,7 +251,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
int32_t adjusted_length = kBaseLen - sizeof(int16_t);
for (int32_t i = 0; i < adjusted_length; ++i) {
@@ -280,7 +274,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
int32_t adjusted_length = kBaseLen - sizeof(int32_t);
for (int32_t i = 0; i < adjusted_length; ++i) {
@@ -303,7 +297,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
int32_t adjusted_length = kBaseLen - sizeof(int64_t);
for (int32_t i = 0; i < adjusted_length; ++i) {
@@ -333,7 +327,7 @@
uint16_t buf[kBaseLen];
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
for (int32_t start_index = 0; start_index < kBaseLen; ++start_index) {
for (int32_t count = 0; count <= kBaseLen; ++count) {
@@ -385,7 +379,7 @@
ObjPtr<mirror::String> test_string = mirror::String::AllocFromModifiedUtf8(self, base_string);
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
for (int32_t i = 0; i < base_len; ++i) {
tmp->SetVRegReference(0, test_string);
@@ -410,7 +404,7 @@
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, method, 0);
const char* base_string = "hello_world";
StackHandleScope<2> hs(self);
Handle<mirror::String> string_arg =
@@ -420,12 +414,14 @@
shadow_frame->SetVRegReference(0, reference_empty_string.Get());
shadow_frame->SetVRegReference(1, string_arg.Get());
- interpreter::DoCall<false, false>(method,
- self,
- *shadow_frame,
- Instruction::At(inst_data),
- inst_data[0],
- &result);
+ ArtMethod* factory = WellKnownClasses::StringInitToStringFactory(method);
+ interpreter::DoCall<false>(factory,
+ self,
+ *shadow_frame,
+ Instruction::At(inst_data),
+ inst_data[0],
+ /* string_init= */ true,
+ &result);
ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
EXPECT_EQ(string_arg->GetLength(), string_result->GetLength());
@@ -453,7 +449,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Note: all tests are not GC safe. Assume there's no GC running here with the few objects we
// allocate.
@@ -485,7 +481,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
StackHandleScope<1> hs_object(self);
Handle<mirror::Class> object_class(hs_object.NewHandle(GetClassRoot<mirror::Object>()));
@@ -588,7 +584,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
// suffixes).
@@ -634,7 +630,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
// suffixes).
@@ -679,7 +675,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
constexpr double inf = std::numeric_limits<double>::infinity();
@@ -706,7 +702,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
constexpr double inf = std::numeric_limits<double>::infinity();
@@ -733,7 +729,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
std::locale c_locale("C");
@@ -828,7 +824,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test an important value, PI/6. That's the one we see in practice.
constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
@@ -845,7 +841,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test an important value, PI/6. That's the one we see in practice.
constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
@@ -862,7 +858,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test an important pair.
constexpr uint64_t lvalue1 = UINT64_C(0x4079000000000000);
@@ -883,7 +879,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
ObjPtr<mirror::Class> class_klass = GetClassRoot<mirror::Class>();
shadow_frame->SetVRegReference(0, class_klass);
@@ -906,7 +902,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
jobject class_loader = LoadDex("Nested");
StackHandleScope<4> hs(self);
@@ -938,7 +934,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
StackHandleScope<1> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -960,14 +956,14 @@
ASSERT_TRUE(caller_method != nullptr);
ASSERT_TRUE(caller_method->IsDirect());
ASSERT_TRUE(caller_method->GetDeclaringClass() == floating_decimal.Get());
- UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0);
+ UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, caller_method, 0);
shadow_frame->SetLink(caller_frame.get());
UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0);
EXPECT_TRUE(result.GetL() != nullptr);
EXPECT_FALSE(self->IsExceptionPending());
- shadow_frame->SetLink(nullptr);
+ shadow_frame->ClearLink();
}
// Negative test.
@@ -978,7 +974,7 @@
ObjPtr<mirror::Class> class_class = GetClassRoot<mirror::Class>();
ArtMethod* caller_method =
&*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin();
- UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0);
+ UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, caller_method, 0);
shadow_frame->SetLink(caller_frame.get());
EnterTransactionMode();
@@ -988,7 +984,7 @@
ASSERT_TRUE(self->IsExceptionPending());
self->ClearException();
- shadow_frame->SetLink(nullptr);
+ shadow_frame->ClearLink();
}
}
@@ -1016,15 +1012,16 @@
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, method, 0);
shadow_frame->SetVRegDouble(0, 1.23);
- interpreter::DoCall<false, false>(method,
- self,
- *shadow_frame,
- Instruction::At(inst_data),
- inst_data[0],
- &result);
+ interpreter::DoCall<false>(method,
+ self,
+ *shadow_frame,
+ Instruction::At(inst_data),
+ inst_data[0],
+ /* string_init= */ false,
+ &result);
ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
ASSERT_TRUE(string_result != nullptr);
@@ -1037,7 +1034,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
StackHandleScope<1> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -1114,13 +1111,15 @@
}
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
for (const char* name : kTestCases) {
ObjPtr<mirror::String> name_string = mirror::String::AllocFromModifiedUtf8(self, name);
CHECK(name_string != nullptr);
if (in_transaction) {
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::String> h(hs.NewHandleWrapper(&name_string));
EnterTransactionMode();
}
CHECK(!self->IsExceptionPending());
@@ -1168,18 +1167,19 @@
CHECK(boot_cp_init != nullptr);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, boot_cp_init, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, boot_cp_init, 0);
shadow_frame->SetVRegReference(0, boot_cp.Get());
// create instruction data for invoke-direct {v0} of method with fake index
uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 };
- interpreter::DoCall<false, false>(boot_cp_init,
- self,
- *shadow_frame,
- Instruction::At(inst_data),
- inst_data[0],
- &result);
+ interpreter::DoCall<false>(boot_cp_init,
+ self,
+ *shadow_frame,
+ Instruction::At(inst_data),
+ inst_data[0],
+ /* string_init= */ false,
+ &result);
CHECK(!self->IsExceptionPending());
}
@@ -1287,7 +1287,7 @@
ASSERT_TRUE(class_linker->EnsureInitialized(self, list_class, true, true));
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
shadow_frame->SetVRegReference(0, list_class.Get());
UnstartedClassGetSignatureAnnotation(self, shadow_frame.get(), &result, 0);
@@ -1339,7 +1339,7 @@
// OK, we're ready now.
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
shadow_frame->SetVRegReference(0, cons.Get());
shadow_frame->SetVRegReference(1, args.Get());
UnstartedConstructorNewInstance0(self, shadow_frame.get(), &result, 0);
@@ -1360,7 +1360,7 @@
TEST_F(UnstartedRuntimeTest, IdentityHashCode) {
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
JValue result;
UnstartedSystemIdentityHashCode(self, tmp.get(), &result, 0);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 7bb359d..abade3a 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -208,7 +208,6 @@
// Jit GC for now (b/147208992).
if (code_cache->GetGarbageCollectCode()) {
code_cache->SetGarbageCollectCode(!jit_compiler_->GenerateDebugInfo() &&
- !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() &&
!jit->JitAtFirstUse());
}
@@ -283,9 +282,8 @@
compilation_kind = CompilationKind::kOptimized;
}
- RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
// Don't compile the method if it has breakpoints.
- if (cb->IsMethodBeingInspected(method)) {
+ if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
VLOG(jit) << "JIT not compiling " << method->PrettyMethod()
<< " due to not being safe to jit according to runtime-callbacks. For example, there"
<< " could be breakpoints in this method.";
@@ -572,12 +570,11 @@
// Before allowing the jump, make sure no code is actively inspecting the method to avoid
// jumping from interpreter to OSR while e.g. single stepping. Note that we could selectively
// disable OSR when single stepping, but that's currently hard to know at this point.
- if (Runtime::Current()->GetInstrumentation()->InterpreterStubsInstalled() ||
- Runtime::Current()->GetInstrumentation()->IsDeoptimized(method) ||
- thread->IsForceInterpreter() ||
- method->GetDeclaringClass()->IsObsoleteObject() ||
- Dbg::IsForcedInterpreterNeededForUpcall(thread, method) ||
- Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
+ // Currently, HaveLocalsChanged is not frame specific. It is possible to make it frame specific
+ // to allow OSR of frames that don't have any locals changed but it isn't worth the additional
+ // complexity.
+ if (Runtime::Current()->GetInstrumentation()->NeedsSlowInterpreterForMethod(thread, method) ||
+ Runtime::Current()->GetRuntimeCallbacks()->HaveLocalsChanged()) {
return false;
}
@@ -754,7 +751,7 @@
class ScopedCompilation {
public:
- ScopedCompilation(ScopedCompilation&& other) :
+ ScopedCompilation(ScopedCompilation&& other) noexcept :
jit_(other.jit_),
method_(other.method_),
compilation_kind_(other.compilation_kind_),
@@ -768,7 +765,11 @@
compilation_kind_(compilation_kind),
owns_compilation_(true) {
MutexLock mu(Thread::Current(), *Locks::jit_lock_);
- if (jit_->GetCodeCache()->IsMethodBeingCompiled(method_, compilation_kind_)) {
+ // We don't want to enqueue any new tasks when thread pool has stopped. This simplifies
+ // the implementation of redefinition feature in jvmti.
+ if (jit_->GetThreadPool() == nullptr ||
+ !jit_->GetThreadPool()->HasStarted(Thread::Current()) ||
+ jit_->GetCodeCache()->IsMethodBeingCompiled(method_, compilation_kind_)) {
owns_compilation_ = false;
return;
}
@@ -779,7 +780,6 @@
return owns_compilation_;
}
-
~ScopedCompilation() {
if (owns_compilation_) {
MutexLock mu(Thread::Current(), *Locks::jit_lock_);
@@ -1317,12 +1317,21 @@
return;
}
Runtime* runtime = Runtime::Current();
- // If the runtime is debuggable, no need to precompile methods.
+ // If the runtime is debuggable, don't bother precompiling methods.
+ // If system server is being profiled, don't precompile as we are going to use
+ // the JIT to count hotness. Note that --count-hotness-in-compiled-code is
+ // only forced when we also profile the boot classpath, see
+ // AndroidRuntime.cpp.
if (runtime->IsSystemServer() &&
UseJitCompilation() &&
options_->UseProfiledJitCompilation() &&
runtime->HasImageWithProfile() &&
+ !runtime->IsSystemServerProfiled() &&
!runtime->IsJavaDebuggable()) {
+ // Note: this precompilation is currently not running in production because:
+ // - UseProfiledJitCompilation() is not set by default.
+ // - System server dex files are registered *before* we set the runtime as
+ // system server (though we are in the system server process).
thread_pool_->AddTask(Thread::Current(), new JitProfileTask(dex_files, class_loader));
}
}
@@ -1366,9 +1375,10 @@
const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
if (class_linker->IsQuickToInterpreterBridge(entry_point) ||
class_linker->IsQuickGenericJniStub(entry_point) ||
- (entry_point == interpreter::GetNterpEntryPoint()) ||
- // We explicitly check for the stub. The trampoline is for methods backed by
- // a .oat file that has a compiled version of the method.
+ class_linker->IsNterpEntryPoint(entry_point) ||
+ // We explicitly check for the resolution stub, and not the resolution trampoline.
+ // The trampoline is for methods backed by a .oat file that has a compiled version of
+ // the method.
(entry_point == GetQuickResolutionStub())) {
VLOG(jit) << "JIT Zygote processing method " << ArtMethod::PrettyMethod(method)
<< " from profile";
@@ -1667,7 +1677,6 @@
// Jit GC for now (b/147208992).
code_cache_->SetGarbageCollectCode(
!jit_compiler_->GenerateDebugInfo() &&
- !runtime->GetInstrumentation()->AreExitStubsInstalled() &&
!JitAtFirstUse());
if (is_system_server && runtime->HasImageWithProfile()) {
@@ -1799,8 +1808,7 @@
// Check if we have precompiled this method.
if (UNLIKELY(method->IsPreCompiled())) {
- if (!NeedsClinitCheckBeforeCall(method) ||
- method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ if (!method->StillNeedsClinitCheck()) {
const void* entry_point = code_cache_->GetSavedEntryPointOfPreCompiledMethod(method);
if (entry_point != nullptr) {
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(method, entry_point);
@@ -1809,7 +1817,7 @@
return;
}
- static constexpr size_t kIndividualSharedMethodHotnessThreshold = 0xff;
+ static constexpr size_t kIndividualSharedMethodHotnessThreshold = 0x3f;
if (method->IsMemorySharedMethod()) {
MutexLock mu(self, lock_);
auto it = shared_method_counters_.find(method);
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index fd92451..c95fd9d 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -196,6 +196,7 @@
virtual bool GenerateDebugInfo() = 0;
virtual void ParseCompilerOptions() = 0;
virtual bool IsBaselineCompiler() const = 0;
+ virtual void SetDebuggableCompilerOption(bool value) = 0;
virtual std::vector<uint8_t> PackElfFileForJIT(ArrayRef<const JITCodeEntry*> elf_files,
ArrayRef<const void*> removed_symbols,
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index a06fe24..c1488ca 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -40,7 +40,7 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/bitmap-inl.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "gc/scoped_gc_critical_section.h"
#include "handle.h"
#include "handle_scope-inl.h"
@@ -127,31 +127,19 @@
DCHECK(entrypoint == OatQuickMethodHeader::FromCodePointer(GetCode())->GetEntryPoint());
instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation();
for (ArtMethod* m : GetMethods()) {
- // Because `m` might be in the process of being deleted:
- // - Call the dedicated method instead of the more generic UpdateMethodsCode
- // - Check the class status without a full read barrier; use ReadBarrier::IsMarked().
- bool can_set_entrypoint = true;
- if (NeedsClinitCheckBeforeCall(m)) {
- // To avoid resurrecting an unreachable object, we must not use a full read
- // barrier but we do not want to miss updating an entrypoint under common
- // circumstances, i.e. during a GC the class becomes visibly initialized,
- // the method becomes hot, we compile the thunk and want to update the
- // entrypoint while the method's declaring class field still points to the
- // from-space class object with the old status. Therefore we read the
- // declaring class without a read barrier and check if it's already marked.
- // If yes, we check the status of the to-space class object as intended.
- // Otherwise, there is no to-space object and the from-space class object
- // contains the most recent value of the status field; even if this races
- // with another thread doing a read barrier and updating the status, that's
- // no different from a race with a thread that just updates the status.
- // Such race can happen only for the zygote method pre-compilation, as we
- // otherwise compile only thunks for methods of visibly initialized classes.
- ObjPtr<mirror::Class> klass = m->GetDeclaringClass<kWithoutReadBarrier>();
- ObjPtr<mirror::Class> marked = ReadBarrier::IsMarked(klass.Ptr());
- ObjPtr<mirror::Class> checked_klass = (marked != nullptr) ? marked : klass;
- can_set_entrypoint = checked_klass->IsVisiblyInitialized();
- }
- if (can_set_entrypoint) {
+ // Because `m` might be in the process of being deleted,
+ // - use the `ArtMethod::StillNeedsClinitCheckMayBeDead()` to check if
+ // we can update the entrypoint, and
+ // - call `Instrumentation::UpdateNativeMethodsCodeToJitCode` instead of the
+ // more generic function `Instrumentation::UpdateMethodsCode()`.
+ // The `ArtMethod::StillNeedsClinitCheckMayBeDead()` checks the class status
+ // in the to-space object if any even if the method's declaring class points to
+ // the from-space class object. This way we do not miss updating an entrypoint
+ // even under uncommon circumstances, when during a GC the class becomes visibly
+ // initialized, the method becomes hot, we compile the thunk and want to update
+ // the entrypoint while the method's declaring class field still points to the
+ // from-space class object with the old status.
+ if (!m->StillNeedsClinitCheckMayBeDead()) {
instrum->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
}
}
@@ -220,9 +208,10 @@
}
}
- size_t initial_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheInitialCapacity();
+ Runtime* runtime = Runtime::Current();
+ size_t initial_capacity = runtime->GetJITOptions()->GetCodeCacheInitialCapacity();
// Check whether the provided max capacity in options is below 1GB.
- size_t max_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheMaxCapacity();
+ size_t max_capacity = runtime->GetJITOptions()->GetCodeCacheMaxCapacity();
// We need to have 32 bit offsets from method headers in code cache which point to things
// in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work.
// Ensure we're below 1 GB to be safe.
@@ -244,6 +233,11 @@
return nullptr;
}
+ if (region.HasCodeMapping()) {
+ const MemMap* exec_pages = region.GetExecPages();
+ runtime->AddGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
+
std::unique_ptr<JitCodeCache> jit_code_cache(new JitCodeCache());
if (is_zygote) {
// Zygote should never collect code to share the memory with the children.
@@ -278,7 +272,16 @@
histogram_profiling_info_memory_use_("Memory used for profiling info", 16) {
}
-JitCodeCache::~JitCodeCache() {}
+JitCodeCache::~JitCodeCache() {
+ if (private_region_.HasCodeMapping()) {
+ const MemMap* exec_pages = private_region_.GetExecPages();
+ Runtime::Current()->RemoveGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
+ if (shared_region_.HasCodeMapping()) {
+ const MemMap* exec_pages = shared_region_.GetExecPages();
+ Runtime::Current()->RemoveGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
+}
bool JitCodeCache::PrivateRegionContainsPc(const void* ptr) const {
return private_region_.IsInExecSpace(ptr);
@@ -289,7 +292,9 @@
}
bool JitCodeCache::ContainsMethod(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
if (UNLIKELY(method->IsNative())) {
auto it = jni_stubs_map_.find(JniStubKey(method));
if (it != jni_stubs_map_.end() &&
@@ -312,7 +317,9 @@
const void* JitCodeCache::GetJniStubCode(ArtMethod* method) {
DCHECK(method->IsNative());
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = jni_stubs_map_.find(JniStubKey(method));
if (it != jni_stubs_map_.end()) {
JniStubData& data = it->second;
@@ -324,12 +331,14 @@
}
const void* JitCodeCache::GetSavedEntryPointOfPreCompiledMethod(ArtMethod* method) {
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
if (method->IsPreCompiled()) {
const void* code_ptr = nullptr;
- if (method->GetDeclaringClass()->IsBootStrapClassLoaded()) {
+ if (method->GetDeclaringClass<kWithoutReadBarrier>()->IsBootStrapClassLoaded()) {
code_ptr = zygote_map_.GetCodeFor(method);
} else {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = saved_compiled_methods_map_.find(method);
if (it != saved_compiled_methods_map_.end()) {
code_ptr = it->second;
@@ -353,12 +362,12 @@
}
static uintptr_t FromCodeToAllocation(const void* code) {
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
return reinterpret_cast<uintptr_t>(code) - RoundUp(sizeof(OatQuickMethodHeader), alignment);
}
static const void* FromAllocationToCode(const uint8_t* alloc) {
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
return reinterpret_cast<const void*>(alloc + RoundUp(sizeof(OatQuickMethodHeader), alignment));
}
@@ -400,7 +409,9 @@
}
void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
for (const auto& entry : method_code_map_) {
uint32_t number_of_roots = 0;
const uint8_t* root_table = GetRootTable(entry.first, &number_of_roots);
@@ -505,6 +516,7 @@
void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) {
ScopedTrace trace(__PRETTY_FUNCTION__);
+ ScopedDebugDisallowReadBarriers sddrb(self);
// We use a set to first collect all method_headers whose code need to be
// removed. We need to free the underlying code after we remove CHA dependencies
// for entries in this set. And it's more efficient to iterate through
@@ -699,32 +711,37 @@
// compiled code is considered invalidated by some class linking, but below we still make the
// compiled code valid for the method. Need cha_lock_ for checking all single-implementation
// flags and register dependencies.
- MutexLock cha_mu(self, *Locks::cha_lock_);
- bool single_impl_still_valid = true;
- for (ArtMethod* single_impl : cha_single_implementation_list) {
- if (!single_impl->HasSingleImplementation()) {
- // Simply discard the compiled code. Clear the counter so that it may be recompiled later.
- // Hopefully the class hierarchy will be more stable when compilation is retried.
- single_impl_still_valid = false;
- ClearMethodCounter(method, /*was_warm=*/ false);
- break;
+ {
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock cha_mu(self, *Locks::cha_lock_);
+ bool single_impl_still_valid = true;
+ for (ArtMethod* single_impl : cha_single_implementation_list) {
+ if (!single_impl->HasSingleImplementation()) {
+ // Simply discard the compiled code. Clear the counter so that it may be recompiled later.
+ // Hopefully the class hierarchy will be more stable when compilation is retried.
+ single_impl_still_valid = false;
+ ClearMethodCounter(method, /*was_warm=*/ false);
+ break;
+ }
+ }
+
+ // Discard the code if any single-implementation assumptions are now invalid.
+ if (UNLIKELY(!single_impl_still_valid)) {
+ VLOG(jit) << "JIT discarded jitted code due to invalid single-implementation assumptions.";
+ return false;
+ }
+ DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsJavaDebuggable())
+ << "Should not be using cha on debuggable apps/runs!";
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ for (ArtMethod* single_impl : cha_single_implementation_list) {
+ class_linker->GetClassHierarchyAnalysis()->AddDependency(
+ single_impl, method, method_header);
}
}
- // Discard the code if any single-implementation assumptions are now invalid.
- if (UNLIKELY(!single_impl_still_valid)) {
- VLOG(jit) << "JIT discarded jitted code due to invalid single-implementation assumptions.";
- return false;
- }
- DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsJavaDebuggable())
- << "Should not be using cha on debuggable apps/runs!";
-
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- for (ArtMethod* single_impl : cha_single_implementation_list) {
- class_linker->GetClassHierarchyAnalysis()->AddDependency(single_impl, method, method_header);
- }
-
if (UNLIKELY(method->IsNative())) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
auto it = jni_stubs_map_.find(JniStubKey(method));
DCHECK(it != jni_stubs_map_.end())
<< "Entry inserted in NotifyCompilationOf() should be alive.";
@@ -735,14 +752,17 @@
data->UpdateEntryPoints(method_header->GetEntryPoint());
} else {
if (method->IsPreCompiled() && IsSharedRegion(*region)) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
zygote_map_.Put(code_ptr, method);
} else {
+ ScopedDebugDisallowReadBarriers sddrb(self);
method_code_map_.Put(code_ptr, method);
}
if (compilation_kind == CompilationKind::kOsr) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
osr_code_map_.Put(method, code_ptr);
- } else if (NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ } else if (method->StillNeedsClinitCheck()) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
// This situation currently only occurs in the jit-zygote mode.
DCHECK(!garbage_collect_code_);
DCHECK(method->IsPreCompiled());
@@ -783,7 +803,9 @@
// This function is used only for testing and only with non-native methods.
CHECK(!method->IsNative());
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
bool osr = osr_code_map_.find(method) != osr_code_map_.end();
bool in_cache = RemoveMethodLocked(method, release_memory);
@@ -852,7 +874,9 @@
// any cached information it has on the method. All threads must be suspended before calling this
// method. The compiled code for the method (if there is any) must not be in any threads call stack.
void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
RemoveMethodLocked(method, /* release_memory= */ true);
}
@@ -863,7 +887,9 @@
// shouldn't be used since it is no longer logically in the jit code cache.
// TODO We should add DCHECKS that validate that the JIT is paused when this method is entered.
void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
if (old_method->IsNative()) {
// Update methods in jni_stubs_map_.
for (auto& entry : jni_stubs_map_) {
@@ -890,11 +916,14 @@
// Check that none of our methods have an entrypoint in the zygote exec
// space (this should be taken care of by
// ClassLinker::UpdateEntryPointsClassVisitor.
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
{
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ MutexLock mu(self, *Locks::jit_lock_);
if (kIsDebugBuild) {
- for (const auto& it : method_code_map_) {
- ArtMethod* method = it.second;
+ // TODO: Check `jni_stubs_map_`?
+ for (const auto& entry : method_code_map_) {
+ ArtMethod* method = entry.second;
DCHECK(!method->IsPreCompiled());
DCHECK(!IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode()));
}
@@ -1031,22 +1060,6 @@
/* context= */ nullptr,
art::StackVisitor::StackWalkKind::kSkipInlinedFrames);
- if (kIsDebugBuild) {
- // The stack walking code queries the side instrumentation stack if it
- // sees an instrumentation exit pc, so the JIT code of methods in that stack
- // must have been seen. We check this below.
- for (const auto& it : *thread->GetInstrumentationStack()) {
- // The 'method_' in InstrumentationStackFrame is the one that has return_pc_ in
- // its stack frame, it is not the method owning return_pc_. We just pass null to
- // LookupMethodHeader: the method is only checked against in debug builds.
- OatQuickMethodHeader* method_header =
- code_cache_->LookupMethodHeader(it.second.return_pc_, /* method= */ nullptr);
- if (method_header != nullptr) {
- const void* code = method_header->GetCode();
- CHECK(bitmap_->Test(FromCodeToAllocation(code)));
- }
- }
- }
barrier_->Pass(Thread::Current());
}
@@ -1155,6 +1168,7 @@
// Start polling the liveness of compiled code to prepare for the next full collection.
if (next_collection_will_be_full) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
for (auto it : profiling_infos_) {
it.second->ResetCounter();
}
@@ -1186,6 +1200,7 @@
void JitCodeCache::RemoveUnmarkedCode(Thread* self) {
ScopedTrace trace(__FUNCTION__);
+ ScopedDebugDisallowReadBarriers sddrb(self);
std::unordered_set<OatQuickMethodHeader*> method_headers;
{
MutexLock mu(self, *Locks::jit_lock_);
@@ -1233,6 +1248,7 @@
}
void JitCodeCache::RemoveMethodBeingCompiled(ArtMethod* method, CompilationKind kind) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
DCHECK(IsMethodBeingCompiled(method, kind));
switch (kind) {
case CompilationKind::kOsr:
@@ -1248,6 +1264,7 @@
}
void JitCodeCache::AddMethodBeingCompiled(ArtMethod* method, CompilationKind kind) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
DCHECK(!IsMethodBeingCompiled(method, kind));
switch (kind) {
case CompilationKind::kOsr:
@@ -1263,6 +1280,7 @@
}
bool JitCodeCache::IsMethodBeingCompiled(ArtMethod* method, CompilationKind kind) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
switch (kind) {
case CompilationKind::kOsr:
return ContainsElement(current_osr_compilations_, method);
@@ -1274,12 +1292,14 @@
}
bool JitCodeCache::IsMethodBeingCompiled(ArtMethod* method) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
return ContainsElement(current_optimized_compilations_, method) ||
ContainsElement(current_osr_compilations_, method) ||
ContainsElement(current_baseline_compilations_, method);
}
ProfilingInfo* JitCodeCache::GetProfilingInfo(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
DCHECK(IsMethodBeingCompiled(method))
<< "GetProfilingInfo should only be called when the method is being compiled";
@@ -1291,6 +1311,7 @@
}
void JitCodeCache::ResetHotnessCounter(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
auto it = profiling_infos_.find(method);
DCHECK(it != profiling_infos_.end());
@@ -1301,6 +1322,7 @@
void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) {
ScopedTrace trace(__FUNCTION__);
{
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
// Update to interpreter the methods that have baseline entrypoints and whose baseline
@@ -1389,7 +1411,9 @@
CHECK(method != nullptr);
}
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
OatQuickMethodHeader* method_header = nullptr;
ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs.
if (method != nullptr && UNLIKELY(method->IsNative())) {
@@ -1444,7 +1468,9 @@
}
OatQuickMethodHeader* JitCodeCache::LookupOsrMethodHeader(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = osr_code_map_.find(method);
if (it == osr_code_map_.end()) {
return nullptr;
@@ -1470,9 +1496,10 @@
return info;
}
-ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNUSED,
+ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self,
ArtMethod* method,
const std::vector<uint32_t>& entries) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
// Check whether some other thread has concurrently created it.
auto it = profiling_infos_.find(method);
if (it != profiling_infos_.end()) {
@@ -1505,11 +1532,14 @@
std::vector<ProfileMethodInfo>& methods) {
Thread* self = Thread::Current();
WaitUntilInlineCacheAccessible(self);
+ // TODO: Avoid read barriers for potentially dead methods.
+ // ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
ScopedTrace trace(__FUNCTION__);
- for (auto it : profiling_infos_) {
- ProfilingInfo* info = it.second;
- ArtMethod* method = info->GetMethod();
+ for (const auto& entry : profiling_infos_) {
+ ArtMethod* method = entry.first;
+ ProfilingInfo* info = entry.second;
+ DCHECK_EQ(method, info->GetMethod());
const DexFile* dex_file = method->GetDexFile();
const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
if (!ContainsElement(dex_base_locations, base_location)) {
@@ -1589,7 +1619,9 @@
}
bool JitCodeCache::IsOsrCompiled(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
return osr_code_map_.find(method) != osr_code_map_.end();
}
@@ -1636,7 +1668,7 @@
}
}
- if (NeedsClinitCheckBeforeCall(method) && !prejit) {
+ if (method->NeedsClinitCheckBeforeCall() && !prejit) {
// We do not need a synchronization barrier for checking the visibly initialized status
// or checking the initialized status just for requesting visible initialization.
ClassStatus status = method->GetDeclaringClass()
@@ -1659,6 +1691,7 @@
}
}
+ ScopedDebugDisallowReadBarriers sddrb(self);
if (compilation_kind == CompilationKind::kOsr) {
MutexLock mu(self, *Locks::jit_lock_);
if (osr_code_map_.find(method) != osr_code_map_.end()) {
@@ -1715,6 +1748,7 @@
}
ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
auto it = profiling_infos_.find(method);
if (it == profiling_infos_.end()) {
@@ -1728,6 +1762,7 @@
}
void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
auto it = profiling_infos_.find(method);
DCHECK(it != profiling_infos_.end());
@@ -1736,6 +1771,7 @@
void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self) {
DCHECK_EQ(Thread::Current(), self);
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
if (UNLIKELY(method->IsNative())) {
auto it = jni_stubs_map_.find(JniStubKey(method));
@@ -1750,19 +1786,36 @@
}
void JitCodeCache::InvalidateAllCompiledCode() {
- art::MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ art::MutexLock mu(self, *Locks::jit_lock_);
VLOG(jit) << "Invalidating all compiled code";
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- for (auto it : method_code_map_) {
- ArtMethod* meth = it.second;
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* linker = runtime->GetClassLinker();
+ instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+ // TODO: Clear `jni_stubs_map_`?
+ for (const auto& entry : method_code_map_) {
+ ArtMethod* meth = entry.second;
// We were compiled, so we must be warm.
ClearMethodCounter(meth, /*was_warm=*/true);
- if (meth->IsObsolete()) {
+ if (UNLIKELY(meth->IsObsolete())) {
linker->SetEntryPointsForObsoleteMethod(meth);
} else {
- Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(meth, /*aot_code=*/ nullptr);
+ instr->InitializeMethodsCode(meth, /*aot_code=*/ nullptr);
}
}
+
+ for (const auto& entry : zygote_map_) {
+ if (entry.method == nullptr) {
+ continue;
+ }
+ if (entry.method->IsPreCompiled()) {
+ entry.method->ClearPreCompiled();
+ }
+ Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(entry.method,
+ /*aot_code=*/nullptr);
+ }
+
saved_compiled_methods_map_.clear();
osr_code_map_.clear();
}
@@ -1780,7 +1833,9 @@
Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ nullptr);
ClearMethodCounter(method, /*was_warm=*/ true);
} else {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = osr_code_map_.find(method);
if (it != osr_code_map_.end() && OatQuickMethodHeader::FromCodePointer(it->second) == header) {
// Remove the OSR method, to avoid using it again.
@@ -1832,7 +1887,8 @@
// We do this now and not in Jit::PostForkChildAction, as system server calls
// JitCodeCache::PostForkChildAction first, and then does some code loading
// that may result in new JIT tasks that we want to keep.
- ThreadPool* pool = Runtime::Current()->GetJit()->GetThreadPool();
+ Runtime* runtime = Runtime::Current();
+ ThreadPool* pool = runtime->GetJit()->GetThreadPool();
if (pool != nullptr) {
pool->RemoveAllTasks(self);
}
@@ -1843,7 +1899,7 @@
// to write to them.
shared_region_.ResetWritableMappings();
- if (is_zygote || Runtime::Current()->IsSafeMode()) {
+ if (is_zygote || runtime->IsSafeMode()) {
// Don't create a private region for a child zygote. Regions are usually map shared
// (to satisfy dual-view), and we don't want children of a child zygote to inherit it.
return;
@@ -1858,8 +1914,8 @@
histogram_code_memory_use_.Reset();
histogram_profiling_info_memory_use_.Reset();
- size_t initial_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheInitialCapacity();
- size_t max_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheMaxCapacity();
+ size_t initial_capacity = runtime->GetJITOptions()->GetCodeCacheInitialCapacity();
+ size_t max_capacity = runtime->GetJITOptions()->GetCodeCacheMaxCapacity();
std::string error_msg;
if (!private_region_.Initialize(initial_capacity,
max_capacity,
@@ -1868,6 +1924,10 @@
&error_msg)) {
LOG(WARNING) << "Could not create private region after zygote fork: " << error_msg;
}
+ if (private_region_.HasCodeMapping()) {
+ const MemMap* exec_pages = private_region_.GetExecPages();
+ runtime->AddGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
}
JitMemoryRegion* JitCodeCache::GetCurrentRegion() {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index a534ba9..3112d27 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -530,6 +530,14 @@
// -------------- Global JIT maps --------------------------------------- //
+ // Note: The methods held in these maps may be dead, so we must ensure that we do not use
+ // read barriers on their declaring classes as that could unnecessarily keep them alive or
+ // crash the GC, depending on the GC phase and particular GC's details. Asserting that we
+ // do not emit read barriers for these methods can be tricky as we're allowed to emit read
+ // barriers for other methods that are known to be alive, such as the method being compiled.
+ // The GC must ensure that methods in these maps are cleaned up with `RemoveMethodsIn()`
+ // before the declaring class memory is freed.
+
// Holds compiled code associated with the shorty for a JNI stub.
SafeMap<JniStubKey, JniStubData> jni_stubs_map_ GUARDED_BY(Locks::jit_lock_);
diff --git a/runtime/jit/jit_load_test.cc b/runtime/jit/jit_load_test.cc
new file mode 100644
index 0000000..4b080a5
--- /dev/null
+++ b/runtime/jit/jit_load_test.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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 "common_runtime_test.h"
+#include "compiler_callbacks.h"
+
+namespace art {
+
+class JitLoadTest : public CommonRuntimeTest {
+ protected:
+ void SetUpRuntimeOptions(RuntimeOptions *options) override {
+ callbacks_.reset();
+ CommonRuntimeTest::SetUpRuntimeOptions(options);
+ options->push_back(std::make_pair("-Xusejit:true", nullptr));
+ }
+};
+
+
+TEST_F(JitLoadTest, JitLoad) {
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ runtime_->Start();
+ ASSERT_NE(runtime_->GetJit(), nullptr);
+}
+
+} // namespace art
diff --git a/runtime/jit/jit_memory_region.cc b/runtime/jit/jit_memory_region.cc
index 56407f5..410bf70 100644
--- a/runtime/jit/jit_memory_region.cc
+++ b/runtime/jit/jit_memory_region.cc
@@ -27,7 +27,7 @@
#include "base/membarrier.h"
#include "base/memfd.h"
#include "base/systrace.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "jit/jit_scoped_code_cache_write.h"
#include "oat_quick_method_header.h"
#include "palette/palette.h"
@@ -360,7 +360,7 @@
DCHECK(IsInExecSpace(reserved_code.data()));
ScopedCodeCacheWrite scc(*this);
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
size_t header_size = OatQuickMethodHeader::InstructionAlignedSize();
size_t total_size = header_size + code.size();
@@ -468,7 +468,7 @@
}
const uint8_t* JitMemoryRegion::AllocateCode(size_t size) {
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
void* result = mspace_memalign(exec_mspace_, alignment, size);
if (UNLIKELY(result == nullptr)) {
return nullptr;
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index cea654f..8466753 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -23,7 +23,6 @@
#include <unistd.h>
#include "android-base/strings.h"
-
#include "art_method-inl.h"
#include "base/compiler_filter.h"
#include "base/enums.h"
@@ -32,6 +31,7 @@
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
+#include "base/unix_file/fd_file.h"
#include "class_table-inl.h"
#include "dex/dex_file_loader.h"
#include "dex_reference_collection.h"
@@ -136,9 +136,10 @@
{
MutexLock mu(self, wait_lock_);
- const uint64_t end_time = NanoTime() + MsToNs(force_early_first_save
+ const uint64_t sleep_time = MsToNs(force_early_first_save
? options_.GetMinFirstSaveMs()
: options_.GetSaveResolvedClassesDelayMs());
+ const uint64_t end_time = NanoTime() + sleep_time;
while (!Runtime::Current()->GetStartupCompleted()) {
const uint64_t current_time = NanoTime();
if (current_time >= end_time) {
@@ -146,7 +147,7 @@
}
period_condition_.TimedWait(self, NsToMs(end_time - current_time), 0);
}
- total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs();
+ total_ms_of_sleep_ += sleep_time;
}
// Tell the runtime that startup is completed if it has not already been notified.
// TODO: We should use another thread to do this in case the profile saver is not running.
@@ -870,10 +871,23 @@
{
ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(),
/*for_boot_image=*/ options_.GetProfileBootClassPath());
- if (!info.Load(filename, /*clear_if_invalid=*/ true)) {
- LOG(WARNING) << "Could not forcefully load profile " << filename;
- continue;
+ if (OS::FileExists(filename.c_str())) {
+ if (!info.Load(filename, /*clear_if_invalid=*/true)) {
+ LOG(WARNING) << "Could not forcefully load profile " << filename;
+ continue;
+ }
+ } else {
+ // Create a file if it doesn't exist.
+ unix_file::FdFile file(filename.c_str(),
+ O_WRONLY | O_TRUNC | O_CREAT,
+ S_IRUSR | S_IWUSR,
+ /*check_usage=*/false);
+ if (!file.IsValid()) {
+ LOG(WARNING) << "Could not create profile " << filename;
+ continue;
+ }
}
+
uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
VLOG(profiler) << "last_save_number_of_methods=" << last_save_number_of_methods
diff --git a/runtime/jit/profiling_info_test.cc b/runtime/jit/profiling_info_test.cc
index ce0a30f..0bd21aa 100644
--- a/runtime/jit/profiling_info_test.cc
+++ b/runtime/jit/profiling_info_test.cc
@@ -72,6 +72,7 @@
Hotness::Flag flags) {
ProfileCompilationInfo info;
std::vector<ProfileMethodInfo> profile_methods;
+ profile_methods.reserve(methods.size());
ScopedObjectAccess soa(Thread::Current());
for (ArtMethod* method : methods) {
profile_methods.emplace_back(
diff --git a/runtime/jni/check_jni.cc b/runtime/jni/check_jni.cc
index 4c7b1aa..eb54f98 100644
--- a/runtime/jni/check_jni.cc
+++ b/runtime/jni/check_jni.cc
@@ -38,6 +38,7 @@
#include "indirect_reference_table-inl.h"
#include "java_vm_ext.h"
#include "jni_internal.h"
+#include "local_reference_table-inl.h"
#include "mirror/class-inl.h"
#include "mirror/field.h"
#include "mirror/method.h"
@@ -56,15 +57,20 @@
// declared as a friend by JniVmExt and JniEnvExt.
inline IndirectReferenceTable* GetIndirectReferenceTable(ScopedObjectAccess& soa,
IndirectRefKind kind) {
- DCHECK_NE(kind, kJniTransitionOrInvalid);
- JNIEnvExt* env = soa.Env();
- IndirectReferenceTable* irt =
- (kind == kLocal) ? &env->locals_
- : ((kind == kGlobal) ? &env->vm_->globals_ : &env->vm_->weak_globals_);
+ DCHECK_NE(kind, kJniTransition);
+ DCHECK_NE(kind, kLocal);
+ JavaVMExt* vm = soa.Env()->GetVm();
+ IndirectReferenceTable* irt = (kind == kGlobal) ? &vm->globals_ : &vm->weak_globals_;
DCHECK_EQ(irt->GetKind(), kind);
return irt;
}
+// This helper cannot be in the anonymous namespace because it needs to be
+// declared as a friend by JniEnvExt.
+inline jni::LocalReferenceTable* GetLocalReferenceTable(ScopedObjectAccess& soa) {
+ return &soa.Env()->locals_;
+}
+
namespace {
using android::base::StringAppendF;
@@ -723,7 +729,7 @@
IndirectRefKind found_kind;
if (expected_kind == kLocal) {
found_kind = IndirectReferenceTable::GetIndirectRefKind(obj);
- if (found_kind == kJniTransitionOrInvalid &&
+ if (found_kind == kJniTransition &&
obj != nullptr &&
self->IsJniTransitionReference(obj)) {
found_kind = kLocal;
@@ -866,13 +872,19 @@
bool expect_null = false;
bool okay = true;
std::string error_msg;
- if (ref_kind == kJniTransitionOrInvalid) {
+ if (ref_kind == kJniTransition) {
if (!soa.Self()->IsJniTransitionReference(java_object)) {
okay = false;
error_msg = "use of invalid jobject";
} else {
obj = soa.Decode<mirror::Object>(java_object);
}
+ } else if (ref_kind == kLocal) {
+ jni::LocalReferenceTable* lrt = GetLocalReferenceTable(soa);
+ okay = lrt->IsValidReference(java_object, &error_msg);
+ if (okay) {
+ obj = lrt->Get(ref);
+ }
} else {
IndirectReferenceTable* irt = GetIndirectReferenceTable(soa, ref_kind);
okay = irt->IsValidReference(java_object, &error_msg);
@@ -881,10 +893,7 @@
// Note: The `IsValidReference()` checks for null but we do not prevent races,
// so the null check below can still fail. Even if it succeeds, another thread
// could delete the global or weak global before it's used by JNI.
- if (ref_kind == kLocal) {
- // Local references do not need a read barrier.
- obj = irt->Get<kWithoutReadBarrier>(ref);
- } else if (ref_kind == kGlobal) {
+ if (ref_kind == kGlobal) {
obj = soa.Env()->GetVm()->DecodeGlobal(ref);
} else {
obj = soa.Env()->GetVm()->DecodeWeakGlobal(soa.Self(), ref);
@@ -2340,6 +2349,7 @@
CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
}
+ NO_STACK_PROTECTOR
static void CallStaticVoidMethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
}
@@ -3304,6 +3314,7 @@
return result;
}
+ NO_STACK_PROTECTOR
static JniValueType CallMethodV(const char* function_name, JNIEnv* env, jobject obj, jclass c,
jmethodID mid, va_list vargs, Primitive::Type type,
InvokeType invoke) {
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index 39d5729..7ae6c99 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -44,7 +44,6 @@
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
#include "nativeloader/native_loader.h"
-#include "object_callbacks.h"
#include "parsed_options.h"
#include "runtime-inl.h"
#include "runtime_options.h"
@@ -53,7 +52,7 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti/agent.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -306,6 +305,7 @@
*detail += "No implementation found for ";
*detail += m->PrettyMethod();
*detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
+ *detail += " - is the library loaded, e.g. System.loadLibrary?";
}
return nullptr;
}
@@ -495,9 +495,7 @@
JII::AttachCurrentThreadAsDaemon
};
-JavaVMExt::JavaVMExt(Runtime* runtime,
- const RuntimeArgumentMap& runtime_options,
- std::string* error_msg)
+JavaVMExt::JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options)
: runtime_(runtime),
check_jni_abort_hook_(nullptr),
check_jni_abort_hook_data_(nullptr),
@@ -506,13 +504,10 @@
tracing_enabled_(runtime_options.Exists(RuntimeArgumentMap::JniTrace)
|| VLOG_IS_ON(third_party_jni)),
trace_(runtime_options.GetOrDefault(RuntimeArgumentMap::JniTrace)),
- globals_(kGlobalsMax, kGlobal, IndirectReferenceTable::ResizableCapacity::kNo, error_msg),
+ globals_(kGlobal),
libraries_(new Libraries),
unchecked_functions_(&gJniInvokeInterface),
- weak_globals_(kWeakGlobalsMax,
- kWeakGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- error_msg),
+ weak_globals_(kWeakGlobal),
allow_accessing_weak_globals_(true),
weak_globals_add_condition_("weak globals add condition",
(CHECK(Locks::jni_weak_globals_lock_ != nullptr),
@@ -527,21 +522,23 @@
SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni) || kIsDebugBuild);
}
+bool JavaVMExt::Initialize(std::string* error_msg) {
+ return globals_.Initialize(kGlobalsMax, error_msg) &&
+ weak_globals_.Initialize(kWeakGlobalsMax, error_msg);
+}
+
JavaVMExt::~JavaVMExt() {
UnloadBootNativeLibraries();
}
-// Checking "globals" and "weak_globals" usually requires locks, but we
-// don't need the locks to check for validity when constructing the
-// object. Use NO_THREAD_SAFETY_ANALYSIS for this.
std::unique_ptr<JavaVMExt> JavaVMExt::Create(Runtime* runtime,
const RuntimeArgumentMap& runtime_options,
- std::string* error_msg) NO_THREAD_SAFETY_ANALYSIS {
- std::unique_ptr<JavaVMExt> java_vm(new JavaVMExt(runtime, runtime_options, error_msg));
- if (java_vm && java_vm->globals_.IsValid() && java_vm->weak_globals_.IsValid()) {
- return java_vm;
+ std::string* error_msg) {
+ std::unique_ptr<JavaVMExt> java_vm(new JavaVMExt(runtime, runtime_options));
+ if (!java_vm->Initialize(error_msg)) {
+ return nullptr;
}
- return nullptr;
+ return java_vm;
}
jint JavaVMExt::HandleGetEnv(/*out*/void** env, jint version) {
@@ -698,7 +695,7 @@
std::string error_msg;
{
WriterMutexLock mu(self, *Locks::jni_globals_lock_);
- ref = globals_.Add(kIRTFirstSegment, obj, &error_msg);
+ ref = globals_.Add(obj, &error_msg);
MaybeTraceGlobals();
}
if (UNLIKELY(ref == nullptr)) {
@@ -734,7 +731,7 @@
WaitForWeakGlobalsAccess(self);
}
std::string error_msg;
- IndirectRef ref = weak_globals_.Add(kIRTFirstSegment, obj, &error_msg);
+ IndirectRef ref = weak_globals_.Add(obj, &error_msg);
MaybeTraceWeakGlobals();
if (UNLIKELY(ref == nullptr)) {
LOG(FATAL) << error_msg;
@@ -749,7 +746,7 @@
}
{
WriterMutexLock mu(self, *Locks::jni_globals_lock_);
- if (!globals_.Remove(kIRTFirstSegment, obj)) {
+ if (!globals_.Remove(obj)) {
LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
<< "failed to find entry";
}
@@ -763,7 +760,7 @@
return;
}
MutexLock mu(self, *Locks::jni_weak_globals_lock_);
- if (!weak_globals_.Remove(kIRTFirstSegment, obj)) {
+ if (!weak_globals_.Remove(obj)) {
LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
<< "failed to find entry";
}
@@ -834,7 +831,7 @@
}
ObjPtr<mirror::Object> JavaVMExt::DecodeGlobal(IndirectRef ref) {
- return globals_.SynchronizedGet(ref);
+ return globals_.Get(ref);
}
void JavaVMExt::UpdateGlobal(Thread* self, IndirectRef ref, ObjPtr<mirror::Object> result) {
@@ -851,7 +848,7 @@
// if MayAccessWeakGlobals is false.
DCHECK_EQ(IndirectReferenceTable::GetIndirectRefKind(ref), kWeakGlobal);
if (LIKELY(MayAccessWeakGlobals(self))) {
- return weak_globals_.SynchronizedGet(ref);
+ return weak_globals_.Get(ref);
}
MutexLock mu(self, *Locks::jni_weak_globals_lock_);
return DecodeWeakGlobalLocked(self, ref);
@@ -869,6 +866,11 @@
return weak_globals_.Get(ref);
}
+ObjPtr<mirror::Object> JavaVMExt::DecodeWeakGlobalAsStrong(IndirectRef ref) {
+ // The target is known to be alive. Simple `Get()` with read barrier is enough.
+ return weak_globals_.Get(ref);
+}
+
ObjPtr<mirror::Object> JavaVMExt::DecodeWeakGlobalDuringShutdown(Thread* self, IndirectRef ref) {
DCHECK_EQ(IndirectReferenceTable::GetIndirectRefKind(ref), kWeakGlobal);
DCHECK(Runtime::Current()->IsShuttingDown(self));
@@ -879,7 +881,7 @@
if (!gUseReadBarrier) {
DCHECK(allow_accessing_weak_globals_.load(std::memory_order_seq_cst));
}
- return weak_globals_.SynchronizedGet(ref);
+ return weak_globals_.Get(ref);
}
bool JavaVMExt::IsWeakGlobalCleared(Thread* self, IndirectRef ref) {
@@ -945,7 +947,7 @@
ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
+ if (class_linker->IsBootClassLoader(loader)) {
loader = nullptr;
class_loader = nullptr;
if (caller_class != nullptr) {
@@ -957,7 +959,7 @@
}
}
- class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
+ class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader);
CHECK(class_loader_allocator != nullptr);
}
if (library != nullptr) {
@@ -1160,7 +1162,7 @@
CHECK(m->IsNative());
ObjPtr<mirror::Class> c = m->GetDeclaringClass();
// If this is a static method, it could be called before the class has been initialized.
- CHECK(c->IsInitializing() || !NeedsClinitCheckBeforeCall(m))
+ CHECK(c->IsInitializing() || !m->NeedsClinitCheckBeforeCall())
<< c->GetStatus() << " " << m->PrettyMethod();
Thread* const self = Thread::Current();
void* native_method = libraries_->FindNativeMethod(self, m, error_msg, can_suspend);
@@ -1172,23 +1174,6 @@
return native_method;
}
-void JavaVMExt::SweepJniWeakGlobals(IsMarkedVisitor* visitor) {
- MutexLock mu(Thread::Current(), *Locks::jni_weak_globals_lock_);
- Runtime* const runtime = Runtime::Current();
- for (auto* entry : weak_globals_) {
- // Need to skip null here to distinguish between null entries and cleared weak ref entries.
- if (!entry->IsNull()) {
- // Since this is called by the GC, we don't need a read barrier.
- mirror::Object* obj = entry->Read<kWithoutReadBarrier>();
- mirror::Object* new_obj = visitor->IsMarked(obj);
- if (new_obj == nullptr) {
- new_obj = runtime->GetClearedJniWeakGlobal();
- }
- *entry = GcRoot<mirror::Object>(new_obj);
- }
- }
-}
-
void JavaVMExt::TrimGlobals() {
WriterMutexLock mu(Thread::Current(), *Locks::jni_globals_lock_);
globals_.Trim();
@@ -1205,12 +1190,14 @@
if (class_loader == nullptr) {
return nullptr;
}
- if (!env->IsInstanceOf(class_loader, WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Object> mirror_class_loader = soa.Decode<mirror::Object>(class_loader);
+ if (!mirror_class_loader->InstanceOf(WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
return nullptr;
}
- return reinterpret_cast<jstring>(env->CallObjectMethod(
- class_loader,
- WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath));
+ return soa.AddLocalReference<jstring>(
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath->InvokeVirtual<'L'>(
+ soa.Self(), mirror_class_loader));
}
// JNI Invocation interface.
diff --git a/runtime/jni/java_vm_ext.h b/runtime/jni/java_vm_ext.h
index 08de18b..7f4f548 100644
--- a/runtime/jni/java_vm_ext.h
+++ b/runtime/jni/java_vm_ext.h
@@ -165,7 +165,9 @@
void SweepJniWeakGlobals(IsMarkedVisitor* visitor)
REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::jni_weak_globals_lock_);
+ REQUIRES(!Locks::jni_weak_globals_lock_) {
+ weak_globals_.SweepJniWeakGlobals(visitor);
+ }
ObjPtr<mirror::Object> DecodeGlobal(IndirectRef ref)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -182,6 +184,11 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::jni_weak_globals_lock_);
+ // Decode weak global as strong. Use only if the target object is known to be alive.
+ ObjPtr<mirror::Object> DecodeWeakGlobalAsStrong(IndirectRef ref)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::jni_weak_globals_lock_);
+
// Like DecodeWeakGlobal() but to be used only during a runtime shutdown where self may be
// null.
ObjPtr<mirror::Object> DecodeWeakGlobalDuringShutdown(Thread* self, IndirectRef ref)
@@ -218,9 +225,12 @@
static jstring GetLibrarySearchPath(JNIEnv* env, jobject class_loader);
private:
- // The constructor should not be called directly. It may leave the object in
- // an erroneous state, and the result needs to be checked.
- JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options, std::string* error_msg);
+ // The constructor should not be called directly. Use `Create()` that initializes
+ // the new `JavaVMExt` object by calling `Initialize()`.
+ JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options);
+
+ // Initialize the `JavaVMExt` object.
+ bool Initialize(std::string* error_msg);
// Return true if self can currently access weak globals.
bool MayAccessWeakGlobals(Thread* self) const REQUIRES_SHARED(Locks::mutator_lock_);
@@ -248,7 +258,6 @@
// Extra diagnostics.
const std::string trace_;
- // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
IndirectReferenceTable globals_;
// No lock annotation since UnloadNativeLibraries is called on libraries_ but locks the
@@ -260,10 +269,8 @@
// Since weak_globals_ contain weak roots, be careful not to
// directly access the object references in it. Use Get() with the
- // read barrier enabled.
- // Not guarded by weak_globals_lock since we may use SynchronizedGet in DecodeWeakGlobal.
+ // read barrier enabled or disabled based on the use case.
IndirectReferenceTable weak_globals_;
- // Not guarded by weak_globals_lock since we may use SynchronizedGet in DecodeWeakGlobal.
Atomic<bool> allow_accessing_weak_globals_;
ConditionVariable weak_globals_add_condition_ GUARDED_BY(Locks::jni_weak_globals_lock_);
diff --git a/runtime/jni/jni_env_ext-inl.h b/runtime/jni/jni_env_ext-inl.h
index 7609a9e..0c04192 100644
--- a/runtime/jni/jni_env_ext-inl.h
+++ b/runtime/jni/jni_env_ext-inl.h
@@ -19,6 +19,7 @@
#include "jni_env_ext.h"
+#include "local_reference_table-inl.h"
#include "mirror/object.h"
namespace art {
@@ -49,6 +50,10 @@
return reinterpret_cast<T>(ref);
}
+inline void JNIEnvExt::UpdateLocal(IndirectRef iref, ObjPtr<mirror::Object> obj) {
+ locals_.Update(iref, obj);
+}
+
} // namespace art
#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_
diff --git a/runtime/jni/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc
index 4510b37..619e1de 100644
--- a/runtime/jni/jni_env_ext.cc
+++ b/runtime/jni/jni_env_ext.cc
@@ -44,13 +44,6 @@
const JNINativeInterface* JNIEnvExt::table_override_ = nullptr;
-bool JNIEnvExt::CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS {
- if (in == nullptr) {
- return false;
- }
- return in->locals_.IsValid();
-}
-
jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) {
UNUSED(vm);
// GetEnv always returns a JNIEnv* for the most current supported JNI version,
@@ -66,18 +59,18 @@
}
JNIEnvExt* JNIEnvExt::Create(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg) {
- std::unique_ptr<JNIEnvExt> ret(new JNIEnvExt(self_in, vm_in, error_msg));
- if (CheckLocalsValid(ret.get())) {
- return ret.release();
+ std::unique_ptr<JNIEnvExt> ret(new JNIEnvExt(self_in, vm_in));
+ if (!ret->Initialize(error_msg)) {
+ return nullptr;
}
- return nullptr;
+ return ret.release();
}
-JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg)
+JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in)
: self_(self_in),
vm_(vm_in),
- local_ref_cookie_(kIRTFirstSegment),
- locals_(1, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg),
+ local_ref_cookie_(jni::kLRTFirstSegment),
+ locals_(),
monitors_("monitors", kMonitorsInitial, kMonitorsMax),
critical_(0),
check_jni_(false),
@@ -88,6 +81,10 @@
unchecked_functions_ = GetJniNativeInterface();
}
+bool JNIEnvExt::Initialize(std::string* error_msg) {
+ return locals_.Initialize(/*max_count=*/ 1u, error_msg);
+}
+
void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
functions = GetRuntimeShutdownNativeInterface();
}
@@ -157,7 +154,7 @@
4 + // local_ref_cookie.
(pointer_size - 4); // Padding.
size_t irt_segment_state_offset =
- IndirectReferenceTable::SegmentStateOffset(pointer_size).Int32Value();
+ jni::LocalReferenceTable::SegmentStateOffset(pointer_size).Int32Value();
return MemberOffset(locals_offset + irt_segment_state_offset);
}
diff --git a/runtime/jni/jni_env_ext.h b/runtime/jni/jni_env_ext.h
index bdde5f8..1f57658 100644
--- a/runtime/jni/jni_env_ext.h
+++ b/runtime/jni/jni_env_ext.h
@@ -21,7 +21,7 @@
#include "base/locks.h"
#include "base/macros.h"
-#include "indirect_reference_table.h"
+#include "local_reference_table.h"
#include "obj_ptr.h"
#include "reference_table.h"
@@ -63,9 +63,8 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::alloc_tracker_lock_);
- void UpdateLocal(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- locals_.Update(iref, obj);
- }
+ void UpdateLocal(IndirectRef iref, ObjPtr<mirror::Object> obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
jobject NewLocalRef(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
void DeleteLocalRef(jobject obj) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -80,13 +79,13 @@
return locals_.Capacity();
}
- IRTSegmentState GetLocalRefCookie() const { return local_ref_cookie_; }
- void SetLocalRefCookie(IRTSegmentState new_cookie) { local_ref_cookie_ = new_cookie; }
+ jni::LRTSegmentState GetLocalRefCookie() const { return local_ref_cookie_; }
+ void SetLocalRefCookie(jni::LRTSegmentState new_cookie) { local_ref_cookie_ = new_cookie; }
- IRTSegmentState GetLocalsSegmentState() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ jni::LRTSegmentState GetLocalsSegmentState() const REQUIRES_SHARED(Locks::mutator_lock_) {
return locals_.GetSegmentState();
}
- void SetLocalSegmentState(IRTSegmentState new_state) REQUIRES_SHARED(Locks::mutator_lock_) {
+ void SetLocalSegmentState(jni::LRTSegmentState new_state) REQUIRES_SHARED(Locks::mutator_lock_) {
locals_.SetSegmentState(new_state);
}
@@ -151,20 +150,18 @@
REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_);
private:
- // Checking "locals" requires the mutator lock, but at creation time we're
- // really only interested in validity, which isn't changing. To avoid grabbing
- // the mutator lock, factored out and tagged with NO_THREAD_SAFETY_ANALYSIS.
- static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS;
-
// Override of function tables. This applies to both default as well as instrumented (CheckJNI)
// function tables.
static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_);
- // The constructor should not be called directly. It may leave the object in an erroneous state,
- // and the result needs to be checked.
- JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg)
+ // The constructor should not be called directly. Use `Create()` that initializes
+ // the new `JNIEnvExt` object by calling `Initialize()`.
+ JNIEnvExt(Thread* self, JavaVMExt* vm)
REQUIRES(!Locks::jni_function_table_lock_);
+ // Initialize the `JNIEnvExt` object.
+ bool Initialize(std::string* error_msg);
+
// Link to Thread::Current().
Thread* const self_;
@@ -172,15 +169,15 @@
JavaVMExt* const vm_;
// Cookie used when using the local indirect reference table.
- IRTSegmentState local_ref_cookie_;
+ jni::LRTSegmentState local_ref_cookie_;
// JNI local references.
- IndirectReferenceTable locals_ GUARDED_BY(Locks::mutator_lock_);
+ jni::LocalReferenceTable locals_;
// Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
// TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
// to a native method.
- std::vector<IRTSegmentState> stacked_local_ref_cookies_;
+ std::vector<jni::LRTSegmentState> stacked_local_ref_cookies_;
// Entered JNI monitors, for bulk exit on thread detach.
ReferenceTable monitors_;
@@ -211,6 +208,7 @@
friend class Thread;
friend IndirectReferenceTable* GetIndirectReferenceTable(ScopedObjectAccess& soa,
IndirectRefKind kind);
+ friend jni::LocalReferenceTable* GetLocalReferenceTable(ScopedObjectAccess& soa);
friend void ThreadResetFunctionTable(Thread* thread, void* arg);
ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets);
};
@@ -232,7 +230,7 @@
private:
JNIEnvExt* const env_;
- const IRTSegmentState saved_local_ref_cookie_;
+ const jni::LRTSegmentState saved_local_ref_cookie_;
DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
};
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index ddbe527..ad2efc5 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -22,7 +22,7 @@
#include <utility>
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/allocator.h"
#include "base/atomic.h"
#include "base/casts.h"
@@ -37,6 +37,7 @@
#include "dex/dex_file-inl.h"
#include "dex/utf-inl.h"
#include "fault_handler.h"
+#include "handle_scope.h"
#include "hidden_api.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root.h"
@@ -63,7 +64,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -392,8 +393,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
// If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
- if (method ==
- jni::DecodeArtMethod<kEnableIndexIds>(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
+ if (method == WellKnownClasses::java_lang_Runtime_nativeLoad) {
return soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
}
// If we have a method, use its ClassLoader for context.
@@ -954,16 +954,15 @@
WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid)));
return CallStaticObjectMethodV(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
}
- ObjPtr<mirror::Object> result = c->AllocObject(soa.Self());
+ ScopedLocalRef<jobject> result(env, soa.AddLocalReference<jobject>(c->AllocObject(soa.Self())));
if (result == nullptr) {
return nullptr;
}
- jobject local_result = soa.AddLocalReference<jobject>(result);
- CallNonvirtualVoidMethodV(env, local_result, java_class, mid, args);
+ CallNonvirtualVoidMethodV(env, result.get(), java_class, mid, args);
if (soa.Self()->IsExceptionPending()) {
return nullptr;
}
- return local_result;
+ return result.release();
}
static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID mid, const jvalue* args) {
@@ -981,16 +980,15 @@
WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid)));
return CallStaticObjectMethodA(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
}
- ObjPtr<mirror::Object> result = c->AllocObject(soa.Self());
+ ScopedLocalRef<jobject> result(env, soa.AddLocalReference<jobject>(c->AllocObject(soa.Self())));
if (result == nullptr) {
return nullptr;
}
- jobject local_result = soa.AddLocalReference<jobjectArray>(result);
- CallNonvirtualVoidMethodA(env, local_result, java_class, mid, args);
+ CallNonvirtualVoidMethodA(env, result.get(), java_class, mid, args);
if (soa.Self()->IsExceptionPending()) {
return nullptr;
}
- return local_result;
+ return result.release();
}
static jmethodID GetMethodID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
@@ -1269,8 +1267,7 @@
CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
- jobject local_result = soa.AddLocalReference<jobject>(result.GetL());
- return local_result;
+ return soa.AddLocalReference<jobject>(result.GetL());
}
static jobject CallNonvirtualObjectMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
@@ -1756,8 +1753,7 @@
CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
- jobject local_result = soa.AddLocalReference<jobject>(result.GetL());
- return local_result;
+ return soa.AddLocalReference<jobject>(result.GetL());
}
static jobject CallStaticObjectMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
@@ -1950,6 +1946,7 @@
return InvokeWithJValues(soa, nullptr, mid, args).GetD();
}
+ NO_STACK_PROTECTOR
static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
@@ -1959,6 +1956,7 @@
InvokeWithVarArgs(soa, nullptr, mid, ap);
}
+ NO_STACK_PROTECTOR
static void CallStaticVoidMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
ScopedObjectAccess soa(env);
@@ -2777,10 +2775,10 @@
jlong address_arg = reinterpret_cast<jlong>(address);
jint capacity_arg = static_cast<jint>(capacity);
- jobject result = env->NewObject(WellKnownClasses::java_nio_DirectByteBuffer,
- WellKnownClasses::java_nio_DirectByteBuffer_init,
- address_arg, capacity_arg);
- return static_cast<JNIEnvExt*>(env)->self_->IsExceptionPending() ? nullptr : result;
+ ScopedObjectAccess soa(env);
+ return soa.AddLocalReference<jobject>(
+ WellKnownClasses::java_nio_DirectByteBuffer_init->NewObject<'J', 'I'>(
+ soa.Self(), address_arg, capacity_arg));
}
static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) {
@@ -2789,14 +2787,16 @@
return nullptr;
}
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Object> buffer = soa.Decode<mirror::Object>(java_buffer);
+
// Return null if |java_buffer| is not a java.nio.Buffer instance.
- if (!IsInstanceOf(env, java_buffer, WellKnownClasses::java_nio_Buffer)) {
+ if (!buffer->InstanceOf(WellKnownClasses::java_nio_Buffer.Get())) {
return nullptr;
}
// Buffer.address is non-null when the |java_buffer| is direct.
- return reinterpret_cast<void*>(env->GetLongField(
- java_buffer, WellKnownClasses::java_nio_Buffer_address));
+ return reinterpret_cast<void*>(WellKnownClasses::java_nio_Buffer_address->GetLong(buffer));
}
static jlong GetDirectBufferCapacity(JNIEnv* env, jobject java_buffer) {
@@ -2804,7 +2804,10 @@
return -1;
}
- if (!IsInstanceOf(env, java_buffer, WellKnownClasses::java_nio_Buffer)) {
+ ScopedObjectAccess soa(env);
+ StackHandleScope<1u> hs(soa.Self());
+ Handle<mirror::Object> buffer = hs.NewHandle(soa.Decode<mirror::Object>(java_buffer));
+ if (!buffer->InstanceOf(WellKnownClasses::java_nio_Buffer.Get())) {
return -1;
}
@@ -2813,16 +2816,18 @@
// We therefore call Buffer.isDirect(). One path that creates such a buffer is
// FileChannel.map() if the file size is zero.
//
- // NB GetDirectBufferAddress() does not need to call Buffer.isDirect() since it is only
+ // NB GetDirectBufferAddress() does not need to call `Buffer.isDirect()` since it is only
// able return a valid address if the Buffer address field is not-null.
- jboolean direct = env->CallBooleanMethod(java_buffer,
- WellKnownClasses::java_nio_Buffer_isDirect);
- if (!direct) {
+ //
+ // Note: We can hit a `StackOverflowError` during the invocation but `Buffer.isDirect()`
+ // implementations should not otherwise throw any exceptions.
+ bool direct = WellKnownClasses::java_nio_Buffer_isDirect->InvokeVirtual<'Z'>(
+ soa.Self(), buffer.Get());
+ if (UNLIKELY(soa.Self()->IsExceptionPending()) || !direct) {
return -1;
}
- return static_cast<jlong>(env->GetIntField(
- java_buffer, WellKnownClasses::java_nio_Buffer_capacity));
+ return static_cast<jlong>(WellKnownClasses::java_nio_Buffer_capacity->GetInt(buffer.Get()));
}
static jobjectRefType GetObjectRefType(JNIEnv* env ATTRIBUTE_UNUSED, jobject java_object) {
@@ -2840,7 +2845,7 @@
return JNIGlobalRefType;
case kWeakGlobal:
return JNIWeakGlobalRefType;
- case kJniTransitionOrInvalid:
+ case kJniTransition:
// Assume value is in a JNI transition frame.
return JNILocalRefType;
}
diff --git a/runtime/jni/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc
index eca5aec..5abedb9 100644
--- a/runtime/jni/jni_internal_test.cc
+++ b/runtime/jni/jni_internal_test.cc
@@ -21,7 +21,7 @@
#include "art_method-inl.h"
#include "base/mem_map.h"
#include "common_runtime_test.h"
-#include "indirect_reference_table.h"
+#include "local_reference_table.h"
#include "java_vm_ext.h"
#include "jni_env_ext.h"
#include "mirror/string-inl.h"
@@ -2580,25 +2580,23 @@
// by modifying memory.
// The parameters don't really matter here.
std::string error_msg;
- IndirectReferenceTable irt(5,
- IndirectRefKind::kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
- IRTSegmentState old_state = irt.GetSegmentState();
+ jni::LocalReferenceTable lrt;
+ bool success = lrt.Initialize(/*max_count=*/ 5, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+ jni::LRTSegmentState old_state = lrt.GetSegmentState();
// Write some new state directly. We invert parts of old_state to ensure a new value.
- IRTSegmentState new_state;
+ jni::LRTSegmentState new_state;
new_state.top_index = old_state.top_index ^ 0x07705005;
ASSERT_NE(old_state.top_index, new_state.top_index);
- uint8_t* base = reinterpret_cast<uint8_t*>(&irt);
+ uint8_t* base = reinterpret_cast<uint8_t*>(&lrt);
int32_t segment_state_offset =
- IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
- *reinterpret_cast<IRTSegmentState*>(base + segment_state_offset) = new_state;
+ jni::LocalReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
+ *reinterpret_cast<jni::LRTSegmentState*>(base + segment_state_offset) = new_state;
// Read and compare.
- EXPECT_EQ(new_state.top_index, irt.GetSegmentState().top_index);
+ EXPECT_EQ(new_state.top_index, lrt.GetSegmentState().top_index);
}
// Test the offset computation of JNIEnvExt offsets. b/26071368.
@@ -2612,7 +2610,7 @@
// hope it to be.
uint32_t segment_state_now =
OFFSETOF_MEMBER(JNIEnvExt, locals_) +
- IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value();
+ jni::LocalReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value();
uint32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Uint32Value();
EXPECT_EQ(segment_state_now, segment_state_computed);
}
diff --git a/runtime/jni/local_reference_table-inl.h b/runtime/jni/local_reference_table-inl.h
new file mode 100644
index 0000000..b65dea7
--- /dev/null
+++ b/runtime/jni/local_reference_table-inl.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_
+#define ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_
+
+#include "local_reference_table.h"
+
+#include "android-base/stringprintf.h"
+
+#include "base/dumpable.h"
+#include "gc_root-inl.h"
+#include "obj_ptr-inl.h"
+#include "verify_object.h"
+
+namespace art {
+namespace mirror {
+class Object;
+} // namespace mirror
+
+namespace jni {
+
+// Verifies that the indirect table lookup is valid.
+// Returns "false" if something looks bad.
+inline bool LocalReferenceTable::IsValidReference(IndirectRef iref,
+ /*out*/std::string* error_msg) const {
+ DCHECK(iref != nullptr);
+ DCHECK_EQ(GetIndirectRefKind(iref), kLocal);
+ const uint32_t top_index = segment_state_.top_index;
+ uint32_t idx = ExtractIndex(iref);
+ if (UNLIKELY(idx >= top_index)) {
+ *error_msg = android::base::StringPrintf("deleted reference at index %u in a table of size %u",
+ idx,
+ top_index);
+ return false;
+ }
+ if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
+ *error_msg = android::base::StringPrintf("deleted reference at index %u", idx);
+ return false;
+ }
+ uint32_t iref_serial = DecodeSerial(reinterpret_cast<uintptr_t>(iref));
+ uint32_t entry_serial = table_[idx].GetSerial();
+ if (UNLIKELY(iref_serial != entry_serial)) {
+ *error_msg = android::base::StringPrintf("stale reference with serial number %u v. current %u",
+ iref_serial,
+ entry_serial);
+ return false;
+ }
+ return true;
+}
+
+// Make sure that the entry at "idx" is correctly paired with "iref".
+inline bool LocalReferenceTable::CheckEntry(const char* what,
+ IndirectRef iref,
+ uint32_t idx) const {
+ IndirectRef checkRef = ToIndirectRef(idx);
+ if (UNLIKELY(checkRef != iref)) {
+ std::string msg = android::base::StringPrintf(
+ "JNI ERROR (app bug): attempt to %s stale %s %p (should be %p)",
+ what,
+ GetIndirectRefKindString(kLocal),
+ iref,
+ checkRef);
+ AbortIfNoCheckJNI(msg);
+ return false;
+ }
+ return true;
+}
+
+template<ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Object> LocalReferenceTable::Get(IndirectRef iref) const {
+ DCHECK_EQ(GetIndirectRefKind(iref), kLocal);
+ uint32_t idx = ExtractIndex(iref);
+ DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
+ DCHECK(!table_[idx].GetReference()->IsNull());
+ ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
+ VerifyObject(obj);
+ return obj;
+}
+
+inline void LocalReferenceTable::Update(IndirectRef iref, ObjPtr<mirror::Object> obj) {
+ DCHECK_EQ(GetIndirectRefKind(iref), kLocal);
+ uint32_t idx = ExtractIndex(iref);
+ DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
+ DCHECK(!table_[idx].GetReference()->IsNull());
+ table_[idx].SetReference(obj);
+}
+
+inline void LrtEntry::Add(ObjPtr<mirror::Object> obj) {
+ ++serial_;
+ if (serial_ == kLRTMaxSerial) {
+ serial_ = 0;
+ }
+ reference_ = GcRoot<mirror::Object>(obj);
+}
+
+inline void LrtEntry::SetReference(ObjPtr<mirror::Object> obj) {
+ DCHECK_LT(serial_, kLRTMaxSerial);
+ reference_ = GcRoot<mirror::Object>(obj);
+}
+
+} // namespace jni
+} // namespace art
+
+#endif // ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_
diff --git a/runtime/jni/local_reference_table.cc b/runtime/jni/local_reference_table.cc
new file mode 100644
index 0000000..6cbbde7
--- /dev/null
+++ b/runtime/jni/local_reference_table.cc
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2022 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 "local_reference_table-inl.h"
+
+#include "base/globals.h"
+#include "base/mutator_locked_dumpable.h"
+#include "base/systrace.h"
+#include "base/utils.h"
+#include "indirect_reference_table.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
+#include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
+#include "reference_table.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+#include <cstdlib>
+
+namespace art {
+namespace jni {
+
+static constexpr bool kDumpStackOnNonLocalReference = false;
+static constexpr bool kDebugLRT = false;
+
+// Maximum table size we allow.
+static constexpr size_t kMaxTableSizeInBytes = 128 * MB;
+
+void LocalReferenceTable::AbortIfNoCheckJNI(const std::string& msg) {
+ // If -Xcheck:jni is on, it'll give a more detailed error before aborting.
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ if (!vm->IsCheckJniEnabled()) {
+ // Otherwise, we want to abort rather than hand back a bad reference.
+ LOG(FATAL) << msg;
+ } else {
+ LOG(ERROR) << msg;
+ }
+}
+
+// Mmap an "indirect ref table region. Table_bytes is a multiple of a page size.
+static inline MemMap NewLRTMap(size_t table_bytes, std::string* error_msg) {
+ MemMap result = MemMap::MapAnonymous("local ref table",
+ table_bytes,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb=*/ false,
+ error_msg);
+ if (!result.IsValid() && error_msg->empty()) {
+ *error_msg = "Unable to map memory for indirect ref table";
+ }
+ return result;
+}
+
+SmallLrtAllocator::SmallLrtAllocator()
+ : small_lrt_freelist_(nullptr), lock_("Small LRT table lock", LockLevel::kGenericBottomLock) {
+}
+
+// Allocate a LRT table for kSmallLrtEntries.
+LrtEntry* SmallLrtAllocator::Allocate(std::string* error_msg) {
+ MutexLock lock(Thread::Current(), lock_);
+ if (small_lrt_freelist_ == nullptr) {
+ // Refill.
+ MemMap map = NewLRTMap(kPageSize, error_msg);
+ if (map.IsValid()) {
+ small_lrt_freelist_ = reinterpret_cast<LrtEntry*>(map.Begin());
+ for (uint8_t* p = map.Begin(); p + kInitialLrtBytes < map.End(); p += kInitialLrtBytes) {
+ *reinterpret_cast<LrtEntry**>(p) = reinterpret_cast<LrtEntry*>(p + kInitialLrtBytes);
+ }
+ shared_lrt_maps_.emplace_back(std::move(map));
+ }
+ }
+ if (small_lrt_freelist_ == nullptr) {
+ return nullptr;
+ }
+ LrtEntry* result = small_lrt_freelist_;
+ small_lrt_freelist_ = *reinterpret_cast<LrtEntry**>(small_lrt_freelist_);
+ // Clear pointer in first entry.
+ new(result) LrtEntry();
+ return result;
+}
+
+void SmallLrtAllocator::Deallocate(LrtEntry* unneeded) {
+ MutexLock lock(Thread::Current(), lock_);
+ *reinterpret_cast<LrtEntry**>(unneeded) = small_lrt_freelist_;
+ small_lrt_freelist_ = unneeded;
+}
+
+LocalReferenceTable::LocalReferenceTable()
+ : segment_state_(kLRTFirstSegment),
+ table_(nullptr),
+ max_entries_(0u),
+ current_num_holes_(0) {
+}
+
+bool LocalReferenceTable::Initialize(size_t max_count, std::string* error_msg) {
+ CHECK(error_msg != nullptr);
+
+ // Overflow and maximum check.
+ CHECK_LE(max_count, kMaxTableSizeInBytes / sizeof(LrtEntry));
+
+ if (max_count <= kSmallLrtEntries) {
+ table_ = Runtime::Current()->GetSmallLrtAllocator()->Allocate(error_msg);
+ if (table_ != nullptr) {
+ max_entries_ = kSmallLrtEntries;
+ // table_mem_map_ remains invalid.
+ }
+ }
+ if (table_ == nullptr) {
+ const size_t table_bytes = RoundUp(max_count * sizeof(LrtEntry), kPageSize);
+ table_mem_map_ = NewLRTMap(table_bytes, error_msg);
+ if (!table_mem_map_.IsValid()) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+
+ table_ = reinterpret_cast<LrtEntry*>(table_mem_map_.Begin());
+ // Take into account the actual length.
+ max_entries_ = table_bytes / sizeof(LrtEntry);
+ }
+ segment_state_ = kLRTFirstSegment;
+ last_known_previous_state_ = kLRTFirstSegment;
+ return true;
+}
+
+LocalReferenceTable::~LocalReferenceTable() {
+ if (table_ != nullptr && !table_mem_map_.IsValid()) {
+ Runtime::Current()->GetSmallLrtAllocator()->Deallocate(table_);
+ }
+}
+
+void LocalReferenceTable::ConstexprChecks() {
+ // Use this for some assertions. They can't be put into the header as C++ wants the class
+ // to be complete.
+
+ // Check kind.
+ static_assert((EncodeIndirectRefKind(kLocal) & (~kKindMask)) == 0, "Kind encoding error");
+ static_assert((EncodeIndirectRefKind(kGlobal) & (~kKindMask)) == 0, "Kind encoding error");
+ static_assert((EncodeIndirectRefKind(kWeakGlobal) & (~kKindMask)) == 0, "Kind encoding error");
+ static_assert(DecodeIndirectRefKind(EncodeIndirectRefKind(kLocal)) == kLocal,
+ "Kind encoding error");
+ static_assert(DecodeIndirectRefKind(EncodeIndirectRefKind(kGlobal)) == kGlobal,
+ "Kind encoding error");
+ static_assert(DecodeIndirectRefKind(EncodeIndirectRefKind(kWeakGlobal)) == kWeakGlobal,
+ "Kind encoding error");
+
+ // Check serial.
+ static_assert(DecodeSerial(EncodeSerial(0u)) == 0u, "Serial encoding error");
+ static_assert(DecodeSerial(EncodeSerial(1u)) == 1u, "Serial encoding error");
+ static_assert(DecodeSerial(EncodeSerial(2u)) == 2u, "Serial encoding error");
+ static_assert(DecodeSerial(EncodeSerial(3u)) == 3u, "Serial encoding error");
+
+ // Table index.
+ static_assert(DecodeIndex(EncodeIndex(0u)) == 0u, "Index encoding error");
+ static_assert(DecodeIndex(EncodeIndex(1u)) == 1u, "Index encoding error");
+ static_assert(DecodeIndex(EncodeIndex(2u)) == 2u, "Index encoding error");
+ static_assert(DecodeIndex(EncodeIndex(3u)) == 3u, "Index encoding error");
+}
+
+bool LocalReferenceTable::IsValid() const {
+ return table_ != nullptr;
+}
+
+// Holes:
+//
+// To keep the LRT compact, we want to fill "holes" created by non-stack-discipline Add & Remove
+// operation sequences. For simplicity and lower memory overhead, we do not use a free list or
+// similar. Instead, we scan for holes, with the expectation that we will find holes fast as they
+// are usually near the end of the table (see the header, TODO: verify this assumption). To avoid
+// scans when there are no holes, the number of known holes should be tracked.
+//
+// A previous implementation stored the top index and the number of holes as the segment state.
+// This constraints the maximum number of references to 16-bit. We want to relax this, as it
+// is easy to require more references (e.g., to list all classes in large applications). Thus,
+// the implicitly stack-stored state, the LRTSegmentState, is only the top index.
+//
+// Thus, hole count is a local property of the current segment, and needs to be recovered when
+// (or after) a frame is pushed or popped. To keep JNI transitions simple (and inlineable), we
+// cannot do work when the segment changes. Thus, Add and Remove need to ensure the current
+// hole count is correct.
+//
+// To be able to detect segment changes, we require an additional local field that can describe
+// the known segment. This is last_known_previous_state_. The requirement will become clear with
+// the following (some non-trivial) cases that have to be supported:
+//
+// 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
+// 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
+// 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
+// reference
+// 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
+// 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
+// reference
+//
+// Storing the last known *previous* state (bottom index) allows conservatively detecting all the
+// segment changes above. The condition is simply that the last known state is greater than or
+// equal to the current previous state, and smaller than the current state (top index). The
+// condition is conservative as it adds O(1) overhead to operations on an empty segment.
+
+static size_t CountNullEntries(const LrtEntry* table, size_t from, size_t to) {
+ size_t count = 0;
+ for (size_t index = from; index != to; ++index) {
+ if (table[index].GetReference()->IsNull()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+void LocalReferenceTable::RecoverHoles(LRTSegmentState prev_state) {
+ if (last_known_previous_state_.top_index >= segment_state_.top_index ||
+ last_known_previous_state_.top_index < prev_state.top_index) {
+ const size_t top_index = segment_state_.top_index;
+ size_t count = CountNullEntries(table_, prev_state.top_index, top_index);
+
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ Recovered holes: "
+ << " Current prev=" << prev_state.top_index
+ << " Current top_index=" << top_index
+ << " Old num_holes=" << current_num_holes_
+ << " New num_holes=" << count;
+ }
+
+ current_num_holes_ = count;
+ last_known_previous_state_ = prev_state;
+ } else if (kDebugLRT) {
+ LOG(INFO) << "No need to recover holes";
+ }
+}
+
+ALWAYS_INLINE
+static inline void CheckHoleCount(LrtEntry* table,
+ size_t exp_num_holes,
+ LRTSegmentState prev_state,
+ LRTSegmentState cur_state) {
+ if (kIsDebugBuild) {
+ size_t count = CountNullEntries(table, prev_state.top_index, cur_state.top_index);
+ CHECK_EQ(exp_num_holes, count) << "prevState=" << prev_state.top_index
+ << " topIndex=" << cur_state.top_index;
+ }
+}
+
+bool LocalReferenceTable::Resize(size_t new_size, std::string* error_msg) {
+ CHECK_GT(new_size, max_entries_);
+
+ constexpr size_t kMaxEntries = kMaxTableSizeInBytes / sizeof(LrtEntry);
+ if (new_size > kMaxEntries) {
+ *error_msg = android::base::StringPrintf("Requested size exceeds maximum: %zu", new_size);
+ return false;
+ }
+ // Note: the above check also ensures that there is no overflow below.
+
+ const size_t table_bytes = RoundUp(new_size * sizeof(LrtEntry), kPageSize);
+
+ MemMap new_map = NewLRTMap(table_bytes, error_msg);
+ if (!new_map.IsValid()) {
+ return false;
+ }
+
+ memcpy(new_map.Begin(), table_, max_entries_ * sizeof(LrtEntry));
+ if (!table_mem_map_.IsValid()) {
+ // Didn't have its own map; deallocate old table.
+ Runtime::Current()->GetSmallLrtAllocator()->Deallocate(table_);
+ }
+ table_mem_map_ = std::move(new_map);
+ table_ = reinterpret_cast<LrtEntry*>(table_mem_map_.Begin());
+ const size_t real_new_size = table_bytes / sizeof(LrtEntry);
+ DCHECK_GE(real_new_size, new_size);
+ max_entries_ = real_new_size;
+
+ return true;
+}
+
+IndirectRef LocalReferenceTable::Add(LRTSegmentState previous_state,
+ ObjPtr<mirror::Object> obj,
+ std::string* error_msg) {
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ Add: previous_state=" << previous_state.top_index
+ << " top_index=" << segment_state_.top_index
+ << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ << " holes=" << current_num_holes_;
+ }
+
+ size_t top_index = segment_state_.top_index;
+
+ CHECK(obj != nullptr);
+ VerifyObject(obj);
+ DCHECK(table_ != nullptr);
+
+ if (top_index == max_entries_) {
+ // Try to double space.
+ if (std::numeric_limits<size_t>::max() / 2 < max_entries_) {
+ std::ostringstream oss;
+ oss << "JNI ERROR (app bug): " << kLocal << " table overflow "
+ << "(max=" << max_entries_ << ")" << std::endl
+ << MutatorLockedDumpable<LocalReferenceTable>(*this)
+ << " Resizing failed: exceeds size_t";
+ *error_msg = oss.str();
+ return nullptr;
+ }
+
+ std::string inner_error_msg;
+ if (!Resize(max_entries_ * 2, &inner_error_msg)) {
+ std::ostringstream oss;
+ oss << "JNI ERROR (app bug): " << kLocal << " table overflow "
+ << "(max=" << max_entries_ << ")" << std::endl
+ << MutatorLockedDumpable<LocalReferenceTable>(*this)
+ << " Resizing failed: " << inner_error_msg;
+ *error_msg = oss.str();
+ return nullptr;
+ }
+ }
+
+ RecoverHoles(previous_state);
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+
+ // We know there's enough room in the table. Now we just need to find
+ // the right spot. If there's a hole, find it and fill it; otherwise,
+ // add to the end of the list.
+ IndirectRef result;
+ size_t index;
+ if (current_num_holes_ > 0) {
+ DCHECK_GT(top_index, 1U);
+ // Find the first hole; likely to be near the end of the list.
+ LrtEntry* p_scan = &table_[top_index - 1];
+ DCHECK(!p_scan->GetReference()->IsNull());
+ --p_scan;
+ while (!p_scan->GetReference()->IsNull()) {
+ DCHECK_GE(p_scan, table_ + previous_state.top_index);
+ --p_scan;
+ }
+ index = p_scan - table_;
+ current_num_holes_--;
+ } else {
+ // Add to the end.
+ index = top_index++;
+ segment_state_.top_index = top_index;
+ }
+ table_[index].Add(obj);
+ result = ToIndirectRef(index);
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.top_index
+ << " holes=" << current_num_holes_;
+ }
+
+ DCHECK(result != nullptr);
+ return result;
+}
+
+void LocalReferenceTable::AssertEmpty() {
+ for (size_t i = 0; i < Capacity(); ++i) {
+ if (!table_[i].GetReference()->IsNull()) {
+ LOG(FATAL) << "Internal Error: non-empty local reference table\n"
+ << MutatorLockedDumpable<LocalReferenceTable>(*this);
+ UNREACHABLE();
+ }
+ }
+}
+
+// Removes an object. We extract the table offset bits from "iref"
+// and zap the corresponding entry, leaving a hole if it's not at the top.
+// If the entry is not between the current top index and the bottom index
+// specified by the cookie, we don't remove anything. This is the behavior
+// required by JNI's DeleteLocalRef function.
+// This method is not called when a local frame is popped; this is only used
+// for explicit single removals.
+// Returns "false" if nothing was removed.
+bool LocalReferenceTable::Remove(LRTSegmentState previous_state, IndirectRef iref) {
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ Remove: previous_state=" << previous_state.top_index
+ << " top_index=" << segment_state_.top_index
+ << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ << " holes=" << current_num_holes_;
+ }
+
+ const uint32_t top_index = segment_state_.top_index;
+ const uint32_t bottom_index = previous_state.top_index;
+
+ DCHECK(table_ != nullptr);
+
+ // TODO: We should eagerly check the ref kind against the `kLocal` kind instead of
+ // relying on this weak check and postponing the rest until `CheckEntry()` below.
+ // Passing the wrong kind shall currently result in misleading warnings.
+ if (GetIndirectRefKind(iref) == kJniTransition) {
+ auto* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ if (self->IsJniTransitionReference(reinterpret_cast<jobject>(iref))) {
+ auto* env = self->GetJniEnv();
+ DCHECK(env != nullptr);
+ if (env->IsCheckJniEnabled()) {
+ LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
+ if (kDumpStackOnNonLocalReference) {
+ self->Dump(LOG_STREAM(WARNING));
+ }
+ }
+ return true;
+ }
+ }
+
+ const uint32_t idx = ExtractIndex(iref);
+ if (idx < bottom_index) {
+ // Wrong segment.
+ LOG(WARNING) << "Attempt to remove index outside index area (" << idx
+ << " vs " << bottom_index << "-" << top_index << ")";
+ return false;
+ }
+ if (idx >= top_index) {
+ // Bad --- stale reference?
+ LOG(WARNING) << "Attempt to remove invalid index " << idx
+ << " (bottom=" << bottom_index << " top=" << top_index << ")";
+ return false;
+ }
+
+ RecoverHoles(previous_state);
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+
+ if (idx == top_index - 1) {
+ // Top-most entry. Scan up and consume holes.
+
+ if (!CheckEntry("remove", iref, idx)) {
+ return false;
+ }
+
+ *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
+ if (current_num_holes_ != 0) {
+ uint32_t collapse_top_index = top_index;
+ while (--collapse_top_index > bottom_index && current_num_holes_ != 0) {
+ if (kDebugLRT) {
+ ScopedObjectAccess soa(Thread::Current());
+ LOG(INFO) << "+++ checking for hole at " << collapse_top_index - 1
+ << " (previous_state=" << bottom_index << ") val="
+ << table_[collapse_top_index - 1].GetReference()->Read<kWithoutReadBarrier>();
+ }
+ if (!table_[collapse_top_index - 1].GetReference()->IsNull()) {
+ break;
+ }
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ ate hole at " << (collapse_top_index - 1);
+ }
+ current_num_holes_--;
+ }
+ segment_state_.top_index = collapse_top_index;
+
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ } else {
+ segment_state_.top_index = top_index - 1;
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ ate last entry " << top_index - 1;
+ }
+ }
+ } else {
+ // Not the top-most entry. This creates a hole. We null out the entry to prevent somebody
+ // from deleting it twice and screwing up the hole count.
+ if (table_[idx].GetReference()->IsNull()) {
+ LOG(INFO) << "--- WEIRD: removing null entry " << idx;
+ return false;
+ }
+ if (!CheckEntry("remove", iref, idx)) {
+ return false;
+ }
+
+ *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
+ current_num_holes_++;
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ left hole at " << idx << ", holes=" << current_num_holes_;
+ }
+ }
+
+ return true;
+}
+
+void LocalReferenceTable::Trim() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ if (!table_mem_map_.IsValid()) {
+ // Small table; nothing to do here.
+ return;
+ }
+ const size_t top_index = Capacity();
+ uint8_t* release_start = AlignUp(reinterpret_cast<uint8_t*>(&table_[top_index]), kPageSize);
+ uint8_t* release_end = static_cast<uint8_t*>(table_mem_map_.BaseEnd());
+ DCHECK_GE(reinterpret_cast<uintptr_t>(release_end), reinterpret_cast<uintptr_t>(release_start));
+ DCHECK_ALIGNED(release_end, kPageSize);
+ DCHECK_ALIGNED(release_end - release_start, kPageSize);
+ if (release_start != release_end) {
+ madvise(release_start, release_end - release_start, MADV_DONTNEED);
+ }
+}
+
+void LocalReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
+ BufferedRootVisitor<kDefaultBufferedRootCount> root_visitor(visitor, root_info);
+ for (size_t i = 0, capacity = Capacity(); i != capacity; ++i) {
+ GcRoot<mirror::Object>* ref = table_[i].GetReference();
+ if (!ref->IsNull()) {
+ root_visitor.VisitRoot(*ref);
+ DCHECK(!ref->IsNull());
+ }
+ }
+}
+
+void LocalReferenceTable::Dump(std::ostream& os) const {
+ os << kLocal << " table dump:\n";
+ ReferenceTable::Table entries;
+ for (size_t i = 0; i < Capacity(); ++i) {
+ ObjPtr<mirror::Object> obj = table_[i].GetReference()->Read<kWithoutReadBarrier>();
+ if (obj != nullptr) {
+ obj = table_[i].GetReference()->Read();
+ entries.push_back(GcRoot<mirror::Object>(obj));
+ }
+ }
+ ReferenceTable::Dump(os, entries);
+}
+
+void LocalReferenceTable::SetSegmentState(LRTSegmentState new_state) {
+ if (kDebugLRT) {
+ LOG(INFO) << "Setting segment state: "
+ << segment_state_.top_index
+ << " -> "
+ << new_state.top_index;
+ }
+ segment_state_ = new_state;
+}
+
+bool LocalReferenceTable::EnsureFreeCapacity(size_t free_capacity, std::string* error_msg) {
+ DCHECK_GE(free_capacity, static_cast<size_t>(1));
+ if (free_capacity > kMaxTableSizeInBytes) {
+ // Arithmetic might even overflow.
+ *error_msg = "Requested table size implausibly large";
+ return false;
+ }
+ size_t top_index = segment_state_.top_index;
+ if (top_index + free_capacity <= max_entries_) {
+ return true;
+ }
+
+ // Try to increase the table size.
+ if (!Resize(top_index + free_capacity, error_msg)) {
+ LOG(WARNING) << "JNI ERROR: Unable to reserve space in EnsureFreeCapacity (" << free_capacity
+ << "): " << std::endl
+ << MutatorLockedDumpable<LocalReferenceTable>(*this)
+ << " Resizing failed: " << *error_msg;
+ return false;
+ }
+ return true;
+}
+
+size_t LocalReferenceTable::FreeCapacity() const {
+ return max_entries_ - segment_state_.top_index;
+}
+
+} // namespace jni
+} // namespace art
diff --git a/runtime/jni/local_reference_table.h b/runtime/jni/local_reference_table.h
new file mode 100644
index 0000000..debaa8b
--- /dev/null
+++ b/runtime/jni/local_reference_table.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_H_
+#define ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <limits>
+#include <string>
+
+#include <android-base/logging.h>
+
+#include "base/bit_utils.h"
+#include "base/locks.h"
+#include "base/macros.h"
+#include "base/mem_map.h"
+#include "base/mutex.h"
+#include "gc_root.h"
+#include "indirect_reference_table.h"
+#include "obj_ptr.h"
+#include "offsets.h"
+#include "read_barrier_option.h"
+
+namespace art {
+
+class RootInfo;
+
+namespace mirror {
+class Object;
+} // namespace mirror
+
+namespace jni {
+
+// Maintain a table of local references. Used for local JNI references.
+// TODO: Rewrite the implementation, so that valid local references are effectively
+// `CompressedReference<Object>*`, so that it can be decoded very quickly.
+//
+// The table contains object references, where the strong (local/global) references are part of the
+// GC root set (but not the weak global references). When an object is added we return an
+// IndirectRef that is not a valid pointer but can be used to find the original value in O(1) time.
+// Conversions to and from indirect references are performed on upcalls and downcalls, so they need
+// to be very fast.
+//
+// To be efficient for JNI local variable storage, we need to provide operations that allow us to
+// operate on segments of the table, where segments are pushed and popped as if on a stack. For
+// example, deletion of an entry should only succeed if it appears in the current segment, and we
+// want to be able to strip off the current segment quickly when a method returns. Additions to the
+// table must be made in the current segment even if space is available in an earlier area.
+//
+// A new segment is created when we call into native code from interpreted code, or when we handle
+// the JNI PushLocalFrame function.
+//
+// The GC must be able to scan the entire table quickly.
+//
+// In summary, these must be very fast:
+// - adding or removing a segment
+// - adding references to a new segment
+// - converting an indirect reference back to an Object
+// These can be a little slower, but must still be pretty quick:
+// - adding references to a "mature" segment
+// - removing individual references
+// - scanning the entire table straight through
+//
+// If there's more than one segment, we don't guarantee that the table will fill completely before
+// we fail due to lack of space. We do ensure that the current segment will pack tightly, which
+// should satisfy JNI requirements (e.g. EnsureLocalCapacity).
+
+// Indirect reference definition. This must be interchangeable with JNI's jobject, and it's
+// convenient to let null be null, so we use void*.
+//
+// We need a (potentially) large table index and a 2-bit reference type (global, local, weak
+// global). We also reserve some bits to be used to detect stale indirect references: we put a
+// serial number in the extra bits, and keep a copy of the serial number in the table. This requires
+// more memory and additional memory accesses on add/get, but is moving-GC safe. It will catch
+// additional problems, e.g.: create iref1 for obj, delete iref1, create iref2 for same obj,
+// lookup iref1. A pattern based on object bits will miss this.
+
+// Table definition.
+//
+// For the global reference table, the expected common operations are adding a new entry and
+// removing a recently-added entry (usually the most-recently-added entry). For JNI local
+// references, the common operations are adding a new entry and removing an entire table segment.
+//
+// If we delete entries from the middle of the list, we will be left with "holes". We track the
+// number of holes so that, when adding new elements, we can quickly decide to do a trivial append
+// or go slot-hunting.
+//
+// When the top-most entry is removed, any holes immediately below it are also removed. Thus,
+// deletion of an entry may reduce "top_index" by more than one.
+//
+// To get the desired behavior for JNI locals, we need to know the bottom and top of the current
+// "segment". The top is managed internally, and the bottom is passed in as a function argument.
+// When we call a native method or push a local frame, the current top index gets pushed on, and
+// serves as the new bottom. When we pop a frame off, the value from the stack becomes the new top
+// index, and the value stored in the previous frame becomes the new bottom.
+//
+// Holes are being locally cached for the segment. Otherwise we'd have to pass bottom index and
+// number of holes, which restricts us to 16 bits for the top index. The value is cached within the
+// table. To avoid code in generated JNI transitions, which implicitly form segments, the code for
+// adding and removing references needs to detect the change of a segment. Helper fields are used
+// for this detection.
+//
+// Common alternative implementation: make IndirectRef a pointer to the actual reference slot.
+// Instead of getting a table and doing a lookup, the lookup can be done instantly. Operations like
+// determining the type and deleting the reference are more expensive because the table must be
+// hunted for (i.e. you have to do a pointer comparison to see which table it's in), you can't move
+// the table when expanding it (so realloc() is out), and tricks like serial number checking to
+// detect stale references aren't possible (though we may be able to get similar benefits with other
+// approaches).
+//
+// TODO: consider a "lastDeleteIndex" for quick hole-filling when an add immediately follows a
+// delete; must invalidate after segment pop might be worth only using it for JNI globals.
+//
+// TODO: may want completely different add/remove algorithms for global and local refs to improve
+// performance. A large circular buffer might reduce the amortized cost of adding global
+// references.
+
+// The state of the current segment. We only store the index. Splitting it for index and hole
+// count restricts the range too much.
+struct LRTSegmentState {
+ uint32_t top_index;
+};
+
+// Use as initial value for "cookie", and when table has only one segment.
+static constexpr LRTSegmentState kLRTFirstSegment = { 0 };
+
+// We associate a few bits of serial number with each reference, for error checking.
+static constexpr unsigned int kLRTSerialBits = 3;
+static constexpr uint32_t kLRTMaxSerial = ((1 << kLRTSerialBits) - 1);
+
+class LrtEntry {
+ public:
+ void Add(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ GcRoot<mirror::Object>* GetReference() {
+ DCHECK_LE(serial_, kLRTMaxSerial);
+ return &reference_;
+ }
+
+ const GcRoot<mirror::Object>* GetReference() const {
+ DCHECK_LE(serial_, kLRTMaxSerial);
+ return &reference_;
+ }
+
+ uint32_t GetSerial() const {
+ return serial_;
+ }
+
+ void SetReference(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ uint32_t serial_; // Incremented for each reuse; checked against reference.
+ GcRoot<mirror::Object> reference_;
+};
+static_assert(sizeof(LrtEntry) == 2 * sizeof(uint32_t), "Unexpected sizeof(LrtEntry)");
+static_assert(IsPowerOfTwo(sizeof(LrtEntry)), "Unexpected sizeof(LrtEntry)");
+
+// We initially allocate local reference tables with a very small number of entries, packing
+// multiple tables into a single page. If we need to expand one, we allocate them in units of
+// pages.
+// TODO: We should allocate all LRT tables as nonmovable Java objects, That in turn works better
+// if we break up each table into 2 parallel arrays, one for the Java reference, and one for the
+// serial number. The current scheme page-aligns regions containing LRT tables, and so allows them
+// to be identified and page-protected in the future.
+constexpr size_t kInitialLrtBytes = 512; // Number of bytes in an initial local table.
+constexpr size_t kSmallLrtEntries = kInitialLrtBytes / sizeof(LrtEntry);
+static_assert(kPageSize % kInitialLrtBytes == 0);
+static_assert(kInitialLrtBytes % sizeof(LrtEntry) == 0);
+static_assert(kInitialLrtBytes % sizeof(void *) == 0);
+
+// A minimal stopgap allocator for initial small local LRT tables.
+class SmallLrtAllocator {
+ public:
+ SmallLrtAllocator();
+
+ // Allocate a LRT table for kSmallLrtEntries.
+ LrtEntry* Allocate(std::string* error_msg) REQUIRES(!lock_);
+
+ void Deallocate(LrtEntry* unneeded) REQUIRES(!lock_);
+
+ private:
+ // A free list of kInitialLrtBytes chunks linked through the first word.
+ LrtEntry* small_lrt_freelist_;
+
+ // Repository of MemMaps used for small LRT tables.
+ std::vector<MemMap> shared_lrt_maps_;
+
+ Mutex lock_; // Level kGenericBottomLock; acquired before mem_map_lock_, which is a C++ mutex.
+};
+
+class LocalReferenceTable {
+ public:
+ // Constructs an uninitialized indirect reference table. Use `Initialize()` to initialize it.
+ LocalReferenceTable();
+
+ // Initialize the indirect reference table.
+ //
+ // Max_count is the minimum initial capacity (resizable).
+ // A value of 1 indicates an implementation-convenient small size.
+ bool Initialize(size_t max_count, std::string* error_msg);
+
+ ~LocalReferenceTable();
+
+ /*
+ * Checks whether construction of the LocalReferenceTable succeeded.
+ *
+ * This object must only be used if IsValid() returns true. It is safe to
+ * call IsValid from multiple threads without locking or other explicit
+ * synchronization.
+ */
+ bool IsValid() const;
+
+ // Add a new entry. "obj" must be a valid non-null object reference. This function will
+ // return null if an error happened (with an appropriate error message set).
+ IndirectRef Add(LRTSegmentState previous_state,
+ ObjPtr<mirror::Object> obj,
+ std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Given an IndirectRef in the table, return the Object it refers to.
+ //
+ // This function may abort under error conditions.
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ ObjPtr<mirror::Object> Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_)
+ ALWAYS_INLINE;
+
+ // Updates an existing indirect reference to point to a new object.
+ void Update(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Remove an existing entry.
+ //
+ // If the entry is not between the current top index and the bottom index
+ // specified by the cookie, we don't remove anything. This is the behavior
+ // required by JNI's DeleteLocalRef function.
+ //
+ // Returns "false" if nothing was removed.
+ bool Remove(LRTSegmentState previous_state, IndirectRef iref);
+
+ void AssertEmpty() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void Dump(std::ostream& os) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::alloc_tracker_lock_);
+
+ IndirectRefKind GetKind() const {
+ return kLocal;
+ }
+
+ // Return the #of entries in the entire table. This includes holes, and
+ // so may be larger than the actual number of "live" entries.
+ size_t Capacity() const {
+ return segment_state_.top_index;
+ }
+
+ // Return the number of non-null entries in the table. Only reliable for a
+ // single segment table.
+ int32_t NEntriesForGlobal() {
+ return segment_state_.top_index - current_num_holes_;
+ }
+
+ // Ensure that at least free_capacity elements are available, or return false.
+ // Caller ensures free_capacity > 0.
+ bool EnsureFreeCapacity(size_t free_capacity, std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // See implementation of EnsureFreeCapacity. We'll only state here how much is trivially free,
+ // without recovering holes. Thus this is a conservative estimate.
+ size_t FreeCapacity() const;
+
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ LRTSegmentState GetSegmentState() const {
+ return segment_state_;
+ }
+
+ void SetSegmentState(LRTSegmentState new_state);
+
+ static Offset SegmentStateOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
+ // Note: Currently segment_state_ is at offset 0. We're testing the expected value in
+ // jni_internal_test to make sure it stays correct. It is not OFFSETOF_MEMBER, as that
+ // is not pointer-size-safe.
+ return Offset(0);
+ }
+
+ // Release pages past the end of the table that may have previously held references.
+ void Trim() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Determine what kind of indirect reference this is. Opposite of EncodeIndirectRefKind.
+ ALWAYS_INLINE static inline IndirectRefKind GetIndirectRefKind(IndirectRef iref) {
+ return DecodeIndirectRefKind(reinterpret_cast<uintptr_t>(iref));
+ }
+
+ /* Reference validation for CheckJNI. */
+ bool IsValidReference(IndirectRef, /*out*/std::string* error_msg) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static constexpr uint32_t kShiftedSerialMask = (1u << kLRTSerialBits) - 1;
+
+ static constexpr size_t kKindBits = MinimumBitsToStore(
+ static_cast<uint32_t>(IndirectRefKind::kLastKind));
+ static constexpr uint32_t kKindMask = (1u << kKindBits) - 1;
+
+ static constexpr uintptr_t EncodeIndex(uint32_t table_index) {
+ static_assert(sizeof(IndirectRef) == sizeof(uintptr_t), "Unexpected IndirectRef size");
+ DCHECK_LE(MinimumBitsToStore(table_index), BitSizeOf<uintptr_t>() - kLRTSerialBits - kKindBits);
+ return (static_cast<uintptr_t>(table_index) << kKindBits << kLRTSerialBits);
+ }
+ static constexpr uint32_t DecodeIndex(uintptr_t uref) {
+ return static_cast<uint32_t>((uref >> kKindBits) >> kLRTSerialBits);
+ }
+
+ static constexpr uintptr_t EncodeIndirectRefKind(IndirectRefKind kind) {
+ return static_cast<uintptr_t>(kind);
+ }
+ static constexpr IndirectRefKind DecodeIndirectRefKind(uintptr_t uref) {
+ return static_cast<IndirectRefKind>(uref & kKindMask);
+ }
+
+ static constexpr uintptr_t EncodeSerial(uint32_t serial) {
+ DCHECK_LE(MinimumBitsToStore(serial), kLRTSerialBits);
+ return serial << kKindBits;
+ }
+ static constexpr uint32_t DecodeSerial(uintptr_t uref) {
+ return static_cast<uint32_t>(uref >> kKindBits) & kShiftedSerialMask;
+ }
+
+ constexpr uintptr_t EncodeIndirectRef(uint32_t table_index, uint32_t serial) const {
+ DCHECK_LT(table_index, max_entries_);
+ return EncodeIndex(table_index) | EncodeSerial(serial) | EncodeIndirectRefKind(kLocal);
+ }
+
+ static void ConstexprChecks();
+
+ // Extract the table index from an indirect reference.
+ ALWAYS_INLINE static uint32_t ExtractIndex(IndirectRef iref) {
+ return DecodeIndex(reinterpret_cast<uintptr_t>(iref));
+ }
+
+ IndirectRef ToIndirectRef(uint32_t table_index) const {
+ DCHECK_LT(table_index, max_entries_);
+ uint32_t serial = table_[table_index].GetSerial();
+ return reinterpret_cast<IndirectRef>(EncodeIndirectRef(table_index, serial));
+ }
+
+ // Resize the backing table to be at least new_size elements long. Currently
+ // must be larger than the current size. After return max_entries_ >= new_size.
+ bool Resize(size_t new_size, std::string* error_msg);
+
+ void RecoverHoles(LRTSegmentState from);
+
+ // Abort if check_jni is not enabled. Otherwise, just log as an error.
+ static void AbortIfNoCheckJNI(const std::string& msg);
+
+ /* extra debugging checks */
+ bool CheckEntry(const char*, IndirectRef, uint32_t) const;
+
+ /// semi-public - read/write by jni down calls.
+ LRTSegmentState segment_state_;
+
+ // Mem map where we store the indirect refs. If it's invalid, and table_ is non-null, then
+ // table_ is valid, but was allocated via `SmallLrtAllocator`;
+ MemMap table_mem_map_;
+ // bottom of the stack. Do not directly access the object references
+ // in this as they are roots. Use Get() that has a read barrier.
+ LrtEntry* table_;
+
+ // max #of entries allowed (modulo resizing).
+ size_t max_entries_;
+
+ // Some values to retain old behavior with holes. Description of the algorithm is in the .cc
+ // file.
+ // TODO: Consider other data structures for compact tables, e.g., free lists.
+ size_t current_num_holes_; // Number of holes in the current / top segment.
+ LRTSegmentState last_known_previous_state_;
+};
+
+} // namespace jni
+} // namespace art
+
+#endif // ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_H_
diff --git a/runtime/jni/local_reference_table_test.cc b/runtime/jni/local_reference_table_test.cc
new file mode 100644
index 0000000..84bb189
--- /dev/null
+++ b/runtime/jni/local_reference_table_test.cc
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2022 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 "indirect_reference_table-inl.h"
+
+#include "android-base/stringprintf.h"
+
+#include "class_linker-inl.h"
+#include "common_runtime_test.h"
+#include "mirror/class-alloc-inl.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+namespace jni {
+
+using android::base::StringPrintf;
+
+class LocalReferenceTableTest : public CommonRuntimeTest {
+ protected:
+ LocalReferenceTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
+
+static void CheckDump(LocalReferenceTable* lrt, size_t num_objects, size_t num_unique)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream oss;
+ lrt->Dump(oss);
+ if (num_objects == 0) {
+ EXPECT_EQ(oss.str().find("java.lang.Object"), std::string::npos) << oss.str();
+ } else if (num_objects == 1) {
+ EXPECT_NE(oss.str().find("1 of java.lang.Object"), std::string::npos) << oss.str();
+ } else {
+ EXPECT_NE(oss.str().find(StringPrintf("%zd of java.lang.Object (%zd unique instances)",
+ num_objects, num_unique)),
+ std::string::npos)
+ << "\n Expected number of objects: " << num_objects
+ << "\n Expected unique objects: " << num_unique << "\n"
+ << oss.str();
+ }
+}
+
+TEST_F(LocalReferenceTableTest, BasicTest) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
+ ScopedObjectAccess soa(Thread::Current());
+ static const size_t kTableMax = 20;
+ std::string error_msg;
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ StackHandleScope<5> hs(soa.Self());
+ Handle<mirror::Class> c =
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+ ASSERT_TRUE(c != nullptr);
+ Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj0 != nullptr);
+ Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj1 != nullptr);
+ Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj2 != nullptr);
+ Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj3 != nullptr);
+
+ const LRTSegmentState cookie = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = (IndirectRef) 0x11110;
+ EXPECT_FALSE(lrt.Remove(cookie, iref0)) << "unexpectedly successful removal";
+
+ // Add three, check, remove in the order in which they were added.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ IndirectRef iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ CheckDump(&lrt, 2, 2);
+ IndirectRef iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ CheckDump(&lrt, 3, 3);
+
+ EXPECT_OBJ_PTR_EQ(obj0.Get(), lrt.Get(iref0));
+ EXPECT_OBJ_PTR_EQ(obj1.Get(), lrt.Get(iref1));
+ EXPECT_OBJ_PTR_EQ(obj2.Get(), lrt.Get(iref2));
+
+ EXPECT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 2, 2);
+ EXPECT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 1, 1);
+ EXPECT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 0, 0);
+
+ // Table should be empty now.
+ EXPECT_EQ(0U, lrt.Capacity());
+
+ // Check that the entry off the end of the list is not valid.
+ // (CheckJNI shall abort for such entries.)
+ EXPECT_FALSE(lrt.IsValidReference(iref0, &error_msg));
+
+ // Add three, remove in the opposite order.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ CheckDump(&lrt, 3, 3);
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 2, 2);
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+
+ // Table should be empty now.
+ ASSERT_EQ(0U, lrt.Capacity());
+
+ // Add three, remove middle / middle / bottom / top. (Second attempt
+ // to remove middle should fail.)
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ CheckDump(&lrt, 3, 3);
+
+ ASSERT_EQ(3U, lrt.Capacity());
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 2, 2);
+ ASSERT_FALSE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 2, 2);
+
+ // Check that the reference to the hole is not valid.
+ EXPECT_FALSE(lrt.IsValidReference(iref1, &error_msg));
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+
+ // Table should be empty now.
+ ASSERT_EQ(0U, lrt.Capacity());
+
+ // Add four entries. Remove #1, add new entry, verify that table size
+ // is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify
+ // that we delete one and don't hole-compact the other.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ IndirectRef iref3 = lrt.Add(cookie, obj3.Get(), &error_msg);
+ EXPECT_TRUE(iref3 != nullptr);
+ CheckDump(&lrt, 4, 4);
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 3, 3);
+
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+
+ ASSERT_EQ(4U, lrt.Capacity()) << "hole not filled";
+ CheckDump(&lrt, 4, 4);
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 3, 3);
+ ASSERT_TRUE(lrt.Remove(cookie, iref3));
+ CheckDump(&lrt, 2, 2);
+
+ ASSERT_EQ(3U, lrt.Capacity()) << "should be 3 after two deletions";
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+
+ ASSERT_EQ(0U, lrt.Capacity()) << "not empty after split remove";
+
+ // Add an entry, remove it, add a new entry, and try to use the original
+ // iref. They have the same slot number but are for different objects.
+ // With the extended checks in place, this should fail.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_FALSE(lrt.Remove(cookie, iref0)) << "mismatched del succeeded";
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref1)) << "switched del failed";
+ ASSERT_EQ(0U, lrt.Capacity()) << "switching del not empty";
+ CheckDump(&lrt, 0, 0);
+
+ // Same as above, but with the same object. A more rigorous checker
+ // (e.g. with slot serialization) will catch this.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+ iref1 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ if (iref0 != iref1) {
+ // Try 0, should not work.
+ ASSERT_FALSE(lrt.Remove(cookie, iref0)) << "temporal del succeeded";
+ }
+ ASSERT_TRUE(lrt.Remove(cookie, iref1)) << "temporal cleanup failed";
+ ASSERT_EQ(0U, lrt.Capacity()) << "temporal del not empty";
+ CheckDump(&lrt, 0, 0);
+
+ // Stale reference is not valid.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ EXPECT_FALSE(lrt.IsValidReference(iref0, &error_msg)) << "stale lookup succeeded";
+ CheckDump(&lrt, 0, 0);
+
+ // Test table resizing.
+ // These ones fit...
+ static const size_t kTableInitial = kTableMax / 2;
+ IndirectRef manyRefs[kTableInitial];
+ for (size_t i = 0; i < kTableInitial; i++) {
+ manyRefs[i] = lrt.Add(cookie, obj0.Get(), &error_msg);
+ ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
+ CheckDump(&lrt, i + 1, 1);
+ }
+ // ...this one causes overflow.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ ASSERT_TRUE(iref0 != nullptr);
+ ASSERT_EQ(kTableInitial + 1, lrt.Capacity());
+ CheckDump(&lrt, kTableInitial + 1, 1);
+
+ for (size_t i = 0; i < kTableInitial; i++) {
+ ASSERT_TRUE(lrt.Remove(cookie, manyRefs[i])) << "failed removing " << i;
+ CheckDump(&lrt, kTableInitial - i, 1);
+ }
+ // Because of removal order, should have 11 entries, 10 of them holes.
+ ASSERT_EQ(kTableInitial + 1, lrt.Capacity());
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref0)) << "multi-remove final failed";
+
+ ASSERT_EQ(0U, lrt.Capacity()) << "multi-del not empty";
+ CheckDump(&lrt, 0, 0);
+}
+
+TEST_F(LocalReferenceTableTest, Holes) {
+ // Test the explicitly named cases from the LRT implementation:
+ //
+ // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
+ // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
+ // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
+ // reference
+ // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
+ // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
+ // reference
+
+ ScopedObjectAccess soa(Thread::Current());
+ static const size_t kTableMax = 10;
+
+ StackHandleScope<6> hs(soa.Self());
+ Handle<mirror::Class> c = hs.NewHandle(
+ class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+ ASSERT_TRUE(c != nullptr);
+ Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj0 != nullptr);
+ Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj1 != nullptr);
+ Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj2 != nullptr);
+ Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj3 != nullptr);
+ Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj4 != nullptr);
+
+ std::string error_msg;
+
+ // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference.
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+ IndirectRef iref1 = lrt.Add(cookie0, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie0, obj2.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie0, iref1));
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref3 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ // Must not have filled the previous hole.
+ EXPECT_EQ(lrt.Capacity(), 4u);
+ EXPECT_FALSE(lrt.IsValidReference(iref1, &error_msg));
+ CheckDump(&lrt, 3, 3);
+
+ UNUSED(iref0, iref1, iref2, iref3);
+ }
+
+ // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+ IndirectRef iref3 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref2));
+
+ // Pop segment.
+ lrt.SetSegmentState(cookie1);
+
+ IndirectRef iref4 = lrt.Add(cookie1, obj4.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 2u);
+ EXPECT_FALSE(lrt.IsValidReference(iref2, &error_msg));
+ CheckDump(&lrt, 2, 2);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4);
+ }
+
+ // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
+ // reference.
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref1));
+
+ // New segment.
+ const LRTSegmentState cookie2 = lrt.GetSegmentState();
+
+ IndirectRef iref3 = lrt.Add(cookie2, obj3.Get(), &error_msg);
+
+ // Pop segment.
+ lrt.SetSegmentState(cookie2);
+
+ IndirectRef iref4 = lrt.Add(cookie1, obj4.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 3u);
+ EXPECT_FALSE(lrt.IsValidReference(iref1, &error_msg));
+ CheckDump(&lrt, 3, 3);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4);
+ }
+
+ // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference.
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ EXPECT_TRUE(lrt.Remove(cookie1, iref1));
+
+ // Emptied segment, push new one.
+ const LRTSegmentState cookie2 = lrt.GetSegmentState();
+
+ IndirectRef iref2 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref3 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+ IndirectRef iref4 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref3));
+
+ // Pop segment.
+ UNUSED(cookie2);
+ lrt.SetSegmentState(cookie1);
+
+ IndirectRef iref5 = lrt.Add(cookie1, obj4.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 2u);
+ EXPECT_FALSE(lrt.IsValidReference(iref3, &error_msg));
+ CheckDump(&lrt, 2, 2);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4, iref5);
+ }
+
+ // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
+ // reference
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref3 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref2));
+
+ // Pop segment.
+ lrt.SetSegmentState(cookie1);
+
+ // Push segment.
+ const LRTSegmentState cookie1_second = lrt.GetSegmentState();
+ UNUSED(cookie1_second);
+
+ IndirectRef iref4 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 2u);
+ EXPECT_FALSE(lrt.IsValidReference(iref3, &error_msg));
+ CheckDump(&lrt, 2, 2);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4);
+ }
+}
+
+TEST_F(LocalReferenceTableTest, Resize) {
+ ScopedObjectAccess soa(Thread::Current());
+ static const size_t kTableMax = 512;
+
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> c = hs.NewHandle(
+ class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+ ASSERT_TRUE(c != nullptr);
+ Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj0 != nullptr);
+
+ std::string error_msg;
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ CheckDump(&lrt, 0, 0);
+ const LRTSegmentState cookie = kLRTFirstSegment;
+
+ for (size_t i = 0; i != kTableMax + 1; ++i) {
+ lrt.Add(cookie, obj0.Get(), &error_msg);
+ }
+
+ EXPECT_EQ(lrt.Capacity(), kTableMax + 1);
+}
+
+} // namespace jni
+} // namespace art
diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h
index f4cde68..c40af8a 100644
--- a/runtime/linear_alloc.h
+++ b/runtime/linear_alloc.h
@@ -64,7 +64,6 @@
std::ostream& operator<<(std::ostream& os, LinearAllocKind value);
-// TODO: Support freeing if we add class unloading.
class LinearAlloc {
public:
static constexpr size_t kAlignment = 8u;
diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h
index 678be8e..ca983ea 100644
--- a/runtime/managed_stack-inl.h
+++ b/runtime/managed_stack-inl.h
@@ -36,6 +36,7 @@
CHECK(top_shadow_frame_ != nullptr);
ShadowFrame* frame = top_shadow_frame_;
top_shadow_frame_ = frame->GetLink();
+ frame->ClearLink();
return frame;
}
diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h
index 04a27fe..0e7dfe3 100644
--- a/runtime/managed_stack.h
+++ b/runtime/managed_stack.h
@@ -43,6 +43,8 @@
// code.
class PACKED(4) ManagedStack {
public:
+ static size_t constexpr kTaggedJniSpMask = 0x3;
+
ManagedStack()
: tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)),
link_(nullptr),
@@ -75,8 +77,12 @@
return tagged_top_quick_frame_.GetSp();
}
- bool GetTopQuickFrameTag() const {
- return tagged_top_quick_frame_.GetTag();
+ bool GetTopQuickFrameGenericJniTag() const {
+ return tagged_top_quick_frame_.GetGenericJniTag();
+ }
+
+ bool GetTopQuickFrameJitJniTag() const {
+ return tagged_top_quick_frame_.GetJitJniTag();
}
bool HasTopQuickFrame() const {
@@ -89,10 +95,10 @@
tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top);
}
- void SetTopQuickFrameTagged(ArtMethod** top) {
+ void SetTopQuickFrameGenericJniTagged(ArtMethod** top) {
DCHECK(top_shadow_frame_ == nullptr);
DCHECK_ALIGNED(top, 4u);
- tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top);
+ tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateGenericJniTagged(top);
}
static constexpr size_t TaggedTopQuickFrameOffset() {
@@ -129,26 +135,30 @@
return TaggedTopQuickFrame(reinterpret_cast<uintptr_t>(sp));
}
- static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) {
+ static TaggedTopQuickFrame CreateGenericJniTagged(ArtMethod** sp) {
DCHECK_ALIGNED(sp, 4u);
return TaggedTopQuickFrame(reinterpret_cast<uintptr_t>(sp) | 1u);
}
// Get SP known to be not tagged and non-null.
ArtMethod** GetSpKnownNotTagged() const {
- DCHECK(!GetTag());
+ DCHECK(!GetGenericJniTag() && !GetJitJniTag());
DCHECK_NE(tagged_sp_, 0u);
return reinterpret_cast<ArtMethod**>(tagged_sp_);
}
ArtMethod** GetSp() const {
- return reinterpret_cast<ArtMethod**>(tagged_sp_ & ~static_cast<uintptr_t>(1u));
+ return reinterpret_cast<ArtMethod**>(tagged_sp_ & ~static_cast<uintptr_t>(kTaggedJniSpMask));
}
- bool GetTag() const {
+ bool GetGenericJniTag() const {
return (tagged_sp_ & 1u) != 0u;
}
+ bool GetJitJniTag() const {
+ return (tagged_sp_ & 2u) != 0u;
+ }
+
uintptr_t GetTaggedSp() const {
return tagged_sp_;
}
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 1327a24..c8c6ef9 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -81,7 +81,7 @@
ObjPtr<mirror::Class> GetBoxedPrimitiveClass(Primitive::Type type)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
- jmethodID m = nullptr;
+ ArtMethod* m = nullptr;
switch (type) {
#define CASE_PRIMITIVE(primitive, _, java_name, __) \
case primitive: \
@@ -93,7 +93,7 @@
case Primitive::Type::kPrimVoid:
return nullptr;
}
- return jni::DecodeArtMethod(m)->GetDeclaringClass();
+ return m->GetDeclaringClass();
}
bool GetUnboxedTypeAndValue(ObjPtr<mirror::Object> o, Primitive::Type* type, JValue* value)
@@ -290,6 +290,13 @@
return false;
}
+ ObjPtr<mirror::Class> from_obj_type = from_obj->GetClass();
+ Primitive::Type from_primitive_type;
+ if (!GetUnboxedPrimitiveType(from_obj_type, &from_primitive_type)) {
+ ThrowClassCastException(from, to);
+ return false;
+ }
+
Primitive::Type unboxed_type;
JValue unboxed_value;
if (UNLIKELY(!GetUnboxedTypeAndValue(from_obj, &unboxed_type, &unboxed_value))) {
@@ -393,7 +400,7 @@
const char* old_cause = self->StartAssertNoThreadSuspension("MethodHandleInvokeTransform");
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(kNumRegsForTransform, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
new_shadow_frame->SetVRegReference(0, method_handle.Get());
new_shadow_frame->SetVRegReference(1, sf.Get());
@@ -459,7 +466,7 @@
} else if (handle_kind == mirror::MethodHandle::Kind::kInvokeDirect) {
// String constructors are a special case, they are replaced with
// StringFactory methods.
- if (target_method->IsConstructor() && target_method->GetDeclaringClass()->IsStringClass()) {
+ if (target_method->IsStringConstructor()) {
DCHECK(handle_type->GetRType()->IsStringClass());
return WellKnownClasses::StringInitToStringFactory(target_method);
}
@@ -544,31 +551,30 @@
JValue& value) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
static const bool kTransaction = false; // Not in a transaction.
- static const bool kAssignabilityCheck = false; // No access check.
switch (field_type) {
case Primitive::kPrimBoolean:
return
- DoFieldPutCommon<Primitive::kPrimBoolean, kAssignabilityCheck, kTransaction>(
+ DoFieldPutCommon<Primitive::kPrimBoolean, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimByte:
- return DoFieldPutCommon<Primitive::kPrimByte, kAssignabilityCheck, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimByte, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimChar:
- return DoFieldPutCommon<Primitive::kPrimChar, kAssignabilityCheck, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimChar, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimShort:
- return DoFieldPutCommon<Primitive::kPrimShort, kAssignabilityCheck, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimShort, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimInt:
case Primitive::kPrimFloat:
- return DoFieldPutCommon<Primitive::kPrimInt, kAssignabilityCheck, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimInt, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimLong:
case Primitive::kPrimDouble:
- return DoFieldPutCommon<Primitive::kPrimLong, kAssignabilityCheck, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimLong, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimNot:
- return DoFieldPutCommon<Primitive::kPrimNot, kAssignabilityCheck, kTransaction>(
+ return DoFieldPutCommon<Primitive::kPrimNot, kTransaction>(
self, shadow_frame, obj, field, value);
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -625,6 +631,10 @@
case mirror::MethodHandle::kInstanceGet: {
size_t obj_reg = operands->GetOperand(0);
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
+ if (obj == nullptr) {
+ ThrowNullPointerException("Receiver is null");
+ return false;
+ }
MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result);
return true;
}
@@ -648,6 +658,10 @@
callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(),
value_reg);
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
+ if (obj == nullptr) {
+ ThrowNullPointerException("Receiver is null");
+ return false;
+ }
return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value);
}
case mirror::MethodHandle::kStaticPut: {
@@ -769,7 +783,7 @@
const char* old_cause = self->StartAssertNoThreadSuspension("DoMethodHandleInvokeMethod");
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(num_regs, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
CopyArgumentsFromCallerFrame(shadow_frame, new_shadow_frame, operands, first_dest_reg);
self->EndAssertNoThreadSuspension(old_cause);
@@ -856,19 +870,13 @@
if (atc == nullptr || !callsite_type->IsExactMatch(atc->GetMethodType())) {
// Cached asType adapter does not exist or is for another call site. Call
// MethodHandle::asType() to get an appropriate adapter.
- ArtMethod* as_type =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_asType);
- uint32_t as_type_args[] = {
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_handle.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(callsite_type.Get()))};
- JValue atc_result;
- as_type->Invoke(self, as_type_args, sizeof(as_type_args), &atc_result, "LL");
- if (atc_result.GetL() == nullptr) {
+ ArtMethod* as_type = WellKnownClasses::java_lang_invoke_MethodHandle_asType;
+ ObjPtr<mirror::MethodHandle> atc_method_handle = ObjPtr<mirror::MethodHandle>::DownCast(
+ as_type->InvokeVirtual<'L', 'L'>(self, method_handle.Get(), callsite_type.Get()));
+ if (atc_method_handle == nullptr) {
DCHECK(self->IsExceptionPending());
return false;
}
- ObjPtr<mirror::MethodHandle> atc_method_handle =
- down_cast<mirror::MethodHandle*>(atc_result.GetL());
atc.Assign(atc_method_handle);
DCHECK(!atc.IsNull());
}
@@ -909,10 +917,9 @@
const uint16_t num_vregs = callsite_type->NumberOfVRegs();
const char* old_cause = self->StartAssertNoThreadSuspension("EmulatedStackFrame to ShadowFrame");
- ArtMethod* invoke_exact =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+ ArtMethod* invoke_exact = WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
ShadowFrameAllocaUniquePtr shadow_frame =
- CREATE_SHADOW_FRAME(num_vregs, /*link*/ nullptr, invoke_exact, /*dex_pc*/ 0);
+ CREATE_SHADOW_FRAME(num_vregs, invoke_exact, /*dex_pc*/ 0);
emulated_frame->WriteToShadowFrame(self, callsite_type, 0, shadow_frame.get());
self->EndAssertNoThreadSuspension(old_cause);
diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc
index eb3f2ad..588f861 100644
--- a/runtime/method_handles_test.cc
+++ b/runtime/method_handles_test.cc
@@ -71,7 +71,12 @@
}
} // namespace
-class MethodHandlesTest : public CommonRuntimeTest {};
+class MethodHandlesTest : public CommonRuntimeTest {
+ protected:
+ MethodHandlesTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
//
// Primitive -> Primitive Conversions
@@ -342,7 +347,7 @@
value.SetL(cl->FindPrimitiveClass('V'));
ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
ASSERT_TRUE(soa.Self()->IsExceptionPending());
- ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ ASSERT_TRUE(IsClassCastException(soa.Self()->GetException()));
soa.Self()->ClearException();
}
diff --git a/runtime/metrics/reporter_test.cc b/runtime/metrics/reporter_test.cc
index 4b078db..848a74e 100644
--- a/runtime/metrics/reporter_test.cc
+++ b/runtime/metrics/reporter_test.cc
@@ -171,9 +171,9 @@
CompilationReason reason = CompilationReason::kUnknown) {
// TODO: we should iterate through all the other metrics to make sure they were not
// reported. However, we don't have an easy to use iteration mechanism over metrics yet.
- // We should ads one
+ // We should add one
ASSERT_EQ(backend_->GetReports().size(), size);
- for (auto report : backend_->GetReports()) {
+ for (const TestBackend::Report& report : backend_->GetReports()) {
ASSERT_EQ(report.data.Get(DatumId::kClassVerificationCount), with_metrics ? 2u : 0u);
ASSERT_EQ(report.data.Get(DatumId::kJitMethodCompileCount), with_metrics ? 1u : 0u);
}
@@ -217,7 +217,7 @@
WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 50);
VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
- // We still should not report at period.
+ // We should still not report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
@@ -238,11 +238,11 @@
WaitForReport(/*report_count=*/ 2, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 2, /*with_metrics*/ true);
- // We should not longer report at period.
+ // We should no longer report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
-// LARGE TEST: This takes take 2s to run.
+// LARGE TEST: This test take 2s to run.
// Verifies startup reporting, followed by continuous reporting.
TEST_F(MetricsReporterTest, StartupAndPeriodContinuous) {
SetupReporter("S,1,*");
@@ -259,7 +259,7 @@
WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
- // We should keep reporting at period.
+ // We should keep reporting continuously.
ASSERT_TRUE(ShouldContinueReporting());
}
@@ -279,11 +279,11 @@
WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
- // We should not longer report at period.
+ // We should no longer report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
-// LARGE TEST: This takes take 5s to run.
+// LARGE TEST: This test takes 5s to run.
// Verifies a sequence of reporting, at different interval of times.
TEST_F(MetricsReporterTest, PeriodContinuous) {
SetupReporter("1,2,*");
@@ -300,7 +300,7 @@
WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
- // We should keep reporting at period.
+ // We should keep reporting continuously.
ASSERT_TRUE(ShouldContinueReporting());
}
@@ -320,13 +320,13 @@
WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 1, /*with_metrics*/ false);
- // We should not longer report at period.
+ // We should no longer report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
// Verify we don't start reporting if the sample rate is set to 0.
TEST_F(MetricsReporterTest, SampleRateDisable) {
- SetupReporter("1", /*session_id=*/ 1, /*reporting_mods=*/ 0);
+ SetupReporter("1,*", /*session_id=*/ 1, /*reporting_mods=*/ 0);
// The background thread should not start.
ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
@@ -338,7 +338,7 @@
// Verify we don't start reporting if the sample rate is low and the session does
// not meet conditions.
TEST_F(MetricsReporterTest, SampleRateDisable24) {
- SetupReporter("1", /*session_id=*/ 125, /*reporting_mods=*/ 24);
+ SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 24);
// The background thread should not start.
ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
@@ -350,9 +350,9 @@
// Verify we start reporting if the sample rate and the session meet
// reporting conditions
TEST_F(MetricsReporterTest, SampleRateEnable50) {
- SetupReporter("1", /*session_id=*/ 125, /*reporting_mods=*/ 50);
+ SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 50);
- // The background thread should not start.
+ // The background thread should start.
ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
ASSERT_FALSE(ShouldReportAtStartup());
@@ -362,7 +362,7 @@
// Verify we start reporting if the sample rate and the session meet
// reporting conditions
TEST_F(MetricsReporterTest, SampleRateEnableAll) {
- SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
+ SetupReporter("1,*", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
// The background thread should start.
ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
@@ -408,7 +408,7 @@
const std::string& spec_str,
bool startup_first,
bool continuous,
- std::vector<uint32_t> periods) {
+ const std::vector<uint32_t>& periods) {
Verify(spec_str, true, startup_first, continuous, periods);
}
@@ -417,7 +417,7 @@
bool valid,
bool startup_first,
bool continuous,
- std::vector<uint32_t> periods) {
+ const std::vector<uint32_t>& periods) {
std::string error_msg;
std::optional<ReportingPeriodSpec> spec = ReportingPeriodSpec::Parse(spec_str, &error_msg);
diff --git a/runtime/metrics/statsd.cc b/runtime/metrics/statsd.cc
index 034c039..7002f22 100644
--- a/runtime/metrics/statsd.cc
+++ b/runtime/metrics/statsd.cc
@@ -271,6 +271,9 @@
return statsd::ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_CMDLINE;
case CompilationReason::kVdex:
return statsd::ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_VDEX;
+ case CompilationReason::kBootAfterMainlineUpdate:
+ return statsd::
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT_AFTER_MAINLINE_UPDATE;
}
}
@@ -282,6 +285,8 @@
return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM;
case InstructionSet::kArm64:
return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM64;
+ case InstructionSet::kRiscv64:
+ return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_RISCV64;
case InstructionSet::kX86:
return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_X86;
case InstructionSet::kX86_64:
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index f2ed3b6..2bdf827 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -97,7 +97,7 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteArray(this, i, GetWithoutChecks(i));
}
- DCHECK(CheckIsValidIndex<kVerifyFlags>(i));
+ DCHECK(CheckIsValidIndex<kVerifyFlags>(i)) << i << " " << GetLength<kVerifyFlags>();
GetData()[i] = value;
}
// Backward copy where elements are of aligned appropriately for T. Count is in T sized units.
@@ -240,13 +240,16 @@
// C style casts here since we sometimes have T be a pointer, or sometimes an integer
// (for stack traces).
using ConversionType = typename std::conditional_t<std::is_pointer_v<T>, uintptr_t, T>;
+ // Note: we cast the array directly when unchecked as this code gets called by
+ // runtime_image, which can pass a 64bit pointer and therefore cannot be held
+ // by an ObjPtr.
if (kPointerSize == PointerSize::k64) {
uint64_t value =
- static_cast<uint64_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+ static_cast<uint64_t>(reinterpret_cast<LongArray*>(this)->GetWithoutChecks(idx));
return (T) dchecked_integral_cast<ConversionType>(value);
} else {
uint32_t value =
- static_cast<uint32_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+ static_cast<uint32_t>(reinterpret_cast<IntArray*>(this)->GetWithoutChecks(idx));
return (T) dchecked_integral_cast<ConversionType>(value);
}
}
@@ -261,12 +264,15 @@
template<bool kTransactionActive, bool kCheckTransaction, bool kUnchecked>
inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) {
+ // Note: we cast the array directly when unchecked as this code gets called by
+ // runtime_image, which can pass a 64bit pointer and therefore cannot be held
+ // by an ObjPtr.
if (ptr_size == PointerSize::k64) {
- (kUnchecked ? ObjPtr<LongArray>::DownCast(ObjPtr<Object>(this)) : AsLongArray())->
+ (kUnchecked ? reinterpret_cast<LongArray*>(this) : AsLongArray().Ptr())->
SetWithoutChecks<kTransactionActive, kCheckTransaction>(idx, element);
} else {
uint32_t element32 = dchecked_integral_cast<uint32_t>(element);
- (kUnchecked ? ObjPtr<IntArray>::DownCast(ObjPtr<Object>(this)) : AsIntArray())
+ (kUnchecked ? reinterpret_cast<IntArray*>(this) : AsIntArray().Ptr())
->SetWithoutChecks<kTransactionActive, kCheckTransaction>(idx, element32);
}
}
@@ -278,7 +284,7 @@
}
template <VerifyObjectFlags kVerifyFlags, typename Visitor>
-inline void PointerArray::Fixup(ObjPtr<mirror::PointerArray> dest,
+inline void PointerArray::Fixup(mirror::PointerArray* dest,
PointerSize pointer_size,
const Visitor& visitor) {
for (size_t i = 0, count = GetLength(); i < count; ++i) {
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index dfe7d47..0116fde 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -264,7 +264,7 @@
// Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies
// to dest if visitor(source_ptr) != source_ptr.
template <VerifyObjectFlags kVerifyFlags = kVerifyNone, typename Visitor>
- void Fixup(ObjPtr<mirror::PointerArray> dest, PointerSize pointer_size, const Visitor& visitor)
+ void Fixup(mirror::PointerArray* dest, PointerSize pointer_size, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
// Works like memcpy(), except we guarantee not to allow tearing of array values (ie using smaller
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 77f78c5..b6b1415 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -498,47 +498,6 @@
return false;
}
-template <bool throw_on_failure>
-inline bool Class::ResolvedMethodAccessTest(ObjPtr<Class> access_to,
- ArtMethod* method,
- ObjPtr<DexCache> dex_cache,
- uint32_t method_idx,
- InvokeType throw_invoke_type) {
- DCHECK(throw_on_failure || throw_invoke_type == kStatic);
- DCHECK(dex_cache != nullptr);
- if (UNLIKELY(!this->CanAccess(access_to))) {
- // The referrer class can't access the method's declaring class but may still be able
- // to access the method if the MethodId specifies an accessible subclass of the declaring
- // class rather than the declaring class itself.
- dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
- // The referenced class has already been resolved with the method, but may not be in the dex
- // cache.
- ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
- class_idx,
- dex_cache,
- GetClassLoader());
- DCHECK(dex_access_to != nullptr)
- << " Could not resolve " << dex_cache->GetDexFile()->StringByTypeIdx(class_idx)
- << " when checking access to " << method->PrettyMethod() << " from " << PrettyDescriptor();
- if (UNLIKELY(!this->CanAccess(dex_access_to))) {
- if (throw_on_failure) {
- ThrowIllegalAccessErrorClassForMethodDispatch(this,
- dex_access_to,
- method,
- throw_invoke_type);
- }
- return false;
- }
- }
- if (LIKELY(this->CanAccessMember(access_to, method->GetAccessFlags()))) {
- return true;
- }
- if (throw_on_failure) {
- ThrowIllegalAccessErrorMethod(this, method);
- }
- return false;
-}
-
inline bool Class::CanAccessResolvedField(ObjPtr<Class> access_to,
ArtField* field,
ObjPtr<DexCache> dex_cache,
@@ -553,22 +512,6 @@
return ResolvedFieldAccessTest<true>(access_to, field, dex_cache, field_idx);
}
-inline bool Class::CanAccessResolvedMethod(ObjPtr<Class> access_to,
- ArtMethod* method,
- ObjPtr<DexCache> dex_cache,
- uint32_t method_idx) {
- return ResolvedMethodAccessTest<false>(access_to, method, dex_cache, method_idx, kStatic);
-}
-
-inline bool Class::CheckResolvedMethodAccess(ObjPtr<Class> access_to,
- ArtMethod* method,
- ObjPtr<DexCache> dex_cache,
- uint32_t method_idx,
- InvokeType throw_invoke_type) {
- return ResolvedMethodAccessTest<true>(
- access_to, method, dex_cache, method_idx, throw_invoke_type);
-}
-
inline bool Class::IsObsoleteVersionOf(ObjPtr<Class> klass) {
DCHECK(!klass->IsObsoleteObject()) << klass->PrettyClass() << " is obsolete!";
if (LIKELY(!IsObsoleteObject())) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 37a4197..6458613 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -27,6 +27,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "base/logging.h" // For VLOG.
+#include "base/sdk_version.h"
#include "base/utils.h"
#include "class-inl.h"
#include "class_ext-inl.h"
@@ -59,10 +60,6 @@
namespace art {
-// TODO: move to own CC file?
-constexpr size_t BitString::kBitSizeAtPosition[BitString::kCapacity];
-constexpr size_t BitString::kCapacity;
-
namespace mirror {
using android::base::StringPrintf;
@@ -1436,7 +1433,7 @@
void Class::ClearSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.ClearSkipAccessChecks();
}
}
@@ -1445,7 +1442,7 @@
void Class::ClearMustCountLocksFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.ClearMustCountLocks();
}
}
@@ -1454,7 +1451,7 @@
void Class::ClearDontCompileFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.ClearDontCompile();
}
}
@@ -1463,7 +1460,7 @@
void Class::SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.SetSkipAccessChecks();
}
}
@@ -2132,6 +2129,19 @@
return res;
}
+bool Class::CheckIsVisibleWithTargetSdk(Thread* self) {
+ uint32_t targetSdkVersion = Runtime::Current()->GetTargetSdkVersion();
+ if (IsSdkVersionSetAndAtMost(targetSdkVersion, SdkVersion::kT)) {
+ ObjPtr<mirror::Class> java_lang_ClassValue =
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_ClassValue);
+ if (this == java_lang_ClassValue.Ptr()) {
+ self->ThrowNewException("Ljava/lang/ClassNotFoundException;", "java.lang.ClassValue");
+ return false;
+ }
+ }
+ return true;
+}
+
ArtMethod* Class::FindAccessibleInterfaceMethod(ArtMethod* implementation_method,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d1ac97f..b9eb9d0 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -65,6 +65,7 @@
template<size_t kNumReferences> class PACKED(4) StackHandleScope;
class Thread;
class DexCacheVisitor;
+class RuntimeImageHelper;
namespace mirror {
@@ -237,6 +238,15 @@
// Set access flags, recording the change if running inside a Transaction.
void SetAccessFlags(uint32_t new_access_flags) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetInBootImageAndNotInPreloadedClasses() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t flags = GetAccessFlags();
+ SetAccessFlags(flags | kAccInBootImageAndNotInPreloadedClasses);
+ }
+
+ ALWAYS_INLINE bool IsInBootImageAndNotInPreloadedClasses() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccInBootImageAndNotInPreloadedClasses) != 0;
+ }
+
// Returns true if the class is an enum.
ALWAYS_INLINE bool IsEnum() REQUIRES_SHARED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccEnum) != 0;
@@ -556,7 +566,7 @@
// The size of java.lang.Class.class.
static uint32_t ClassClassSize(PointerSize pointer_size) {
// The number of vtable entries in java.lang.Class.
- uint32_t vtable_entries = Object::kVTableLength + 67;
+ uint32_t vtable_entries = Object::kVTableLength + 73;
return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
}
@@ -573,6 +583,9 @@
static constexpr MemberOffset ObjectSizeAllocFastPathOffset() {
return OFFSET_OF_OBJECT_MEMBER(Class, object_size_alloc_fast_path_);
}
+ static constexpr MemberOffset ClinitThreadIdOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_);
+ }
ALWAYS_INLINE void SetObjectSize(uint32_t new_object_size) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -626,21 +639,6 @@
uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Can this class access a resolved method?
- // Note that access to methods's class is checked and this may require looking up the class
- // referenced by the MethodId in the DexFile in case the declaring class is inaccessible.
- bool CanAccessResolvedMethod(ObjPtr<Class> access_to,
- ArtMethod* resolved_method,
- ObjPtr<DexCache> dex_cache,
- uint32_t method_idx)
- REQUIRES_SHARED(Locks::mutator_lock_);
- bool CheckResolvedMethodAccess(ObjPtr<Class> access_to,
- ArtMethod* resolved_method,
- ObjPtr<DexCache> dex_cache,
- uint32_t method_idx,
- InvokeType throw_invoke_type)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
bool IsSubClass(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
// Can src be assigned to this class? For example, String can be assigned to Object (by an
@@ -1359,6 +1357,13 @@
size_t GetMethodIdOffset(ArtMethod* method, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns whether the class should be visible to an app.
+ // Notorious example is java.lang.ClassValue, which was added in Android U and proguarding tools
+ // used that as justification to remove computeValue method implementation. Such an app running
+ // on U+ will fail with AbstractMethodError as computeValue is not implemented.
+ // See b/259501764.
+ bool CheckIsVisibleWithTargetSdk(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
template <typename T, VerifyObjectFlags kVerifyFlags, typename Visitor>
void FixupNativePointer(
@@ -1379,14 +1384,6 @@
uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <bool throw_on_failure>
- bool ResolvedMethodAccessTest(ObjPtr<Class> access_to,
- ArtMethod* resolved_method,
- ObjPtr<DexCache> dex_cache,
- uint32_t method_idx,
- InvokeType throw_invoke_type)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
bool IsArrayAssignableFromArray(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
bool IsAssignableFromArray(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1429,7 +1426,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
// 'Class' Object Fields
- // Order governed by java field ordering. See art::ClassLinker::LinkFields.
+ // Order governed by java field ordering. See art::ClassLinker::LinkFieldsHelper::LinkFields.
// Defining class loader, or null for the "bootstrap" system loader.
HeapReference<ClassLoader> class_loader_;
@@ -1583,6 +1580,7 @@
friend struct art::ClassOffsets; // for verifying offset information
friend class Object; // For VisitReferences
friend class linker::ImageWriter; // For SetStatusInternal
+ friend class art::RuntimeImageHelper; // For SetStatusInternal
DISALLOW_IMPLICIT_CONSTRUCTORS(Class);
};
diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h
index 1aae151..b025eb2 100644
--- a/runtime/mirror/class_ext.h
+++ b/runtime/mirror/class_ext.h
@@ -170,6 +170,10 @@
bool EnsureJniIdsArrayPresent(MemberOffset off, size_t count)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Backing store of user-defined values pertaining to a class.
+ // Maintained by the ClassValue class.
+ HeapReference<Object> class_value_map_;
+
// The saved error for this class being erroneous.
HeapReference<Throwable> erroneous_state_error_;
@@ -195,9 +199,10 @@
// classes sfields_ array or '0' if no id has been assigned to that field yet.
HeapReference<PointerArray> static_jfield_ids_;
+ int32_t pre_redefine_class_def_index_;
+
// Native pointer to DexFile and ClassDef index of this class before it was JVMTI-redefined.
int64_t pre_redefine_dex_file_ptr_;
- int32_t pre_redefine_class_def_index_;
friend struct art::ClassExtOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ClassExt);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 402bb72..3452f17 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -49,28 +49,28 @@
}
template<typename T>
-static void InitializeArray(GcRoot<T>*) {
- // No special initialization is needed.
+static void InitializeArray(T*) {
+ // Nothing to do.
}
-template<typename T, size_t kMaxCacheSize>
-T* DexCache::AllocArray(MemberOffset obj_offset,
- MemberOffset num_offset,
- size_t num,
- LinearAllocKind kind) {
- num = std::min<size_t>(num, kMaxCacheSize);
- if (num == 0) {
- return nullptr;
- }
+template<typename T>
+T* DexCache::AllocArray(MemberOffset obj_offset, size_t num, LinearAllocKind kind, bool startup) {
+ Thread* self = Thread::Current();
mirror::DexCache* dex_cache = this;
- if (gUseReadBarrier && Thread::Current()->GetIsGcMarking()) {
+ if (gUseReadBarrier && self->GetIsGcMarking()) {
// Several code paths use DexCache without read-barrier for performance.
// We have to check the "to-space" object here to avoid allocating twice.
- dex_cache = reinterpret_cast<DexCache*>(ReadBarrier::Mark(dex_cache));
+ dex_cache = reinterpret_cast<DexCache*>(ReadBarrier::Mark(this));
}
- Thread* self = Thread::Current();
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- LinearAlloc* alloc = linker->GetOrCreateAllocatorForClassLoader(GetClassLoader());
+ // DON'T USE 'this' from now on.
+ Runtime* runtime = Runtime::Current();
+ // Note: in the 1002-notify-startup test, the startup linear alloc can become null
+ // concurrently, even if the runtime is marked at startup. Therefore we should only
+ // fetch it once here.
+ LinearAlloc* startup_linear_alloc = runtime->GetStartupLinearAlloc();
+ LinearAlloc* alloc = (startup && startup_linear_alloc != nullptr)
+ ? startup_linear_alloc
+ : runtime->GetClassLinker()->GetOrCreateAllocatorForClassLoader(GetClassLoader());
MutexLock mu(self, *Locks::dex_cache_lock_); // Avoid allocation by multiple threads.
T* array = dex_cache->GetFieldPtr64<T*>(obj_offset);
if (array != nullptr) {
@@ -79,7 +79,6 @@
}
array = reinterpret_cast<T*>(alloc->AllocAlign16(self, RoundUp(num * sizeof(T), 16), kind));
InitializeArray(array); // Ensure other threads see the array initialized.
- dex_cache->SetField32Volatile<false, false>(num_offset, num);
dex_cache->SetField64Volatile<false, false>(obj_offset, reinterpret_cast64<uint64_t>(array));
return array;
}
@@ -89,14 +88,6 @@
: object(object), index(index) {}
template <typename T>
-inline void DexCachePair<T>::Initialize(std::atomic<DexCachePair<T>>* dex_cache) {
- DexCachePair<T> first_elem;
- first_elem.object = GcRoot<T>(nullptr);
- first_elem.index = InvalidIndexForSlot(0);
- dex_cache[0].store(first_elem, std::memory_order_relaxed);
-}
-
-template <typename T>
inline T* DexCachePair<T>::GetObjectForIndex(uint32_t idx) {
if (idx != index) {
return nullptr;
@@ -106,11 +97,33 @@
}
template <typename T>
+inline void DexCachePair<T>::Initialize(std::atomic<DexCachePair<T>>* dex_cache) {
+ DexCachePair<T> first_elem;
+ first_elem.object = GcRoot<T>(nullptr);
+ first_elem.index = InvalidIndexForSlot(0);
+ dex_cache[0].store(first_elem, std::memory_order_relaxed);
+}
+
+template <typename T>
inline void NativeDexCachePair<T>::Initialize(std::atomic<NativeDexCachePair<T>>* dex_cache) {
NativeDexCachePair<T> first_elem;
first_elem.object = nullptr;
first_elem.index = InvalidIndexForSlot(0);
- DexCache::SetNativePair(dex_cache, 0, first_elem);
+
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(dex_cache);
+ AtomicPair<uintptr_t> v(reinterpret_cast<size_t>(first_elem.object), first_elem.index);
+ AtomicPairStoreRelease(&array[0], v);
+}
+
+template <typename T>
+inline void GcRootArray<T>::Set(uint32_t index, T* value) {
+ GcRoot<T> root(value);
+ entries_[index] = root;
+}
+
+template <typename T>
+inline T* GcRootArray<T>::Get(uint32_t index) {
+ return entries_[index].Read();
}
inline uint32_t DexCache::ClassSize(PointerSize pointer_size) {
@@ -118,34 +131,13 @@
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
}
-inline uint32_t DexCache::StringSlotIndex(dex::StringIndex string_idx) {
- DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
- const uint32_t slot_idx = string_idx.index_ % kDexCacheStringCacheSize;
- DCHECK_LT(slot_idx, NumStrings());
- return slot_idx;
-}
-
inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
- StringDexCacheType* strings = GetStrings();
- if (UNLIKELY(strings == nullptr)) {
- return nullptr;
- }
- return strings[StringSlotIndex(string_idx)].load(
- std::memory_order_relaxed).GetObjectForIndex(string_idx.index_);
+ return GetStringsEntry(string_idx.index_);
}
inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
DCHECK(resolved != nullptr);
- StringDexCacheType* strings = GetStrings();
- if (UNLIKELY(strings == nullptr)) {
- strings = AllocArray<StringDexCacheType, kDexCacheStringCacheSize>(
- StringsOffset(),
- NumStringsOffset(),
- GetDexFile()->NumStringIds(),
- LinearAllocKind::kDexCacheArray);
- }
- strings[StringSlotIndex(string_idx)].store(
- StringDexCachePair(resolved, string_idx.index_), std::memory_order_relaxed);
+ SetStringsEntry(string_idx.index_, resolved.Ptr());
Runtime* const runtime = Runtime::Current();
if (UNLIKELY(runtime->IsActiveTransaction())) {
DCHECK(runtime->IsAotCompiler());
@@ -157,102 +149,42 @@
inline void DexCache::ClearString(dex::StringIndex string_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
- uint32_t slot_idx = StringSlotIndex(string_idx);
- StringDexCacheType* strings = GetStrings();
+ auto* array = GetStringsArray();
+ if (array != nullptr) {
+ array->Set(string_idx.index_, nullptr);
+ }
+ auto* strings = GetStrings();
if (UNLIKELY(strings == nullptr)) {
return;
}
- StringDexCacheType* slot = &strings[slot_idx];
- // This is racy but should only be called from the transactional interpreter.
- if (slot->load(std::memory_order_relaxed).index == string_idx.index_) {
- StringDexCachePair cleared(nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx));
- slot->store(cleared, std::memory_order_relaxed);
- }
-}
-
-inline uint32_t DexCache::TypeSlotIndex(dex::TypeIndex type_idx) {
- DCHECK_LT(type_idx.index_, GetDexFile()->NumTypeIds());
- const uint32_t slot_idx = type_idx.index_ % kDexCacheTypeCacheSize;
- DCHECK_LT(slot_idx, NumResolvedTypes());
- return slot_idx;
+ strings->Clear(string_idx.index_);
}
inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
- // It is theorized that a load acquire is not required since obtaining the resolved class will
- // always have an address dependency or a lock.
- TypeDexCacheType* resolved_types = GetResolvedTypes();
- if (UNLIKELY(resolved_types == nullptr)) {
- return nullptr;
- }
- return resolved_types[TypeSlotIndex(type_idx)].load(
- std::memory_order_relaxed).GetObjectForIndex(type_idx.index_);
-}
-
-inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
- DCHECK(resolved != nullptr);
- DCHECK(resolved->IsResolved()) << resolved->GetStatus();
- TypeDexCacheType* resolved_types = GetResolvedTypes();
- if (UNLIKELY(resolved_types == nullptr)) {
- resolved_types = AllocArray<TypeDexCacheType, kDexCacheTypeCacheSize>(
- ResolvedTypesOffset(),
- NumResolvedTypesOffset(),
- GetDexFile()->NumTypeIds(),
- LinearAllocKind::kDexCacheArray);
- }
- // TODO default transaction support.
- // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
- // class but not necessarily seeing the loaded members like the static fields array.
- // See b/32075261.
- resolved_types[TypeSlotIndex(type_idx)].store(
- TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release);
- // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
- WriteBarrier::ForEveryFieldWrite(this);
+ return GetResolvedTypesEntry(type_idx.index_);
}
inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
- TypeDexCacheType* resolved_types = GetResolvedTypes();
+ auto* array = GetResolvedTypesArray();
+ if (array != nullptr) {
+ array->Set(type_idx.index_, nullptr);
+ }
+ auto* resolved_types = GetResolvedTypes();
if (UNLIKELY(resolved_types == nullptr)) {
return;
}
- uint32_t slot_idx = TypeSlotIndex(type_idx);
- TypeDexCacheType* slot = &resolved_types[slot_idx];
- // This is racy but should only be called from the single-threaded ImageWriter and tests.
- if (slot->load(std::memory_order_relaxed).index == type_idx.index_) {
- TypeDexCachePair cleared(nullptr, TypeDexCachePair::InvalidIndexForSlot(slot_idx));
- slot->store(cleared, std::memory_order_relaxed);
- }
-}
-
-inline uint32_t DexCache::MethodTypeSlotIndex(dex::ProtoIndex proto_idx) {
- DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- DCHECK_LT(proto_idx.index_, GetDexFile()->NumProtoIds());
- const uint32_t slot_idx = proto_idx.index_ % kDexCacheMethodTypeCacheSize;
- DCHECK_LT(slot_idx, NumResolvedMethodTypes());
- return slot_idx;
+ resolved_types->Clear(type_idx.index_);
}
inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) {
- MethodTypeDexCacheType* methods = GetResolvedMethodTypes();
- if (UNLIKELY(methods == nullptr)) {
- return nullptr;
- }
- return methods[MethodTypeSlotIndex(proto_idx)].load(
- std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_);
+ return GetResolvedMethodTypesEntry(proto_idx.index_);
}
inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) {
DCHECK(resolved != nullptr);
- MethodTypeDexCacheType* methods = GetResolvedMethodTypes();
- if (UNLIKELY(methods == nullptr)) {
- methods = AllocArray<MethodTypeDexCacheType, kDexCacheMethodTypeCacheSize>(
- ResolvedMethodTypesOffset(),
- NumResolvedMethodTypesOffset(),
- GetDexFile()->NumProtoIds(),
- LinearAllocKind::kDexCacheArray);
- }
- methods[MethodTypeSlotIndex(proto_idx)].store(
- MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed);
+ SetResolvedMethodTypesEntry(proto_idx.index_, resolved);
+
Runtime* const runtime = Runtime::Current();
if (UNLIKELY(runtime->IsActiveTransaction())) {
DCHECK(runtime->IsAotCompiler());
@@ -264,24 +196,25 @@
inline void DexCache::ClearMethodType(dex::ProtoIndex proto_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
- uint32_t slot_idx = MethodTypeSlotIndex(proto_idx);
- MethodTypeDexCacheType* slot = &GetResolvedMethodTypes()[slot_idx];
- // This is racy but should only be called from the transactional interpreter.
- if (slot->load(std::memory_order_relaxed).index == proto_idx.index_) {
- MethodTypeDexCachePair cleared(nullptr,
- MethodTypeDexCachePair::InvalidIndexForSlot(proto_idx.index_));
- slot->store(cleared, std::memory_order_relaxed);
+ auto* array = GetResolvedMethodTypesArray();
+ if (array != nullptr) {
+ array->Set(proto_idx.index_, nullptr);
}
+ auto* methods = GetResolvedMethodTypes();
+ if (methods == nullptr) {
+ return;
+ }
+ methods->Clear(proto_idx.index_);
}
inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
- GcRoot<CallSite>* call_sites = GetResolvedCallSites();
+ GcRootArray<CallSite>* call_sites = GetResolvedCallSites();
if (UNLIKELY(call_sites == nullptr)) {
return nullptr;
}
- GcRoot<mirror::CallSite>& target = call_sites[call_site_idx];
+ GcRoot<mirror::CallSite>& target = call_sites->GetGcRoot(call_site_idx);
Atomic<GcRoot<mirror::CallSite>>& ref =
reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
return ref.load(std::memory_order_seq_cst).Read();
@@ -294,15 +227,11 @@
GcRoot<mirror::CallSite> null_call_site(nullptr);
GcRoot<mirror::CallSite> candidate(call_site);
- GcRoot<CallSite>* call_sites = GetResolvedCallSites();
+ GcRootArray<CallSite>* call_sites = GetResolvedCallSites();
if (UNLIKELY(call_sites == nullptr)) {
- call_sites = AllocArray<GcRoot<CallSite>, std::numeric_limits<size_t>::max()>(
- ResolvedCallSitesOffset(),
- NumResolvedCallSitesOffset(),
- GetDexFile()->NumCallSiteIds(),
- LinearAllocKind::kGCRootArray);
+ call_sites = AllocateResolvedCallSites();
}
- GcRoot<mirror::CallSite>& target = call_sites[call_site_idx];
+ GcRoot<mirror::CallSite>& target = call_sites->GetGcRoot(call_site_idx);
// The first assignment for a given call site wins.
Atomic<GcRoot<mirror::CallSite>>& ref =
@@ -316,103 +245,42 @@
}
}
-inline uint32_t DexCache::FieldSlotIndex(uint32_t field_idx) {
- DCHECK_LT(field_idx, GetDexFile()->NumFieldIds());
- const uint32_t slot_idx = field_idx % kDexCacheFieldCacheSize;
- DCHECK_LT(slot_idx, NumResolvedFields());
- return slot_idx;
-}
-
inline ArtField* DexCache::GetResolvedField(uint32_t field_idx) {
- FieldDexCacheType* fields = GetResolvedFields();
- if (UNLIKELY(fields == nullptr)) {
- return nullptr;
- }
- auto pair = GetNativePair(fields, FieldSlotIndex(field_idx));
- return pair.GetObjectForIndex(field_idx);
+ return GetResolvedFieldsEntry(field_idx);
}
inline void DexCache::SetResolvedField(uint32_t field_idx, ArtField* field) {
- DCHECK(field != nullptr);
- FieldDexCachePair pair(field, field_idx);
- FieldDexCacheType* fields = GetResolvedFields();
- if (UNLIKELY(fields == nullptr)) {
- fields = AllocArray<FieldDexCacheType, kDexCacheFieldCacheSize>(
- ResolvedFieldsOffset(),
- NumResolvedFieldsOffset(),
- GetDexFile()->NumFieldIds(),
- LinearAllocKind::kNoGCRoots);
- }
- SetNativePair(fields, FieldSlotIndex(field_idx), pair);
-}
-
-inline uint32_t DexCache::MethodSlotIndex(uint32_t method_idx) {
- DCHECK_LT(method_idx, GetDexFile()->NumMethodIds());
- const uint32_t slot_idx = method_idx % kDexCacheMethodCacheSize;
- DCHECK_LT(slot_idx, NumResolvedMethods());
- return slot_idx;
+ SetResolvedFieldsEntry(field_idx, field);
}
inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx) {
- MethodDexCacheType* methods = GetResolvedMethods();
- if (UNLIKELY(methods == nullptr)) {
- return nullptr;
- }
- auto pair = GetNativePair(methods, MethodSlotIndex(method_idx));
- return pair.GetObjectForIndex(method_idx);
+ return GetResolvedMethodsEntry(method_idx);
}
inline void DexCache::SetResolvedMethod(uint32_t method_idx, ArtMethod* method) {
- DCHECK(method != nullptr);
- MethodDexCachePair pair(method, method_idx);
- MethodDexCacheType* methods = GetResolvedMethods();
- if (UNLIKELY(methods == nullptr)) {
- methods = AllocArray<MethodDexCacheType, kDexCacheMethodCacheSize>(
- ResolvedMethodsOffset(),
- NumResolvedMethodsOffset(),
- GetDexFile()->NumMethodIds(),
- LinearAllocKind::kNoGCRoots);
- }
- SetNativePair(methods, MethodSlotIndex(method_idx), pair);
+ SetResolvedMethodsEntry(method_idx, method);
}
-template <typename T>
-NativeDexCachePair<T> DexCache::GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx) {
- auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
- AtomicPair<uintptr_t> value = AtomicPairLoadAcquire(&array[idx]);
- return NativeDexCachePair<T>(reinterpret_cast<T*>(value.first), value.second);
-}
-
-template <typename T>
-void DexCache::SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx,
- NativeDexCachePair<T> pair) {
- auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
- AtomicPair<uintptr_t> v(reinterpret_cast<size_t>(pair.object), pair.index);
- AtomicPairStoreRelease(&array[idx], v);
-}
-
-template <typename T,
- ReadBarrierOption kReadBarrierOption,
- typename Visitor>
-inline void VisitDexCachePairs(std::atomic<DexCachePair<T>>* pairs,
+template <ReadBarrierOption kReadBarrierOption,
+ typename Visitor,
+ typename T>
+inline void VisitDexCachePairs(T* array,
size_t num_pairs,
const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
// Check both the data pointer and count since the array might be initialized
// concurrently on other thread, and we might observe just one of the values.
- for (size_t i = 0; pairs != nullptr && i < num_pairs; ++i) {
- DexCachePair<T> source = pairs[i].load(std::memory_order_relaxed);
+ for (size_t i = 0; array != nullptr && i < num_pairs; ++i) {
+ auto source = array->GetPair(i);
// NOTE: We need the "template" keyword here to avoid a compilation
// failure. GcRoot<T> is a template argument-dependent type and we need to
// tell the compiler to treat "Read" as a template rather than a field or
// function. Otherwise, on encountering the "<" token, the compiler would
// treat "Read" as a field.
- T* const before = source.object.template Read<kReadBarrierOption>();
+ auto const before = source.object.template Read<kReadBarrierOption>();
visitor.VisitRootIfNonNull(source.object.AddressWithoutBarrier());
if (source.object.template Read<kReadBarrierOption>() != before) {
- pairs[i].store(source, std::memory_order_relaxed);
+ array->SetPair(i, source);
}
}
}
@@ -443,19 +311,38 @@
ReadBarrierOption kReadBarrierOption,
typename Visitor>
inline void DexCache::VisitNativeRoots(const Visitor& visitor) {
- VisitDexCachePairs<String, kReadBarrierOption, Visitor>(
+ VisitDexCachePairs<kReadBarrierOption, Visitor>(
GetStrings<kVerifyFlags>(), NumStrings<kVerifyFlags>(), visitor);
- VisitDexCachePairs<Class, kReadBarrierOption, Visitor>(
+ VisitDexCachePairs<kReadBarrierOption, Visitor>(
GetResolvedTypes<kVerifyFlags>(), NumResolvedTypes<kVerifyFlags>(), visitor);
- VisitDexCachePairs<MethodType, kReadBarrierOption, Visitor>(
+ VisitDexCachePairs<kReadBarrierOption, Visitor>(
GetResolvedMethodTypes<kVerifyFlags>(), NumResolvedMethodTypes<kVerifyFlags>(), visitor);
- GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites<kVerifyFlags>();
+ GcRootArray<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites<kVerifyFlags>();
size_t num_call_sites = NumResolvedCallSites<kVerifyFlags>();
for (size_t i = 0; resolved_call_sites != nullptr && i != num_call_sites; ++i) {
- visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
+ visitor.VisitRootIfNonNull(resolved_call_sites->GetGcRoot(i).AddressWithoutBarrier());
+ }
+
+ GcRootArray<mirror::Class>* resolved_types = GetResolvedTypesArray<kVerifyFlags>();
+ size_t num_resolved_types = NumResolvedTypesArray<kVerifyFlags>();
+ for (size_t i = 0; resolved_types != nullptr && i != num_resolved_types; ++i) {
+ visitor.VisitRootIfNonNull(resolved_types->GetGcRoot(i).AddressWithoutBarrier());
+ }
+
+ GcRootArray<mirror::String>* resolved_strings = GetStringsArray<kVerifyFlags>();
+ size_t num_resolved_strings = NumStringsArray<kVerifyFlags>();
+ for (size_t i = 0; resolved_strings != nullptr && i != num_resolved_strings; ++i) {
+ visitor.VisitRootIfNonNull(resolved_strings->GetGcRoot(i).AddressWithoutBarrier());
+ }
+
+ GcRootArray<mirror::MethodType>* resolved_method_types =
+ GetResolvedMethodTypesArray<kVerifyFlags>();
+ size_t num_resolved_method_types = NumResolvedMethodTypesArray<kVerifyFlags>();
+ for (size_t i = 0; resolved_method_types != nullptr && i != num_resolved_method_types; ++i) {
+ visitor.VisitRootIfNonNull(resolved_method_types->GetGcRoot(i).AddressWithoutBarrier());
}
}
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index b7f8ee7..b0b6012 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -20,6 +20,7 @@
#include "class_linker.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/heap.h"
+#include "jit/profile_saver.h"
#include "linear_alloc.h"
#include "oat_file.h"
#include "object-inl.h"
@@ -44,6 +45,12 @@
DCHECK(GetResolvedMethodTypes() == nullptr);
DCHECK(GetResolvedCallSites() == nullptr);
+ DCHECK(GetStringsArray() == nullptr);
+ DCHECK(GetResolvedTypesArray() == nullptr);
+ DCHECK(GetResolvedMethodsArray() == nullptr);
+ DCHECK(GetResolvedFieldsArray() == nullptr);
+ DCHECK(GetResolvedMethodTypesArray() == nullptr);
+
ScopedAssertNoThreadSuspension sants(__FUNCTION__);
SetDexFile(dex_file);
@@ -52,48 +59,81 @@
void DexCache::VisitReflectiveTargets(ReflectiveValueVisitor* visitor) {
bool wrote = false;
- FieldDexCacheType* fields = GetResolvedFields();
+ auto* fields = GetResolvedFields();
size_t num_fields = NumResolvedFields();
// Check both the data pointer and count since the array might be initialized
// concurrently on other thread, and we might observe just one of the values.
for (size_t i = 0; fields != nullptr && i < num_fields; i++) {
- auto pair(GetNativePair(fields, i));
- if (pair.index == FieldDexCachePair::InvalidIndexForSlot(i)) {
+ auto pair(fields->GetNativePair(i));
+ if (pair.index == NativeDexCachePair<ArtField>::InvalidIndexForSlot(i)) {
continue;
}
ArtField* new_val = visitor->VisitField(
pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedField, pair.index, this));
if (UNLIKELY(new_val != pair.object)) {
if (new_val == nullptr) {
- pair = FieldDexCachePair(nullptr, FieldDexCachePair::InvalidIndexForSlot(i));
+ pair = NativeDexCachePair<ArtField>(
+ nullptr, NativeDexCachePair<ArtField>::InvalidIndexForSlot(i));
} else {
pair.object = new_val;
}
- SetNativePair(fields, i, pair);
+ fields->SetNativePair(i, pair);
wrote = true;
}
}
- MethodDexCacheType* methods = GetResolvedMethods();
+ auto* methods = GetResolvedMethods();
size_t num_methods = NumResolvedMethods();
// Check both the data pointer and count since the array might be initialized
// concurrently on other thread, and we might observe just one of the values.
for (size_t i = 0; methods != nullptr && i < num_methods; i++) {
- auto pair(GetNativePair(methods, i));
- if (pair.index == MethodDexCachePair::InvalidIndexForSlot(i)) {
+ auto pair(methods->GetNativePair(i));
+ if (pair.index == NativeDexCachePair<ArtMethod>::InvalidIndexForSlot(i)) {
continue;
}
ArtMethod* new_val = visitor->VisitMethod(
pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedMethod, pair.index, this));
if (UNLIKELY(new_val != pair.object)) {
if (new_val == nullptr) {
- pair = MethodDexCachePair(nullptr, MethodDexCachePair::InvalidIndexForSlot(i));
+ pair = NativeDexCachePair<ArtMethod>(
+ nullptr, NativeDexCachePair<ArtMethod>::InvalidIndexForSlot(i));
} else {
pair.object = new_val;
}
- SetNativePair(methods, i, pair);
+ methods->SetNativePair(i, pair);
wrote = true;
}
}
+
+ auto* fields_array = GetResolvedFieldsArray();
+ num_fields = NumResolvedFieldsArray();
+ for (size_t i = 0; fields_array != nullptr && i < num_fields; i++) {
+ ArtField* old_val = fields_array->Get(i);
+ if (old_val == nullptr) {
+ continue;
+ }
+ ArtField* new_val = visitor->VisitField(
+ old_val, DexCacheSourceInfo(kSourceDexCacheResolvedField, i, this));
+ if (new_val != old_val) {
+ fields_array->Set(i, new_val);
+ wrote = true;
+ }
+ }
+
+ auto* methods_array = GetResolvedMethodsArray();
+ num_methods = NumResolvedMethodsArray();
+ for (size_t i = 0; methods_array != nullptr && i < num_methods; i++) {
+ ArtMethod* old_val = methods_array->Get(i);
+ if (old_val == nullptr) {
+ continue;
+ }
+ ArtMethod* new_val = visitor->VisitMethod(
+ old_val, DexCacheSourceInfo(kSourceDexCacheResolvedMethod, i, this));
+ if (new_val != old_val) {
+ methods_array->Set(i, new_val);
+ wrote = true;
+ }
+ }
+
if (wrote) {
WriteBarrier::ForEveryFieldWrite(this);
}
@@ -106,12 +146,12 @@
SetResolvedFields(nullptr);
SetResolvedMethodTypes(nullptr);
SetResolvedCallSites(nullptr);
- SetField32<false>(NumStringsOffset(), 0);
- SetField32<false>(NumResolvedTypesOffset(), 0);
- SetField32<false>(NumResolvedMethodsOffset(), 0);
- SetField32<false>(NumResolvedFieldsOffset(), 0);
- SetField32<false>(NumResolvedMethodTypesOffset(), 0);
- SetField32<false>(NumResolvedCallSitesOffset(), 0);
+
+ SetStringsArray(nullptr);
+ SetResolvedTypesArray(nullptr);
+ SetResolvedMethodsArray(nullptr);
+ SetResolvedFieldsArray(nullptr);
+ SetResolvedMethodTypesArray(nullptr);
}
void DexCache::SetLocation(ObjPtr<mirror::String> location) {
@@ -126,5 +166,95 @@
return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(DexCache, class_loader_));
}
+bool DexCache::ShouldAllocateFullArrayAtStartup() {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsAotCompiler()) {
+ // To save on memory in dex2oat, we don't allocate full arrays by default.
+ return false;
+ }
+
+ if (runtime->GetStartupCompleted()) {
+ // We only allocate full arrays during app startup.
+ return false;
+ }
+
+ if (GetClassLoader() == nullptr) {
+ // Only allocate full array for app dex files (also note that for
+ // multi-image, the `GetCompilerFilter` call below does not work for
+ // non-primary oat files).
+ return false;
+ }
+
+ const OatDexFile* oat_dex_file = GetDexFile()->GetOatDexFile();
+ if (oat_dex_file != nullptr &&
+ CompilerFilter::IsAotCompilationEnabled(oat_dex_file->GetOatFile()->GetCompilerFilter())) {
+ // We only allocate full arrays for dex files where we do not have
+ // compilation.
+ return false;
+ }
+
+ if (!ProfileSaver::IsStarted()) {
+ // Only allocate full arrays if the profile saver is running: if the app
+ // does not call `reportFullyDrawn`, then only the profile saver will notify
+ // that the app has eventually started.
+ return false;
+ }
+
+ return true;
+}
+
+void DexCache::UnlinkStartupCaches() {
+ if (GetDexFile() == nullptr) {
+ // Unused dex cache.
+ return;
+ }
+ UnlinkStringsArrayIfStartup();
+ UnlinkResolvedFieldsArrayIfStartup();
+ UnlinkResolvedMethodsArrayIfStartup();
+ UnlinkResolvedTypesArrayIfStartup();
+ UnlinkResolvedMethodTypesArrayIfStartup();
+}
+
+void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
+ DCHECK(resolved != nullptr);
+ DCHECK(resolved->IsResolved()) << resolved->GetStatus();
+ // TODO default transaction support.
+ // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
+ // class but not necessarily seeing the loaded members like the static fields array.
+ // See b/32075261.
+ SetResolvedTypesEntry(type_idx.index_, resolved.Ptr());
+ // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+ WriteBarrier::ForEveryFieldWrite(this);
+
+ if (this == resolved->GetDexCache()) {
+ // If we're updating the dex cache of the class, optimistically update the cache for methods and
+ // fields if the caches are full arrays.
+ auto* resolved_methods = GetResolvedMethodsArray();
+ if (resolved_methods != nullptr) {
+ PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ // Because there could be duplicate method entries, we make sure we only
+ // update the cache with the first one found to be consistent with method
+ // resolution.
+ uint32_t previous_method_index = dex::kDexNoIndex;
+ for (ArtMethod& current_method : resolved->GetDeclaredMethods(pointer_size)) {
+ uint32_t new_index = current_method.GetDexMethodIndex();
+ if (new_index != previous_method_index) {
+ resolved_methods->Set(new_index, ¤t_method);
+ previous_method_index = new_index;
+ }
+ }
+ }
+ auto* resolved_fields = GetResolvedFieldsArray();
+ if (resolved_fields != nullptr) {
+ for (ArtField& current_field : resolved->GetSFields()) {
+ resolved_fields->Set(current_field.GetDexFieldIndex(), ¤t_field);
+ }
+ for (ArtField& current_field : resolved->GetIFields()) {
+ resolved_fields->Set(current_field.GetDexFieldIndex(), ¤t_field);
+ }
+ }
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 7c7b11f..cc01b6b 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -19,15 +19,17 @@
#include "array.h"
#include "base/array_ref.h"
+#include "base/atomic_pair.h"
#include "base/bit_utils.h"
#include "base/locks.h"
+#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
#include "gc_root.h" // Note: must not use -inl here to avoid circular dependency.
+#include "linear_alloc.h"
#include "object.h"
#include "object_array.h"
namespace art {
-enum class LinearAllocKind : uint32_t;
namespace linker {
class ImageWriter;
@@ -46,6 +48,7 @@
class CallSite;
class Class;
class ClassLoader;
+class DexCache;
class MethodType;
class String;
@@ -115,20 +118,124 @@
}
};
-using TypeDexCachePair = DexCachePair<Class>;
-using TypeDexCacheType = std::atomic<TypeDexCachePair>;
+template <typename T, size_t size> class NativeDexCachePairArray {
+ public:
+ NativeDexCachePairArray() {}
-using StringDexCachePair = DexCachePair<String>;
-using StringDexCacheType = std::atomic<StringDexCachePair>;
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ auto pair = GetNativePair(entries_, SlotIndex(index));
+ return pair.GetObjectForIndex(index);
+ }
-using FieldDexCachePair = NativeDexCachePair<ArtField>;
-using FieldDexCacheType = std::atomic<FieldDexCachePair>;
+ void Set(uint32_t index, T* value) {
+ NativeDexCachePair<T> pair(value, index);
+ SetNativePair(entries_, SlotIndex(index), pair);
+ }
-using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
-using MethodDexCacheType = std::atomic<MethodDexCachePair>;
+ NativeDexCachePair<T> GetNativePair(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetNativePair(entries_, SlotIndex(index));
+ }
-using MethodTypeDexCachePair = DexCachePair<MethodType>;
-using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
+ void SetNativePair(uint32_t index, NativeDexCachePair<T> value) {
+ SetNativePair(entries_, SlotIndex(index), value);
+ }
+
+ private:
+ NativeDexCachePair<T> GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array, size_t idx) {
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
+ AtomicPair<uintptr_t> value = AtomicPairLoadAcquire(&array[idx]);
+ return NativeDexCachePair<T>(reinterpret_cast<T*>(value.first), value.second);
+ }
+
+ void SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
+ size_t idx,
+ NativeDexCachePair<T> pair) {
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
+ AtomicPair<uintptr_t> v(reinterpret_cast<size_t>(pair.object), pair.index);
+ AtomicPairStoreRelease(&array[idx], v);
+ }
+
+ uint32_t SlotIndex(uint32_t index) {
+ return index % size;
+ }
+
+ std::atomic<NativeDexCachePair<T>> entries_[0];
+
+ NativeDexCachePairArray(const NativeDexCachePairArray<T, size>&) = delete;
+ NativeDexCachePairArray& operator=(const NativeDexCachePairArray<T, size>&) = delete;
+};
+
+template <typename T, size_t size> class DexCachePairArray {
+ public:
+ DexCachePairArray() {}
+
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetPair(index).GetObjectForIndex(index);
+ }
+
+ void Set(uint32_t index, T* value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ SetPair(index, DexCachePair<T>(value, index));
+ }
+
+ DexCachePair<T> GetPair(uint32_t index) {
+ return entries_[SlotIndex(index)].load(std::memory_order_relaxed);
+ }
+
+ void SetPair(uint32_t index, DexCachePair<T> value) {
+ entries_[SlotIndex(index)].store(value, std::memory_order_relaxed);
+ }
+
+ void Clear(uint32_t index) {
+ uint32_t slot = SlotIndex(index);
+ // This is racy but should only be called from the transactional interpreter.
+ if (entries_[slot].load(std::memory_order_relaxed).index == index) {
+ DexCachePair<T> cleared(nullptr, DexCachePair<T>::InvalidIndexForSlot(slot));
+ entries_[slot].store(cleared, std::memory_order_relaxed);
+ }
+ }
+
+ private:
+ uint32_t SlotIndex(uint32_t index) {
+ return index % size;
+ }
+
+ std::atomic<DexCachePair<T>> entries_[0];
+
+ DexCachePairArray(const DexCachePairArray<T, size>&) = delete;
+ DexCachePairArray& operator=(const DexCachePairArray<T, size>&) = delete;
+};
+
+template <typename T> class GcRootArray {
+ public:
+ GcRootArray() {}
+
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ GcRoot<T>& GetGcRoot(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return entries_[index];
+ }
+
+ void Set(uint32_t index, T* value) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ GcRoot<T> entries_[0];
+};
+
+template <typename T> class NativeArray {
+ public:
+ NativeArray() {}
+
+ T* Get(uint32_t index) {
+ return entries_[index];
+ }
+
+ void Set(uint32_t index, T* value) {
+ entries_[index] = value;
+ }
+
+ private:
+ T* entries_[0];
+};
// C++ mirror of java.lang.DexCache.
class MANAGED DexCache final : public Object {
@@ -164,26 +271,6 @@
static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
"MethodType dex cache size is not a power of 2.");
- static constexpr size_t StaticTypeSize() {
- return kDexCacheTypeCacheSize;
- }
-
- static constexpr size_t StaticStringSize() {
- return kDexCacheStringCacheSize;
- }
-
- static constexpr size_t StaticArtFieldSize() {
- return kDexCacheFieldCacheSize;
- }
-
- static constexpr size_t StaticMethodSize() {
- return kDexCacheMethodCacheSize;
- }
-
- static constexpr size_t StaticMethodTypeSize() {
- return kDexCacheMethodTypeCacheSize;
- }
-
// Size of an instance of java.lang.DexCache not including referenced values.
static constexpr uint32_t InstanceSize() {
return sizeof(DexCache);
@@ -196,7 +283,6 @@
DexCachePair<Object>* pairs_end)
REQUIRES_SHARED(Locks::mutator_lock_);
-
void Initialize(const DexFile* dex_file, ObjPtr<ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::dex_lock_);
@@ -209,66 +295,6 @@
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ObjPtr<String> GetLocation() REQUIRES_SHARED(Locks::mutator_lock_);
- static constexpr MemberOffset StringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_);
- }
-
- static constexpr MemberOffset PreResolvedStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, preresolved_strings_);
- }
-
- static constexpr MemberOffset ResolvedTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_);
- }
-
- static constexpr MemberOffset ResolvedFieldsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_fields_);
- }
-
- static constexpr MemberOffset ResolvedMethodsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_);
- }
-
- static constexpr MemberOffset ResolvedMethodTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
- }
-
- static constexpr MemberOffset ResolvedCallSitesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_);
- }
-
- static constexpr MemberOffset NumStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
- }
-
- static constexpr MemberOffset NumPreResolvedStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_preresolved_strings_);
- }
-
- static constexpr MemberOffset NumResolvedTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_types_);
- }
-
- static constexpr MemberOffset NumResolvedFieldsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_fields_);
- }
-
- static constexpr MemberOffset NumResolvedMethodsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_);
- }
-
- static constexpr MemberOffset NumResolvedMethodTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
- }
-
- static constexpr MemberOffset NumResolvedCallSitesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
- }
-
- static constexpr size_t PreResolvedStringsAlignment() {
- return alignof(GcRoot<mirror::String>);
- }
-
String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -317,106 +343,6 @@
ObjPtr<CallSite> SetResolvedCallSite(uint32_t call_site_idx, ObjPtr<CallSite> resolved)
REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr64<StringDexCacheType*, kVerifyFlags>(StringsOffset());
- }
-
- void SetStrings(StringDexCacheType* strings) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(StringsOffset(), strings);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<TypeDexCacheType*, kVerifyFlags>(ResolvedTypesOffset());
- }
-
- void SetResolvedTypes(TypeDexCacheType* resolved_types)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types);
- }
-
- MethodDexCacheType* GetResolvedMethods() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<MethodDexCacheType*>(ResolvedMethodsOffset());
- }
-
- void SetResolvedMethods(MethodDexCacheType* resolved_methods)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedMethodsOffset(), resolved_methods);
- }
-
- FieldDexCacheType* GetResolvedFields() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<FieldDexCacheType*>(ResolvedFieldsOffset());
- }
-
- void SetResolvedFields(FieldDexCacheType* resolved_fields)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- MethodTypeDexCacheType* GetResolvedMethodTypes()
- ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr64<MethodTypeDexCacheType*, kVerifyFlags>(ResolvedMethodTypesOffset());
- }
-
- void SetResolvedMethodTypes(MethodTypeDexCacheType* resolved_method_types)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- GcRoot<CallSite>* GetResolvedCallSites()
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<GcRoot<CallSite>*, kVerifyFlags>(ResolvedCallSitesOffset());
- }
-
- void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumStringsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumPreResolvedStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumPreResolvedStringsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedTypesOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedMethods() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedMethodsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedFields() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedFieldsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedMethodTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedMethodTypesOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedCallSitesOffset());
- }
-
const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
}
@@ -427,25 +353,6 @@
void SetLocation(ObjPtr<String> location) REQUIRES_SHARED(Locks::mutator_lock_);
- template <typename T>
- static NativeDexCachePair<T> GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx);
-
- template <typename T>
- static void SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx,
- NativeDexCachePair<T> pair);
-
- static size_t PreResolvedStringsSize(size_t num_strings) {
- return sizeof(GcRoot<mirror::String>) * num_strings;
- }
-
- uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-
void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
void SetClassLoader(ObjPtr<ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -458,10 +365,176 @@
void VisitNativeRoots(const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+ // Sets null to dex cache array fields which were allocated with the startup
+ // allocator.
+ void UnlinkStartupCaches() REQUIRES_SHARED(Locks::mutator_lock_);
+
+// NOLINTBEGIN(bugprone-macro-parentheses)
+#define DEFINE_ARRAY(name, array_kind, getter_setter, type, ids, alloc_kind) \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ array_kind* Get ##getter_setter() \
+ ALWAYS_INLINE \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return GetFieldPtr<array_kind*, kVerifyFlags>(getter_setter ##Offset()); \
+ } \
+ void Set ##getter_setter(array_kind* value) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ SetFieldPtr<false>(getter_setter ##Offset(), value); \
+ } \
+ static constexpr MemberOffset getter_setter ##Offset() { \
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, name); \
+ } \
+ array_kind* Allocate ##getter_setter(bool startup = false) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return reinterpret_cast<array_kind*>(AllocArray<type>( \
+ getter_setter ##Offset(), GetDexFile()->ids(), alloc_kind, startup)); \
+ } \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ size_t Num ##getter_setter() REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return Get ##getter_setter() == nullptr ? 0u : GetDexFile()->ids(); \
+ } \
+
+#define DEFINE_PAIR_ARRAY(name, pair_kind, getter_setter, type, size, alloc_kind) \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ pair_kind ##Array<type, size>* Get ##getter_setter() \
+ ALWAYS_INLINE \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return GetFieldPtr<pair_kind ##Array<type, size>*, kVerifyFlags>(getter_setter ##Offset()); \
+ } \
+ void Set ##getter_setter(pair_kind ##Array<type, size>* value) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ SetFieldPtr<false>(getter_setter ##Offset(), value); \
+ } \
+ static constexpr MemberOffset getter_setter ##Offset() { \
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, name); \
+ } \
+ pair_kind ##Array<type, size>* Allocate ##getter_setter() \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return reinterpret_cast<pair_kind ##Array<type, size>*>( \
+ AllocArray<std::atomic<pair_kind<type>>>( \
+ getter_setter ##Offset(), size, alloc_kind)); \
+ } \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ size_t Num ##getter_setter() REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return Get ##getter_setter() == nullptr ? 0u : size; \
+ } \
+
+#define DEFINE_DUAL_CACHE( \
+ name, pair_kind, getter_setter, type, pair_size, alloc_pair_kind, \
+ array_kind, component_type, ids, alloc_array_kind) \
+ DEFINE_PAIR_ARRAY( \
+ name, pair_kind, getter_setter, type, pair_size, alloc_pair_kind) \
+ DEFINE_ARRAY( \
+ name ##array_, array_kind, getter_setter ##Array, component_type, ids, alloc_array_kind) \
+ type* Get ##getter_setter ##Entry(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) { \
+ DCHECK_LT(index, GetDexFile()->ids()); \
+ auto* array = Get ##getter_setter ##Array(); \
+ if (array != nullptr) { \
+ return array->Get(index); \
+ } \
+ auto* pairs = Get ##getter_setter(); \
+ if (pairs != nullptr) { \
+ return pairs->Get(index); \
+ } \
+ return nullptr; \
+ } \
+ void Set ##getter_setter ##Entry(uint32_t index, type* resolved) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ DCHECK_LT(index, GetDexFile()->ids()); \
+ auto* array = Get ##getter_setter ##Array(); \
+ if (array != nullptr) { \
+ array->Set(index, resolved); \
+ } else { \
+ auto* pairs = Get ##getter_setter(); \
+ if (pairs == nullptr) { \
+ bool should_allocate_full_array = ShouldAllocateFullArray(GetDexFile()->ids(), pair_size); \
+ if (ShouldAllocateFullArrayAtStartup() || should_allocate_full_array) { \
+ array = Allocate ##getter_setter ##Array(!should_allocate_full_array); \
+ array->Set(index, resolved); \
+ } else { \
+ pairs = Allocate ##getter_setter(); \
+ pairs->Set(index, resolved); \
+ } \
+ } else { \
+ pairs->Set(index, resolved); \
+ } \
+ } \
+ } \
+ void Unlink ##getter_setter ##ArrayIfStartup() \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ if (!ShouldAllocateFullArray(GetDexFile()->ids(), pair_size)) { \
+ Set ##getter_setter ##Array(nullptr) ; \
+ } \
+ }
+
+ DEFINE_ARRAY(resolved_call_sites_,
+ GcRootArray<CallSite>,
+ ResolvedCallSites,
+ GcRoot<CallSite>,
+ NumCallSiteIds,
+ LinearAllocKind::kGCRootArray)
+
+ DEFINE_DUAL_CACHE(resolved_fields_,
+ NativeDexCachePair,
+ ResolvedFields,
+ ArtField,
+ kDexCacheFieldCacheSize,
+ LinearAllocKind::kNoGCRoots,
+ NativeArray<ArtField>,
+ ArtField,
+ NumFieldIds,
+ LinearAllocKind::kNoGCRoots)
+
+ DEFINE_DUAL_CACHE(resolved_method_types_,
+ DexCachePair,
+ ResolvedMethodTypes,
+ mirror::MethodType,
+ kDexCacheMethodTypeCacheSize,
+ LinearAllocKind::kDexCacheArray,
+ GcRootArray<mirror::MethodType>,
+ GcRoot<mirror::MethodType>,
+ NumProtoIds,
+ LinearAllocKind::kGCRootArray);
+
+ DEFINE_DUAL_CACHE(resolved_methods_,
+ NativeDexCachePair,
+ ResolvedMethods,
+ ArtMethod,
+ kDexCacheMethodCacheSize,
+ LinearAllocKind::kNoGCRoots,
+ NativeArray<ArtMethod>,
+ ArtMethod,
+ NumMethodIds,
+ LinearAllocKind::kNoGCRoots)
+
+ DEFINE_DUAL_CACHE(resolved_types_,
+ DexCachePair,
+ ResolvedTypes,
+ mirror::Class,
+ kDexCacheTypeCacheSize,
+ LinearAllocKind::kDexCacheArray,
+ GcRootArray<mirror::Class>,
+ GcRoot<mirror::Class>,
+ NumTypeIds,
+ LinearAllocKind::kGCRootArray);
+
+ DEFINE_DUAL_CACHE(strings_,
+ DexCachePair,
+ Strings,
+ mirror::String,
+ kDexCacheStringCacheSize,
+ LinearAllocKind::kDexCacheArray,
+ GcRootArray<mirror::String>,
+ GcRoot<mirror::String>,
+ NumStringIds,
+ LinearAllocKind::kGCRootArray);
+
+// NOLINTEND(bugprone-macro-parentheses)
+
private:
// Allocate new array in linear alloc and save it in the given fields.
- template<typename T, size_t kMaxCacheSize>
- T* AllocArray(MemberOffset obj_offset, MemberOffset num_offset, size_t num, LinearAllocKind kind)
+ template<typename T>
+ T* AllocArray(MemberOffset obj_offset, size_t num, LinearAllocKind kind, bool startup = false)
REQUIRES_SHARED(Locks::mutator_lock_);
// Visit instance fields of the dex cache as well as its associated arrays.
@@ -472,30 +545,32 @@
void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+ // Returns whether we should allocate a full array given the current state of
+ // the runtime and oat files.
+ bool ShouldAllocateFullArrayAtStartup() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Returns whether we should allocate a full array given the number of
+ // elements.
+ static bool ShouldAllocateFullArray(size_t number_of_elements, size_t dex_cache_size) {
+ return number_of_elements <= dex_cache_size;
+ }
+
HeapReference<ClassLoader> class_loader_;
HeapReference<String> location_;
- uint64_t dex_file_; // const DexFile*
- uint64_t preresolved_strings_; // GcRoot<mirror::String*> array with num_preresolved_strings
- // elements.
- uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
- // elements.
- uint64_t resolved_fields_; // std::atomic<FieldDexCachePair>*, array with
- // num_resolved_fields_ elements.
- uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
- // num_resolved_method_types_ elements.
- uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
- uint64_t resolved_types_; // TypeDexCacheType*, array with num_resolved_types_ elements.
- uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
- // elements.
-
- uint32_t num_preresolved_strings_; // Number of elements in the preresolved_strings_ array.
- uint32_t num_resolved_call_sites_; // Number of elements in the call_sites_ array.
- uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array.
- uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array.
- uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array.
- uint32_t num_resolved_types_; // Number of elements in the resolved_types_ array.
- uint32_t num_strings_; // Number of elements in the strings_ array.
+ uint64_t dex_file_; // const DexFile*
+ //
+ uint64_t resolved_call_sites_; // Array of call sites
+ uint64_t resolved_fields_; // NativeDexCacheArray holding ArtField's
+ uint64_t resolved_fields_array_; // Array of ArtField's.
+ uint64_t resolved_method_types_; // DexCacheArray holding mirror::MethodType's
+ uint64_t resolved_method_types_array_; // Array of mirror::MethodType's
+ uint64_t resolved_methods_; // NativeDexCacheArray holding ArtMethod's
+ uint64_t resolved_methods_array_; // Array of ArtMethod's
+ uint64_t resolved_types_; // DexCacheArray holding mirror::Class's
+ uint64_t resolved_types_array_; // Array of resolved types.
+ uint64_t strings_; // DexCacheArray holding mirror::String's
+ uint64_t strings_array_; // Array of String's.
friend struct art::DexCacheOffsets; // for verifying offset information
friend class linker::ImageWriter;
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index a0e8fda..bc521f5 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -146,15 +146,16 @@
// The MethodTypes dex file contains a single interface with two abstract
// methods. It must therefore contain precisely two method IDs.
ASSERT_EQ(2u, dex_file.NumProtoIds());
- ASSERT_EQ(dex_file.NumProtoIds(), dex_cache->NumResolvedMethodTypes());
- MethodTypeDexCacheType* method_types_cache = dex_cache->GetResolvedMethodTypes();
+ ASSERT_EQ(dex_file.NumProtoIds(), dex_cache->NumResolvedMethodTypesArray());
+ ASSERT_EQ(0u, dex_cache->NumResolvedMethodTypes());
+ auto* method_types_cache = dex_cache->GetResolvedMethodTypesArray();
for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) {
- const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed);
- if (dex::ProtoIndex(pair.index) == method1_id.proto_idx_) {
- ASSERT_EQ(method1_type.Get(), pair.object.Read());
- } else if (dex::ProtoIndex(pair.index) == method2_id.proto_idx_) {
- ASSERT_EQ(method2_type.Get(), pair.object.Read());
+ auto* method_type = method_types_cache->Get(i);
+ if (dex::ProtoIndex(i) == method1_id.proto_idx_) {
+ ASSERT_EQ(method1_type.Get(), method_type);
+ } else if (dex::ProtoIndex(i) == method2_id.proto_idx_) {
+ ASSERT_EQ(method2_type.Get(), method_type);
} else {
ASSERT_TRUE(false);
}
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index e9c41f9..62c35df 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -20,7 +20,6 @@
#include "class_root-inl.h"
#include "dex/modifiers.h"
#include "handle_scope.h"
-#include "jni/jni_internal.h"
#include "mirror/method_handle_impl.h"
#include "obj_ptr-inl.h"
#include "object-inl.h"
@@ -42,25 +41,17 @@
}
ObjPtr<MethodHandlesLookup> MethodHandlesLookup::GetDefault(Thread* const self) {
- ArtMethod* lookup = jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandles_lookup);
- JValue result;
- lookup->Invoke(self, nullptr, 0, &result, "L");
- return ObjPtr<MethodHandlesLookup>::DownCast(result.GetL());
+ ArtMethod* lookup = WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
+ return ObjPtr<MethodHandlesLookup>::DownCast(lookup->InvokeStatic<'L'>(self));
}
ObjPtr<MethodHandle> MethodHandlesLookup::FindConstructor(Thread* const self,
Handle<Class> klass,
Handle<MethodType> method_type) {
- ArtMethod* findConstructor =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor);
- uint32_t args[] = {
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this)),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(klass.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_type.Get()))
- };
- JValue result;
- findConstructor->Invoke(self, args, sizeof(args), &result, "LLL");
- return ObjPtr<MethodHandle>::DownCast(result.GetL());
+ ArtMethod* find_constructor =
+ WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
+ return ObjPtr<MethodHandle>::DownCast(
+ find_constructor->InvokeFinal<'L', 'L', 'L'>(self, this, klass.Get(), method_type.Get()));
}
} // namespace mirror
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index 386244d..ce123fa 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -21,6 +21,7 @@
#include <string_view>
#include "base/atomic.h"
+#include "base/casts.h"
#include "base/locks.h" // For Locks::mutator_lock_.
#include "heap_poisoning.h"
#include "obj_ptr.h"
@@ -50,6 +51,7 @@
vis("Ljava/lang/ClassNotFoundException;") \
vis("Ljava/lang/DexCache;") \
vis("Ljava/lang/Object;") \
+ vis("Ljava/lang/StackFrameInfo;") \
vis("Ljava/lang/StackTraceElement;") \
vis("Ljava/lang/String;") \
vis("Ljava/lang/Throwable;") \
@@ -94,14 +96,14 @@
public:
// Compress reference to its bit representation.
static uint32_t Compress(MirrorType* mirror_ptr) {
- uintptr_t as_bits = reinterpret_cast<uintptr_t>(mirror_ptr);
- return static_cast<uint32_t>(kPoisonReferences ? -as_bits : as_bits);
+ uint32_t as_bits = reinterpret_cast32<uint32_t>(mirror_ptr);
+ return kPoisonReferences ? -as_bits : as_bits;
}
// Uncompress an encoded reference from its bit representation.
static MirrorType* Decompress(uint32_t ref) {
- uintptr_t as_bits = kPoisonReferences ? -ref : ref;
- return reinterpret_cast<MirrorType*>(as_bits);
+ uint32_t as_bits = kPoisonReferences ? -ref : ref;
+ return reinterpret_cast32<MirrorType*>(as_bits);
}
// Convert an ObjPtr to a compressed reference.
@@ -143,10 +145,6 @@
return reference_ == 0;
}
- uint32_t AsVRegValue() const {
- return reference_;
- }
-
static ObjectReference<kPoisonReferences, MirrorType> FromMirrorPtr(MirrorType* mirror_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
return ObjectReference<kPoisonReferences, MirrorType>(mirror_ptr);
@@ -229,9 +227,19 @@
return CompressedReference<MirrorType>(p);
}
+ static CompressedReference<MirrorType> FromVRegValue(uint32_t vreg_value) {
+ CompressedReference<MirrorType> result(nullptr);
+ result.reference_ = vreg_value;
+ return result;
+ }
+
+ uint32_t AsVRegValue() const {
+ return this->reference_;
+ }
+
private:
explicit CompressedReference(MirrorType* p) REQUIRES_SHARED(Locks::mutator_lock_)
- : mirror::ObjectReference<false, MirrorType>(p) {}
+ : ObjectReference<false, MirrorType>(p) {}
};
} // namespace mirror
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index e26cf42..6f42c5b 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -50,6 +50,10 @@
class ObjectTest : public CommonRuntimeTest {
protected:
+ ObjectTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void AssertString(int32_t expected_utf16_length,
const char* utf8_in,
const char* utf16_expected_le,
@@ -420,10 +424,12 @@
ASSERT_TRUE(field_id != nullptr);
uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id);
- ArtField* field = FindFieldFromCode<StaticObjectRead, true>(field_idx, clinit, Thread::Current(),
- sizeof(HeapReference<Object>));
+ ArtField* field = FindFieldFromCode<StaticObjectRead>(field_idx,
+ clinit,
+ Thread::Current(),
+ sizeof(HeapReference<Object>));
ObjPtr<Object> s0 = field->GetObj(klass.Get());
- EXPECT_TRUE(s0 != nullptr);
+ EXPECT_TRUE(s0 != nullptr) << field->PrettyField();
Handle<CharArray> char_array(hs.NewHandle(CharArray::Alloc(soa.Self(), 0)));
field->SetObj<false>(field->GetDeclaringClass(), char_array.Get());
@@ -522,7 +528,7 @@
StackHandleScope<1> hs(soa.Self());
Handle<String> string(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "android")));
EXPECT_EQ(string->GetLength(), 7);
- EXPECT_EQ(string->GetUtfLength(), 7);
+ EXPECT_EQ(string->GetModifiedUtf8Length(), 7);
}
TEST_F(ObjectTest, DescriptorCompare) {
diff --git a/runtime/mirror/stack_frame_info.cc b/runtime/mirror/stack_frame_info.cc
new file mode 100644
index 0000000..dd3e8f7
--- /dev/null
+++ b/runtime/mirror/stack_frame_info.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 "stack_frame_info.h"
+
+#include "class-alloc-inl.h"
+#include "class.h"
+#include "class_root-inl.h"
+#include "gc/accounting/card_table-inl.h"
+#include "handle_scope-inl.h"
+#include "object-inl.h"
+#include "string.h"
+
+namespace art {
+namespace mirror {
+
+void StackFrameInfo::AssignFields(Handle<Class> declaring_class,
+ Handle<MethodType> method_type,
+ Handle<String> method_name,
+ Handle<String> file_name,
+ int32_t line_number,
+ int32_t dex_pc) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFields<true>(declaring_class.Get(), method_type.Get(), method_name.Get(),
+ file_name.Get(), line_number, dex_pc);
+ } else {
+ SetFields<false>(declaring_class.Get(), method_type.Get(), method_name.Get(),
+ file_name.Get(), line_number, dex_pc);
+ }
+}
+
+template<bool kTransactionActive>
+void StackFrameInfo::SetFields(ObjPtr<Class> declaring_class,
+ ObjPtr<MethodType> method_type,
+ ObjPtr<String> method_name,
+ ObjPtr<String> file_name,
+ int32_t line_number,
+ int32_t bci) {
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, declaring_class_),
+ declaring_class);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, method_type_),
+ method_type);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, method_name_),
+ method_name);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, file_name_),
+ file_name);
+ SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, line_number_),
+ line_number);
+ SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, bci_),
+ bci);
+}
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/stack_frame_info.h b/runtime/mirror/stack_frame_info.h
new file mode 100644
index 0000000..24f8c8f
--- /dev/null
+++ b/runtime/mirror/stack_frame_info.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
+#define ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
+
+#include "method_type.h"
+#include "object.h"
+#include "stack_trace_element.h"
+
+namespace art {
+
+template<class T> class Handle;
+struct StackFrameInfoOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.StackFrameInfo
+class MANAGED StackFrameInfo final : public Object {
+ public:
+ MIRROR_CLASS("Ljava/lang/StackFrameInfo;");
+
+ void AssignFields(Handle<Class> declaring_class,
+ Handle<MethodType> method_type,
+ Handle<String> method_name,
+ Handle<String> file_name,
+ int32_t line_number,
+ int32_t dex_pc)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
+ HeapReference<Class> declaring_class_;
+ HeapReference<String> file_name_;
+ HeapReference<String> method_name_;
+ HeapReference<Class> method_type_;
+ HeapReference<StackTraceElement> ste_;
+ int32_t bci_;
+ int32_t line_number_;
+ bool retain_class_ref_;
+
+ template<bool kTransactionActive>
+ void SetFields(ObjPtr<Class> declaring_class,
+ ObjPtr<MethodType> method_type,
+ ObjPtr<String> method_name,
+ ObjPtr<String> file_name,
+ int32_t line_number,
+ int32_t bci)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ friend struct art::StackFrameInfoOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StackFrameInfo);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
diff --git a/runtime/mirror/string-alloc-inl.h b/runtime/mirror/string-alloc-inl.h
index 533053d..cb2dcb2 100644
--- a/runtime/mirror/string-alloc-inl.h
+++ b/runtime/mirror/string-alloc-inl.h
@@ -18,6 +18,7 @@
#include "string-inl.h"
+#include "android-base/endian.h"
#include "android-base/stringprintf.h"
#include "array.h"
@@ -89,6 +90,41 @@
};
// Sets string count and value in the allocation code path to ensure it is guarded by a CAS.
+class SetStringCountAndUtf16BytesVisitor {
+ public:
+ SetStringCountAndUtf16BytesVisitor(int32_t count, Handle<ByteArray> src_array, int32_t offset)
+ : count_(count), src_array_(src_array), offset_(offset) {
+ }
+
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Avoid AsString as object is not yet in live bitmap or allocation stack.
+ ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
+ string->SetCount(count_);
+ DCHECK_IMPLIES(string->IsCompressed(), kUseStringCompression);
+ uint32_t length = String::GetLengthFromCount(count_);
+ const uint8_t* const src = reinterpret_cast<uint8_t*>(src_array_->GetData()) + offset_;
+ if (UNLIKELY(string->IsCompressed())) {
+ uint8_t* valueCompressed = string->GetValueCompressed();
+ for (uint32_t i = 0; i < length; i++) {
+ valueCompressed[i] = (src[i << 1] & 0xFF);
+ }
+ } else {
+ uint16_t* value = string->GetValue();
+ for (uint32_t i = 0; i < length; i++) {
+ uint32_t index = (i << 1);
+ value[i] = (src[index] & 0xFF) + ((src[index + 1] & 0xFF) << 8);
+ }
+ }
+ }
+
+ private:
+ const int32_t count_;
+ Handle<ByteArray> src_array_;
+ const int32_t offset_;
+};
+
+// Sets string count and value in the allocation code path to ensure it is guarded by a CAS.
class SetStringCountAndValueVisitorFromCharArray {
public:
SetStringCountAndValueVisitorFromCharArray(int32_t count, Handle<CharArray> src_array,
@@ -216,13 +252,39 @@
const uint8_t* const src = reinterpret_cast<uint8_t*>(array->GetData()) + offset;
high_byte &= 0xff; // Extract the relevant bits before determining `compressible`.
const bool compressible =
- kUseStringCompression && String::AllASCII<uint8_t>(src, byte_length) && (high_byte == 0);
+ kUseStringCompression &&
+ String::AllASCII<uint8_t>(src, byte_length) &&
+ (high_byte == 0 || byte_length == 0);
const int32_t length_with_flag = String::GetFlaggedCount(byte_length, compressible);
SetStringCountAndBytesVisitor visitor(length_with_flag, array, offset, high_byte << 8);
return Alloc<kIsInstrumented>(self, length_with_flag, allocator_type, visitor);
}
template <bool kIsInstrumented>
+inline ObjPtr<String> String::AllocFromUtf16ByteArray(Thread* self,
+ int32_t char_count,
+ Handle<ByteArray> array,
+ int32_t offset,
+ gc::AllocatorType allocator_type) {
+ static_assert(__BYTE_ORDER == __LITTLE_ENDIAN,
+ "Please update this function and java-side callers to support big endian.");
+ const uint8_t* const src = reinterpret_cast<uint8_t*>(array->GetData()) + offset;
+ bool compressible = kUseStringCompression;
+ if (compressible) {
+ uint32_t byte_count = (static_cast<uint32_t>(char_count) << 1);
+ for (uint32_t i = 0; i < byte_count; i += 2) {
+ if (!IsASCII((src[i] & 0xff) + ((src[i + 1] & 0xff) << 8))) {
+ compressible = false;
+ break;
+ }
+ }
+ }
+ const int32_t length_with_flag = String::GetFlaggedCount(char_count, compressible);
+ SetStringCountAndUtf16BytesVisitor visitor(length_with_flag, array, offset);
+ return Alloc<kIsInstrumented>(self, length_with_flag, allocator_type, visitor);
+}
+
+template <bool kIsInstrumented>
inline ObjPtr<String> String::AllocFromCharArray(Thread* self,
int32_t count,
Handle<CharArray> array,
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 5903872..883a45c 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -35,11 +35,11 @@
// lambda$codePoints$1$CharSequence
// which were virtual functions in standalone desugar, becomes
// direct functions with D8 desugaring.
- uint32_t vtable_entries = Object::kVTableLength + 60;
+ uint32_t vtable_entries = Object::kVTableLength + 67;
#else
- uint32_t vtable_entries = Object::kVTableLength + 62;
+ uint32_t vtable_entries = Object::kVTableLength + 69;
#endif
- return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size);
+ return Class::ComputeClassSize(true, vtable_entries, 3, 0, 0, 1, 2, pointer_size);
}
inline uint16_t String::CharAt(int32_t index) {
@@ -83,11 +83,11 @@
return result;
}
-inline int32_t String::GetUtfLength() {
+inline int32_t String::GetModifiedUtf8Length() {
if (IsCompressed()) {
return GetLength();
} else {
- return CountUtf8Bytes(GetValue(), GetLength());
+ return CountModifiedUtf8BytesInUtf16(GetValue(), GetLength());
}
}
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index d059278..839e01a 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -19,6 +19,7 @@
#include "arch/memcmp16.h"
#include "array-alloc-inl.h"
#include "base/array_ref.h"
+#include "base/casts.h"
#include "base/stl_util.h"
#include "class-inl.h"
#include "dex/descriptors_names.h"
@@ -254,7 +255,7 @@
return Alloc(self, length_with_flag, allocator_type, visitor);
}
-bool String::Equals(ObjPtr<String> that) {
+bool String::Equals(mirror::String* that) {
if (this == that) {
// Quick reference equality test
return true;
@@ -312,7 +313,7 @@
if (IsCompressed()) {
return std::string(reinterpret_cast<const char*>(GetValueCompressed()), GetLength());
} else {
- size_t byte_count = GetUtfLength();
+ size_t byte_count = GetModifiedUtf8Length();
std::string result(byte_count, static_cast<char>(0));
ConvertUtf16ToModifiedUtf8(&result[0], byte_count, GetValue(), GetLength());
return result;
@@ -396,6 +397,38 @@
}
}
+void String::FillBytesLatin1(Handle<ByteArray> array, int32_t index) {
+ int8_t* data = array->GetData() + index;
+ int32_t length = GetLength();
+ if (IsCompressed()) {
+ const uint8_t* value = GetValueCompressed();
+ memcpy(data, value, length * sizeof(uint8_t));
+ } else {
+ // Drop the high byte of the characters.
+ // The caller should check that all dropped high bytes are zeros.
+ const uint16_t* value = GetValue();
+ for (int32_t i = 0; i < length; ++i) {
+ data[i] = static_cast<int8_t>(dchecked_integral_cast<uint8_t>(value[i]));
+ }
+ }
+}
+
+void String::FillBytesUTF16(Handle<ByteArray> array, int32_t index) {
+ int8_t* data = array->GetData() + index;
+ int32_t length = GetLength();
+ if (IsCompressed()) {
+ const uint8_t* value = GetValueCompressed();
+ uint32_t d_index = 0;
+ for (int i = 0; i < length; ++i) {
+ data[d_index++] = static_cast<int8_t>(value[i]);
+ data[d_index++] = 0;
+ }
+ } else {
+ const uint16_t* value = GetValue();
+ memcpy(data, value, length * sizeof(uint16_t));
+ }
+}
+
bool String::IsValueNull() {
return (IsCompressed()) ? (GetValueCompressed() == nullptr) : (GetValue() == nullptr);
}
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 2eb8e0a..90eb092 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -114,7 +114,7 @@
// Computes and returns the hash code.
int32_t ComputeHashCode() REQUIRES_SHARED(Locks::mutator_lock_);
- int32_t GetUtfLength() REQUIRES_SHARED(Locks::mutator_lock_);
+ int32_t GetModifiedUtf8Length() REQUIRES_SHARED(Locks::mutator_lock_);
uint16_t CharAt(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -142,6 +142,14 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
template <bool kIsInstrumented = true>
+ ALWAYS_INLINE static ObjPtr<String> AllocFromUtf16ByteArray(Thread* self,
+ int32_t char_count,
+ Handle<ByteArray> array,
+ int32_t offset,
+ gc::AllocatorType allocator_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ template <bool kIsInstrumented = true>
ALWAYS_INLINE static ObjPtr<String> AllocFromCharArray(Thread* self,
int32_t count,
Handle<CharArray> array,
@@ -189,7 +197,14 @@
bool Equals(const char* modified_utf8) REQUIRES_SHARED(Locks::mutator_lock_);
- bool Equals(ObjPtr<String> that) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool Equals(ObjPtr<mirror::String> that) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return Equals(that.Ptr());
+ }
+
+ // A version that takes a mirror::String pointer instead of ObjPtr as it's being
+ // called by the runtime app image code which can encode mirror::String at 64bit
+ // addresses (ObjPtr only works with 32bit pointers).
+ bool Equals(mirror::String* that) REQUIRES_SHARED(Locks::mutator_lock_);
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string ToModifiedUtf8() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -209,6 +224,12 @@
void GetChars(int32_t start, int32_t end, Handle<CharArray> array, int32_t index)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void FillBytesLatin1(Handle<ByteArray> array, int32_t index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void FillBytesUTF16(Handle<ByteArray> array, int32_t index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsCompressed() REQUIRES_SHARED(Locks::mutator_lock_) {
return kUseStringCompression && IsCompressed(GetCount());
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index b538fa9..a38ba1c 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -31,7 +31,7 @@
#include "object_array.h"
#include "stack_trace_element-inl.h"
#include "string.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
namespace mirror {
@@ -70,14 +70,14 @@
}
bool Throwable::IsCheckedException() {
- if (InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error))) {
+ if (IsError()) {
return false;
}
- return !InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_RuntimeException));
+ return !InstanceOf(WellKnownClasses::java_lang_RuntimeException.Get());
}
bool Throwable::IsError() {
- return InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error));
+ return InstanceOf(WellKnownClasses::java_lang_Error.Get());
}
int32_t Throwable::GetStackDepth() {
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index 68d329d..2220f92 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -221,11 +221,6 @@
}
}
-inline MemberOffset GetMemberOffset(jfieldID field_id) REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* const field = jni::DecodeArtField(field_id);
- return field->GetOffset();
-}
-
//
// Helper methods for storing results from atomic operations into
// JValue instances.
@@ -1934,7 +1929,7 @@
// Check access_mode is compatible with ByteBuffer's read-only property.
bool is_read_only = byte_buffer->GetFieldBoolean(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_isReadOnly));
+ WellKnownClasses::java_nio_ByteBuffer_isReadOnly->GetOffset());
if (is_read_only && !IsReadOnlyAccessMode(access_mode)) {
ThrowReadOnlyBufferException();
return false;
@@ -1942,20 +1937,20 @@
// The native_address is only set for ByteBuffer instances backed by native memory.
const int64_t native_address =
- byte_buffer->GetField64(GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_address));
+ byte_buffer->GetField64(WellKnownClasses::java_nio_Buffer_address->GetOffset());
// Determine offset and limit for accesses.
int32_t byte_buffer_offset;
if (native_address == 0L) {
// Accessing a heap allocated byte buffer.
byte_buffer_offset = byte_buffer->GetField32(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_offset));
+ WellKnownClasses::java_nio_ByteBuffer_offset->GetOffset());
} else {
// Accessing direct memory.
byte_buffer_offset = 0;
}
- const int32_t byte_buffer_limit = byte_buffer->GetField32(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_limit));
+ const int32_t byte_buffer_limit =
+ byte_buffer->GetField32(WellKnownClasses::java_nio_Buffer_limit->GetOffset());
const int32_t byte_buffer_length = byte_buffer_offset + byte_buffer_limit;
const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType();
@@ -1967,7 +1962,7 @@
int8_t* data;
if (native_address == 0) {
ObjPtr<ByteArray> heap_byte_array = byte_buffer->GetFieldObject<ByteArray>(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_hb));
+ WellKnownClasses::java_nio_ByteBuffer_hb->GetOffset());
data = heap_byte_array->GetData();
} else {
data = reinterpret_cast<int8_t*>(static_cast<uint32_t>(native_address));
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index 9c1a2e7..859e86c 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -40,6 +40,10 @@
// Tests for mirror::VarHandle and it's descendents.
class VarHandleTest : public CommonRuntimeTest {
public:
+ VarHandleTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
static ObjPtr<FieldVarHandle> CreateFieldVarHandle(Thread* const self,
ArtField* art_field,
int32_t access_modes_bit_mask)
diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h
index e07aa97..133cde0 100644
--- a/runtime/monitor_pool.h
+++ b/runtime/monitor_pool.h
@@ -189,12 +189,12 @@
// Size of a monitor, rounded up to a multiple of alignment.
static constexpr size_t kAlignedMonitorSize = (sizeof(Monitor) + kMonitorAlignment - 1) &
-kMonitorAlignment;
- // As close to a page as we can get seems a good start.
- static constexpr size_t kChunkCapacity = kPageSize / kAlignedMonitorSize;
- // Chunk size that is referenced in the id. We can collapse this to the actually used storage
- // in a chunk, i.e., kChunkCapacity * kAlignedMonitorSize, but this will mean proper divisions.
- static constexpr size_t kChunkSize = kPageSize;
+ // Size of the chunks holding the actual monitors. The bottom bits of the monitor id are the
+ // index into such a chunk. We can collapse this to the actually used storage
+ // in a chunk, i.e., kChunkCapacity * kAlignedMonitorSize, but this would mean proper divisions.
+ static constexpr size_t kChunkSize = 4096;
static_assert(IsPowerOfTwo(kChunkSize), "kChunkSize must be power of 2");
+ static constexpr size_t kChunkCapacity = kChunkSize / kAlignedMonitorSize;
// The number of chunks of storage that can be referenced by the initial chunk list.
// The total number of usable monitor chunks is typically 255 times this number, so it
// should be large enough that we don't run out. We run out of address bits if it's > 512.
@@ -215,7 +215,7 @@
// Array of pointers to lists (again arrays) of pointers to chunks containing monitors.
// Zeroth entry points to a list (array) of kInitialChunkStorage pointers to chunks.
// Each subsequent list as twice as large as the preceding one.
- // Monitor Ids are interpreted as follows:
+ // Monitor Ids are effectively interpreted as follows:
// Top 3 bits (of 28): index into monitor_chunks_.
// Next 16 bits: index into the chunk list, i.e. monitor_chunks_[i].
// Last 9 bits: offset within chunk, expressed as multiple of kMonitorAlignment.
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index 66008f3..64986b6 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -35,12 +35,17 @@
class MonitorTest : public CommonRuntimeTest {
protected:
+ MonitorTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void SetUpRuntimeOptions(RuntimeOptions *options) override {
// Use a smaller heap
SetUpRuntimeOptionsForFillHeap(options);
options->push_back(std::make_pair("-Xint", nullptr));
}
+
public:
std::unique_ptr<Monitor> monitor_;
Handle<mirror::String> object_;
diff --git a/runtime/native/dalvik_system_BaseDexClassLoader.cc b/runtime/native/dalvik_system_BaseDexClassLoader.cc
index 607395d..a4f702c 100644
--- a/runtime/native/dalvik_system_BaseDexClassLoader.cc
+++ b/runtime/native/dalvik_system_BaseDexClassLoader.cc
@@ -19,43 +19,54 @@
#include <memory>
#include "class_loader_context.h"
+#include "class_root-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "native_util.h"
#include "nativehelper/jni_macros.h"
-#include "well_known_classes.h"
+#include "thread-inl.h"
namespace art {
-static bool append_string(JNIEnv* env, jobjectArray array, uint32_t& i, const std::string& string) {
- ScopedLocalRef<jstring> jstring(env, env->NewStringUTF(string.c_str()));
- if (jstring.get() == nullptr) {
- DCHECK(env->ExceptionCheck());
+static bool append_string(Thread* self,
+ Handle<mirror::ObjectArray<mirror::String>> array,
+ uint32_t& i,
+ const std::string& string) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::String> ostring = mirror::String::AllocFromModifiedUtf8(self, string.c_str());
+ if (ostring == nullptr) {
+ DCHECK(self->IsExceptionPending());
return false;
}
- env->SetObjectArrayElement(array, i++, jstring.get());
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(i, ostring);
+ ++i;
return true;
}
static jobjectArray BaseDexClassLoader_computeClassLoaderContextsNative(JNIEnv* env,
jobject class_loader) {
CHECK(class_loader != nullptr);
- std::map<std::string, std::string> contextMap =
+ std::map<std::string, std::string> context_map =
ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
- jobjectArray result = env->NewObjectArray(2 * contextMap.size(),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result == nullptr) {
- DCHECK(env->ExceptionCheck());
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
+ mirror::ObjectArray<mirror::String>::Alloc(
+ self, GetClassRoot<mirror::ObjectArray<mirror::String>>(), 2 * context_map.size()));
+ if (array == nullptr) {
+ DCHECK(self->IsExceptionPending());
return nullptr;
}
uint32_t i = 0;
- for (const auto& classpath_to_context : contextMap) {
+ for (const auto& classpath_to_context : context_map) {
const std::string& classpath = classpath_to_context.first;
const std::string& context = classpath_to_context.second;
- if (!append_string(env, result, i, classpath) || !append_string(env, result, i, context)) {
+ if (!append_string(self, array, i, classpath) || !append_string(self, array, i, context)) {
return nullptr;
}
}
- return result;
+ return soa.AddLocalReference<jobjectArray>(array.Get());
}
static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index ecb9010..9f0c216 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -28,6 +28,7 @@
#include "base/logging.h"
#include "base/os.h"
#include "base/stl_util.h"
+#include "base/transform_iterator.h"
#include "base/utils.h"
#include "base/zip_archive.h"
#include "class_linker.h"
@@ -53,10 +54,19 @@
#include "oat_file_manager.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "string_array_utils.h"
+#include "thread-current-inl.h"
+
+#ifdef ART_TARGET_ANDROID
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+#endif // ART_TARGET_ANDROID
namespace art {
+// Should be the same as dalvik.system.DexFile.ENFORCE_READ_ONLY_JAVA_DCL
+static constexpr uint64_t kEnforceReadOnlyJavaDcl = 218865702;
+
using android::base::StringPrintf;
static bool ConvertJavaArrayToDexFiles(
@@ -303,6 +313,57 @@
return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
}
+#ifdef ART_TARGET_ANDROID
+static bool isReadOnlyJavaDclEnforced(JNIEnv* env) {
+ static bool is_at_least_u = [] {
+ const int api_level = android_get_device_api_level();
+ if (api_level > __ANDROID_API_T__) {
+ return true;
+ } else if (api_level == __ANDROID_API_T__) {
+ // Check if running U preview
+ char value[92] = {0};
+ if (__system_property_get("ro.build.version.preview_sdk", value) >= 0 && atoi(value) > 0) {
+ return true;
+ }
+ }
+ return false;
+ }();
+ if (is_at_least_u) {
+ // The reason why we are calling the AppCompat framework through JVM
+ // instead of directly using the CompatFramework C++ API is because feature
+ // overrides only apply to the Java API.
+ // CtsLibcoreTestCases is part of mainline modules, which requires the same test
+ // to run on older Android versions; the target SDK of CtsLibcoreTestCases is locked
+ // to the lowest supported API level (at the time of writing, it's API 31).
+ // We would need to be able to manually enable the compat change in CTS tests.
+ ScopedLocalRef<jclass> compat(env, env->FindClass("android/compat/Compatibility"));
+ jmethodID mId = env->GetStaticMethodID(compat.get(), "isChangeEnabled", "(J)Z");
+ return env->CallStaticBooleanMethod(compat.get(), mId, kEnforceReadOnlyJavaDcl) == JNI_TRUE;
+ } else {
+ return false;
+ }
+}
+#else // ART_TARGET_ANDROID
+constexpr static bool isReadOnlyJavaDclEnforced(JNIEnv*) {
+ (void)kEnforceReadOnlyJavaDcl;
+ return false;
+}
+#endif // ART_TARGET_ANDROID
+
+static bool isReadOnlyJavaDclChecked() {
+ if (!kIsTargetAndroid) {
+ return false;
+ }
+ const int uid = getuid();
+ // The following UIDs are exempted:
+ // * Root (0): root processes always have write access to files.
+ // * System (1000): /data/app/**.apk are owned by AID_SYSTEM;
+ // loading installed APKs in system_server is allowed.
+ // * Shell (2000): directly calling dalvikvm/app_process in ADB shell
+ // to run JARs with CLI is allowed.
+ return uid != 0 && uid != 1000 && uid != 2000;
+}
+
// TODO(calin): clean up the unused parameters (here and in libcore).
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
@@ -316,6 +377,17 @@
return nullptr;
}
+ if (isReadOnlyJavaDclChecked() && access(sourceName.c_str(), W_OK) == 0) {
+ LOG(ERROR) << "Attempt to load writable dex file: " << sourceName.c_str();
+ if (isReadOnlyJavaDclEnforced(env)) {
+ ScopedLocalRef<jclass> se(env, env->FindClass("java/lang/SecurityException"));
+ std::string message(
+ StringPrintf("Writable dex file '%s' is not allowed.", sourceName.c_str()));
+ env->ThrowNew(se.get(), message.c_str());
+ return nullptr;
+ }
+ }
+
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
std::vector<std::unique_ptr<const DexFile>> dex_files =
@@ -482,23 +554,12 @@
}
// Now create output array and copy the set into it.
- jobjectArray result = env->NewObjectArray(descriptors.size(),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result != nullptr) {
- auto it = descriptors.begin();
- auto it_end = descriptors.end();
- jsize i = 0;
- for (; it != it_end; it++, ++i) {
- std::string descriptor(DescriptorToDot(*it));
- ScopedLocalRef<jstring> jdescriptor(env, env->NewStringUTF(descriptor.c_str()));
- if (jdescriptor.get() == nullptr) {
- return nullptr;
- }
- env->SetObjectArrayElement(result, i, jdescriptor.get());
- }
- }
- return result;
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ auto descriptor_to_dot = [](const char* descriptor) { return DescriptorToDot(descriptor); };
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(
+ soa.Self(),
+ descriptors.size(),
+ MakeTransformRange(descriptors, descriptor_to_dot)));
}
static jint GetDexOptNeeded(JNIEnv* env,
@@ -590,6 +651,8 @@
return nullptr;
}
+ // The API doesn't support passing a class loader context, so skip the class loader context check
+ // and assume that it's OK.
OatFileAssistant oat_file_assistant(filename.c_str(),
target_instruction_set,
/* context= */ nullptr,
@@ -627,23 +690,11 @@
OatFileAssistant::GetOptimizationStatus(
filename.c_str(), target_instruction_set, &compilation_filter, &compilation_reason);
- ScopedLocalRef<jstring> j_compilation_filter(env, env->NewStringUTF(compilation_filter.c_str()));
- if (j_compilation_filter.get() == nullptr) {
- return nullptr;
- }
- ScopedLocalRef<jstring> j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str()));
- if (j_compilation_reason.get() == nullptr) {
- return nullptr;
- }
-
- // Now create output array and copy the set into it.
- jobjectArray result = env->NewObjectArray(2,
- WellKnownClasses::java_lang_String,
- nullptr);
- env->SetObjectArrayElement(result, 0, j_compilation_filter.get());
- env->SetObjectArrayElement(result, 1, j_compilation_reason.get());
-
- return result;
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), {
+ compilation_filter.c_str(),
+ compilation_reason.c_str()
+ }));
}
static jint DexFile_getDexOptNeeded(JNIEnv* env,
@@ -734,6 +785,41 @@
return CompilerFilter::DependsOnProfile(filter) ? JNI_TRUE : JNI_FALSE;
}
+static jboolean DexFile_isVerifiedCompilerFilter(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED,
+ jstring javaCompilerFilter) {
+ ScopedUtfChars compiler_filter(env, javaCompilerFilter);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ CompilerFilter::Filter filter;
+ if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
+ return JNI_FALSE;
+ }
+ return CompilerFilter::IsVerificationEnabled(filter) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean DexFile_isOptimizedCompilerFilter(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED,
+ jstring javaCompilerFilter) {
+ ScopedUtfChars compiler_filter(env, javaCompilerFilter);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ CompilerFilter::Filter filter;
+ if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
+ return JNI_FALSE;
+ }
+ return CompilerFilter::IsAotCompilationEnabled(filter) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean DexFile_isReadOnlyJavaDclEnforced(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED) {
+ return (isReadOnlyJavaDclChecked() && isReadOnlyJavaDclEnforced(env)) ? JNI_TRUE : JNI_FALSE;
+}
+
static jstring DexFile_getNonProfileGuidedCompilerFilter(JNIEnv* env,
jclass javeDexFileClass ATTRIBUTE_UNUSED,
jstring javaCompilerFilter) {
@@ -856,31 +942,16 @@
oat_filename = best_oat_file->GetLocation();
is_vdex_only = best_oat_file->IsBackedByVdexOnly();
}
- ScopedLocalRef<jstring> joatFilename(env, env->NewStringUTF(oat_filename.c_str()));
- if (joatFilename.get() == nullptr) {
- return nullptr;
- }
- if (is_vdex_only) {
- jobjectArray result = env->NewObjectArray(1,
- WellKnownClasses::java_lang_String,
- nullptr);
- env->SetObjectArrayElement(result, 0, joatFilename.get());
- return result;
- } else {
+ const char* filenames[] = { oat_filename.c_str(), nullptr };
+ ArrayRef<const char* const> used_filenames(filenames, 1u);
+ if (!is_vdex_only) {
vdex_filename = GetVdexFilename(oat_filename);
- ScopedLocalRef<jstring> jvdexFilename(env, env->NewStringUTF(vdex_filename.c_str()));
- if (jvdexFilename.get() == nullptr) {
- return nullptr;
- }
-
- jobjectArray result = env->NewObjectArray(2,
- WellKnownClasses::java_lang_String,
- nullptr);
- env->SetObjectArrayElement(result, 0, jvdexFilename.get());
- env->SetObjectArrayElement(result, 1, joatFilename.get());
- return result;
+ filenames[1] = vdex_filename.c_str();
+ used_filenames = ArrayRef<const char* const>(filenames, 2u);
}
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), used_filenames));
}
static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) {
@@ -905,7 +976,7 @@
ScopedObjectAccess soa(env);
// Currently only allow this for debuggable apps.
- if (!runtime->IsJavaDebuggable()) {
+ if (!runtime->IsJavaDebuggableAtInit()) {
ThrowSecurityException("Can't exempt class, process is not debuggable.");
return;
}
@@ -924,55 +995,60 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
- NATIVE_METHOD(DexFile,
- defineClassNative,
- "(Ljava/lang/String;"
- "Ljava/lang/ClassLoader;"
- "Ljava/lang/Object;"
- "Ldalvik/system/DexFile;"
- ")Ljava/lang/Class;"),
- NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
- NATIVE_METHOD(DexFile, getDexOptNeeded,
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
- NATIVE_METHOD(DexFile, openDexFileNative,
- "(Ljava/lang/String;"
- "Ljava/lang/String;"
- "I"
- "Ljava/lang/ClassLoader;"
- "[Ldalvik/system/DexPathList$Element;"
- ")Ljava/lang/Object;"),
- NATIVE_METHOD(DexFile, openInMemoryDexFilesNative,
- "([Ljava/nio/ByteBuffer;"
- "[[B"
- "[I"
- "[I"
- "Ljava/lang/ClassLoader;"
- "[Ldalvik/system/DexPathList$Element;"
- ")Ljava/lang/Object;"),
- NATIVE_METHOD(DexFile, verifyInBackgroundNative,
- "(Ljava/lang/Object;"
- "Ljava/lang/ClassLoader;"
- ")V"),
- NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
- NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
- NATIVE_METHOD(DexFile,
- getNonProfileGuidedCompilerFilter,
- "(Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(DexFile,
- getSafeModeCompilerFilter,
- "(Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
- NATIVE_METHOD(DexFile, getDexFileStatus,
- "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, getDexFileOutputPaths,
- "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
- NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
- "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")
-};
+ NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(DexFile,
+ defineClassNative,
+ "(Ljava/lang/String;"
+ "Ljava/lang/ClassLoader;"
+ "Ljava/lang/Object;"
+ "Ldalvik/system/DexFile;"
+ ")Ljava/lang/Class;"),
+ NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile,
+ getDexOptNeeded,
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
+ NATIVE_METHOD(DexFile,
+ openDexFileNative,
+ "(Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "I"
+ "Ljava/lang/ClassLoader;"
+ "[Ldalvik/system/DexPathList$Element;"
+ ")Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile,
+ openInMemoryDexFilesNative,
+ "([Ljava/nio/ByteBuffer;"
+ "[[B"
+ "[I"
+ "[I"
+ "Ljava/lang/ClassLoader;"
+ "[Ldalvik/system/DexPathList$Element;"
+ ")Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile,
+ verifyInBackgroundNative,
+ "(Ljava/lang/Object;"
+ "Ljava/lang/ClassLoader;"
+ ")V"),
+ NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isVerifiedCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isOptimizedCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isReadOnlyJavaDclEnforced, "()Z"),
+ NATIVE_METHOD(
+ DexFile, getNonProfileGuidedCompilerFilter, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, getSafeModeCompilerFilter, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(
+ DexFile, getDexFileStatus, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile,
+ getDexFileOutputPaths,
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
+ NATIVE_METHOD(DexFile,
+ getDexFileOptimizationStatus,
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")};
void register_dalvik_system_DexFile(JNIEnv* env) {
REGISTER_NATIVE_METHODS("dalvik/system/DexFile");
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 2e09c9f..3653a83 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -27,6 +27,7 @@
#include "base/histogram-inl.h"
#include "base/time_utils.h"
#include "class_linker.h"
+#include "class_root-inl.h"
#include "common_throws.h"
#include "debugger.h"
#include "gc/space/bump_pointer_space.h"
@@ -41,37 +42,26 @@
#include "mirror/array-alloc-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class.h"
-#include "mirror/object_array-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "native_util.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
#include "scoped_fast_native_object_access-inl.h"
+#include "string_array_utils.h"
+#include "thread-inl.h"
#include "trace.h"
-#include "well_known_classes.h"
namespace art {
static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
- static const char* features[] = {
- "method-trace-profiling",
- "method-trace-profiling-streaming",
- "method-sample-profiling",
- "hprof-heap-dump",
- "hprof-heap-dump-streaming",
- };
- jobjectArray result = env->NewObjectArray(arraysize(features),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result != nullptr) {
- for (size_t i = 0; i < arraysize(features); ++i) {
- ScopedLocalRef<jstring> jfeature(env, env->NewStringUTF(features[i]));
- if (jfeature.get() == nullptr) {
- return nullptr;
- }
- env->SetObjectArrayElement(result, i, jfeature.get());
- }
- }
- return result;
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), {
+ "method-trace-profiling",
+ "method-trace-profiling-streaming",
+ "method-sample-profiling",
+ "hprof-heap-dump",
+ "hprof-heap-dump-streaming",
+ }));
}
static void VMDebug_startAllocCounting(JNIEnv*, jclass) {
@@ -178,6 +168,13 @@
return -1;
}
+static void VMDebug_suspendAllAndSendVmStart(JNIEnv*, jclass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // This function will be replaced by the debugger when it's connected. See
+ // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected.
+ ThrowRuntimeException("ART's suspendAllAndSendVmStart is not implemented");
+}
+
static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) {
class DumpClassVisitor : public ClassVisitor {
public:
@@ -373,55 +370,77 @@
}
}
-static bool SetRuntimeStatValue(JNIEnv* env,
- jobjectArray result,
+static bool SetRuntimeStatValue(Thread* self,
+ Handle<mirror::ObjectArray<mirror::String>> array,
VMDebugRuntimeStatId id,
- const std::string& value) {
- ScopedLocalRef<jstring> jvalue(env, env->NewStringUTF(value.c_str()));
- if (jvalue.get() == nullptr) {
+ const std::string& value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::String> ovalue = mirror::String::AllocFromModifiedUtf8(self, value.c_str());
+ if (ovalue == nullptr) {
+ DCHECK(self->IsExceptionPending());
return false;
}
- env->SetObjectArrayElement(result, static_cast<jint>(id), jvalue.get());
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+ static_cast<int32_t>(id), ovalue);
return true;
}
static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) {
- jobjectArray result = env->NewObjectArray(
- static_cast<jint>(VMDebugRuntimeStatId::kNumRuntimeStats),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result == nullptr) {
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1u> hs(self);
+ int32_t size = enum_cast<int32_t>(VMDebugRuntimeStatId::kNumRuntimeStats);
+ Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
+ mirror::ObjectArray<mirror::String>::Alloc(
+ self, GetClassRoot<mirror::ObjectArray<mirror::String>>(), size));
+ if (array == nullptr) {
+ DCHECK(self->IsExceptionPending());
return nullptr;
}
gc::Heap* heap = Runtime::Current()->GetHeap();
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCount,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcGcCount,
std::to_string(heap->GetGcCount()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcTime,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcGcTime,
std::to_string(NsToMs(heap->GetGcTime())))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesAllocated,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBytesAllocated,
std::to_string(heap->GetBytesAllocatedEver()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesFreed,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBytesFreed,
std::to_string(heap->GetBytesFreedEver()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCount,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBlockingGcCount,
std::to_string(heap->GetBlockingGcCount()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcTime,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBlockingGcTime,
std::to_string(NsToMs(heap->GetBlockingGcTime())))) {
return nullptr;
}
{
std::ostringstream output;
heap->DumpGcCountRateHistogram(output);
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcGcCountRateHistogram,
output.str())) {
return nullptr;
}
@@ -429,12 +448,14 @@
{
std::ostringstream output;
heap->DumpBlockingGcCountRateHistogram(output);
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram,
output.str())) {
return nullptr;
}
}
- return result;
+ return soa.AddLocalReference<jobjectArray>(array.Get());
}
static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobject classloader) {
@@ -466,7 +487,7 @@
Runtime* runtime = Runtime::Current();
ScopedObjectAccess soa(env);
- if (!runtime->IsJavaDebuggable()) {
+ if (!runtime->IsJavaDebuggableAtInit()) {
ThrowSecurityException("Can't exempt class, process is not debuggable.");
return;
}
@@ -495,32 +516,33 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
- NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
- NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),
- NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
- NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
- NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
- FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
- NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
- FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
- FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
- NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"),
- FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
- FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
- NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
- NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
- NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
- NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"),
- NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"),
- NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
- NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
- FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
- NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
- NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
- NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
- NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
- NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"),
+ NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
+ NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
+ NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),
+ NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
+ NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
+ NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
+ FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
+ NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
+ FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
+ FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
+ NATIVE_METHOD(VMDebug, suspendAllAndSendVmStart, "()V"),
+ NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"),
+ FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
+ FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
+ NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
+ NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"),
+ NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
+ NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
+ FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
+ NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
+ NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
+ NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
+ NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"),
};
void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index db5d420..74202fc 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -29,6 +29,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include "android-base/properties.h"
#include "arch/instruction_set.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -41,7 +42,7 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_file_types.h"
#include "gc/accounting/card_table-inl.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "gc/heap.h"
#include "gc/space/dlmalloc_space.h"
#include "gc/space/image_space.h"
@@ -60,9 +61,9 @@
#include "runtime.h"
#include "scoped_fast_native_object_access-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "thread.h"
+#include "string_array_utils.h"
+#include "thread-inl.h"
#include "thread_list.h"
-#include "well_known_classes.h"
namespace art {
@@ -189,27 +190,9 @@
}
static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
- DCHECK(WellKnownClasses::java_lang_String != nullptr);
-
const std::vector<std::string>& properties = Runtime::Current()->GetProperties();
- ScopedLocalRef<jobjectArray> ret(env,
- env->NewObjectArray(static_cast<jsize>(properties.size()),
- WellKnownClasses::java_lang_String,
- nullptr /* initial element */));
- if (ret == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
- }
- for (size_t i = 0; i != properties.size(); ++i) {
- ScopedLocalRef<jstring> str(env, env->NewStringUTF(properties[i].c_str()));
- if (str == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
- }
- env->SetObjectArrayElement(ret.get(), static_cast<jsize>(i), str.get());
- DCHECK(!env->ExceptionCheck());
- }
- return ret.release();
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), properties));
}
// This is for backward compatibility with dalvik which returned the
@@ -253,6 +236,13 @@
return down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
}
+static jint VMRuntime_getSdkVersionNative(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED,
+ jint default_sdk_version) {
+ return android::base::GetIntProperty("ro.build.version.sdk",
+ default_sdk_version);
+}
+
static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) {
// This is the target SDK version of the app we're about to run. It is intended that this a place
// where workarounds can be enabled.
@@ -337,31 +327,31 @@
}
static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->Trim(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->Trim(Thread::ForEnv(env));
}
static void VMRuntime_requestHeapTrim(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->RequestTrim(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->RequestTrim(Thread::ForEnv(env));
}
static void VMRuntime_requestConcurrentGC(JNIEnv* env, jobject) {
gc::Heap *heap = Runtime::Current()->GetHeap();
- heap->RequestConcurrentGC(ThreadForEnv(env),
+ heap->RequestConcurrentGC(Thread::ForEnv(env),
gc::kGcCauseBackground,
true,
heap->GetCurrentGcNum());
}
static void VMRuntime_startHeapTaskProcessor(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(Thread::ForEnv(env));
}
static void VMRuntime_stopHeapTaskProcessor(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(Thread::ForEnv(env));
}
static void VMRuntime_runHeapTasks(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(Thread::ForEnv(env));
}
static void VMRuntime_preloadDexCaches(JNIEnv* env ATTRIBUTE_UNUSED, jobject) {
@@ -509,6 +499,41 @@
return ClassLoaderContext::IsValidEncoding(encoded_class_loader_context.c_str());
}
+static jobject VMRuntime_getBaseApkOptimizationInfo(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ AppInfo* app_info = Runtime::Current()->GetAppInfo();
+ DCHECK(app_info != nullptr);
+
+ std::string compiler_filter;
+ std::string compilation_reason;
+ app_info->GetPrimaryApkOptimizationStatus(&compiler_filter, &compilation_reason);
+
+ ScopedLocalRef<jclass> cls(env, env->FindClass("dalvik/system/DexFile$OptimizationInfo"));
+ if (cls == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ jmethodID ctor = env->GetMethodID(cls.get(), "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
+ if (ctor == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ ScopedLocalRef<jstring> j_compiler_filter(env, env->NewStringUTF(compiler_filter.c_str()));
+ if (j_compiler_filter == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ ScopedLocalRef<jstring> j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str()));
+ if (j_compilation_reason == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ return env->NewObject(cls.get(), ctor, j_compiler_filter.get(), j_compilation_reason.get());
+}
+
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -524,6 +549,7 @@
FAST_NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
FAST_NATIVE_METHOD(VMRuntime, newUnpaddedArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(VMRuntime, getSdkVersionNative, "(I)I"),
NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
NATIVE_METHOD(VMRuntime, setDisabledCompatChangesNative, "([J)V"),
NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(J)V"),
@@ -557,6 +583,8 @@
NATIVE_METHOD(VMRuntime, bootCompleted, "()V"),
NATIVE_METHOD(VMRuntime, resetJitCounters, "()V"),
NATIVE_METHOD(VMRuntime, isValidClassLoaderContext, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(VMRuntime, getBaseApkOptimizationInfo,
+ "()Ldalvik/system/DexFile$OptimizationInfo;"),
};
void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index eae7c20..75506de 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -205,7 +205,7 @@
if ((runtime_flags & DEBUG_JAVA_DEBUGGABLE) != 0) {
runtime->AddCompilerOption("--debuggable");
runtime_flags |= DEBUG_GENERATE_MINI_DEBUG_INFO;
- runtime->SetJavaDebuggable(true);
+ runtime->SetRuntimeDebugState(Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
{
// Deoptimize the boot image as it may be non-debuggable.
ScopedSuspendAll ssa(__FUNCTION__);
@@ -257,7 +257,7 @@
runtime->PreZygoteFork();
// Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
- return reinterpret_cast<jlong>(ThreadForEnv(env));
+ return reinterpret_cast<jlong>(Thread::ForEnv(env));
}
static void ZygoteHooks_nativePostZygoteFork(JNIEnv*, jclass) {
@@ -359,6 +359,8 @@
Trace::TraceOutputMode output_mode = Trace::GetOutputMode();
Trace::TraceMode trace_mode = Trace::GetMode();
size_t buffer_size = Trace::GetBufferSize();
+ int flags = Trace::GetFlags();
+ int interval = Trace::GetIntervalInMillis();
// Just drop it.
Trace::Abort();
@@ -382,13 +384,14 @@
proc_name = StringPrintf("%u", static_cast<uint32_t>(pid));
}
- std::string trace_file = StringPrintf("/data/misc/trace/%s.trace.bin", proc_name.c_str());
+ const char* path = kIsTargetBuild ? "/data/misc/trace" : "/tmp";
+ std::string trace_file = StringPrintf("%s/%s.trace.bin", path, proc_name.c_str());
Trace::Start(trace_file.c_str(),
buffer_size,
- 0, // TODO: Expose flags.
+ flags,
output_mode,
trace_mode,
- 0); // TODO: Expose interval.
+ interval);
if (thread->IsExceptionPending()) {
ScopedObjectAccess soa(env);
thread->ClearException();
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index da42e61..593a454 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -19,7 +19,7 @@
#include <iostream>
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/enums.h"
#include "class_linker-inl.h"
#include "class_root-inl.h"
@@ -32,6 +32,7 @@
#include "hidden_api.h"
#include "jni/jni_internal.h"
#include "mirror/class-alloc-inl.h"
+#include "mirror/class_ext.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/field.h"
@@ -53,7 +54,7 @@
#include "reflective_handle_scope-inl.h"
#include "scoped_fast_native_object_access-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -89,14 +90,17 @@
static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
jobject javaLoader) {
ScopedFastNativeObjectAccess soa(env);
- ScopedUtfChars name(env, javaName);
- if (name.c_str() == nullptr) {
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::String> mirror_name = hs.NewHandle(soa.Decode<mirror::String>(javaName));
+ if (mirror_name == nullptr) {
+ soa.Self()->ThrowNewWrappedException("Ljava/lang/NullPointerException;", /*msg=*/ nullptr);
return nullptr;
}
// We need to validate and convert the name (from x.y.z to x/y/z). This
// is especially handy for array types, since we want to avoid
// auto-generating bogus array classes.
+ std::string name = mirror_name->ToModifiedUtf8();
if (!IsValidBinaryClassName(name.c_str())) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
"Invalid name: %s", name.c_str());
@@ -104,29 +108,41 @@
}
std::string descriptor(DotToDescriptor(name.c_str()));
- StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> c(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
- if (c == nullptr) {
- ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
- env->ExceptionClear();
- jthrowable cnfe = reinterpret_cast<jthrowable>(
- env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
- WellKnownClasses::java_lang_ClassNotFoundException_init,
- javaName,
- cause.get()));
+ if (UNLIKELY(c == nullptr)) {
+ StackHandleScope<2> hs2(soa.Self());
+ Handle<mirror::Object> cause = hs2.NewHandle(soa.Self()->GetException());
+ soa.Self()->ClearException();
+ Handle<mirror::Object> cnfe =
+ WellKnownClasses::java_lang_ClassNotFoundException_init->NewObject<'L', 'L'>(
+ hs2, soa.Self(), mirror_name, cause);
if (cnfe != nullptr) {
// Make sure allocation didn't fail with an OOME.
- env->Throw(cnfe);
+ soa.Self()->SetException(ObjPtr<mirror::Throwable>::DownCast(cnfe.Get()));
}
return nullptr;
}
if (initialize) {
class_linker->EnsureInitialized(soa.Self(), c, true, true);
}
+
+ // java.lang.ClassValue was added in Android U, and proguarding tools
+ // used that as justification to remove computeValue method implementation.
+ // Usual pattern was to check that Class.forName("java.lang.ClassValue")
+ // call does not throw and use ClassValue-based implementation or fallback
+ // to other solution if it does throw.
+ // So far ClassValue is the only class with such a problem and hence this
+ // ad-hoc check.
+ // See b/259501764.
+ if (!c->CheckIsVisibleWithTargetSdk(soa.Self())) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ return nullptr;
+ }
+
return soa.AddLocalReference<jclass>(c.Get());
}
@@ -563,7 +579,7 @@
if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
// Return an empty array instead of a null pointer.
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(),
annotation_array_class,
@@ -750,6 +766,71 @@
return soa.AddLocalReference<jclass>(annotations::GetDeclaringClass(klass));
}
+static jclass Class_getNestHostFromAnnotation(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+ if (klass->IsObsoleteObject()) {
+ ThrowRuntimeException("Obsolete Object!");
+ return nullptr;
+ }
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::Class> hostClass = annotations::GetNestHost(klass);
+ if (hostClass == nullptr) {
+ return nullptr;
+ }
+ return soa.AddLocalReference<jclass>(hostClass);
+}
+
+static jobjectArray Class_getNestMembersFromAnnotation(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+ if (klass->IsObsoleteObject()) {
+ ThrowRuntimeException("Obsolete Object!");
+ return nullptr;
+ }
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::ObjectArray<mirror::Class>> classes = annotations::GetNestMembers(klass);
+ if (classes == nullptr) {
+ return nullptr;
+ }
+ return soa.AddLocalReference<jobjectArray>(classes);
+}
+
+static jobjectArray Class_getPermittedSubclassesFromAnnotation(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+ if (klass->IsObsoleteObject()) {
+ ThrowRuntimeException("Obsolete Object!");
+ return nullptr;
+ }
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::ObjectArray<mirror::Class>> classes = annotations::GetPermittedSubclasses(klass);
+ if (classes == nullptr) {
+ return nullptr;
+ }
+ return soa.AddLocalReference<jobjectArray>(classes);
+}
+
+static jobject Class_ensureExtDataPresent(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
+
+ ObjPtr<mirror::Object> extDataPtr =
+ mirror::Class::EnsureExtDataPresent(klass, Thread::Current());
+
+ return soa.AddLocalReference<jobject>(extDataPtr);
+}
+
static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<4> hs(soa.Self());
@@ -841,6 +922,7 @@
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Class, classForName,
"(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
+ FAST_NATIVE_METHOD(Class, ensureExtDataPresent, "()Ldalvik/system/ClassExt;"),
FAST_NATIVE_METHOD(Class, getDeclaredAnnotation,
"(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
FAST_NATIVE_METHOD(Class, getDeclaredAnnotations, "()[Ljava/lang/annotation/Annotation;"),
@@ -865,6 +947,9 @@
FAST_NATIVE_METHOD(Class, getInterfacesInternal, "()[Ljava/lang/Class;"),
FAST_NATIVE_METHOD(Class, getPrimitiveClass, "(Ljava/lang/String;)Ljava/lang/Class;"),
FAST_NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
+ FAST_NATIVE_METHOD(Class, getNestHostFromAnnotation, "()Ljava/lang/Class;"),
+ FAST_NATIVE_METHOD(Class, getNestMembersFromAnnotation, "()[Ljava/lang/Class;"),
+ FAST_NATIVE_METHOD(Class, getPermittedSubclassesFromAnnotation, "()[Ljava/lang/Class;"),
FAST_NATIVE_METHOD(Class, getPublicDeclaredFields, "()[Ljava/lang/reflect/Field;"),
FAST_NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/String;"),
FAST_NATIVE_METHOD(Class, isAnonymousClass, "()Z"),
diff --git a/runtime/native/java_lang_StackStreamFactory.cc b/runtime/native/java_lang_StackStreamFactory.cc
new file mode 100644
index 0000000..f876c10
--- /dev/null
+++ b/runtime/native/java_lang_StackStreamFactory.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 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 "java_lang_StackStreamFactory.h"
+
+#include "nativehelper/jni_macros.h"
+
+#include "jni/jni_internal.h"
+#include "native_util.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "thread.h"
+
+namespace art {
+
+static jobject StackStreamFactory_nativeGetStackAnchor(JNIEnv* env, jclass) {
+ ScopedFastNativeObjectAccess soa(env);
+ return soa.Self()->CreateInternalStackTrace(soa);
+}
+
+static jint StackStreamFactory_nativeFetchStackFrameInfo(JNIEnv* env, jclass,
+ jlong mode, jobject anchor, jint startLevel, jint batchSize, jint startBufferIndex,
+ jobjectArray frameBuffer) {
+ if (anchor == nullptr) {
+ return startLevel;
+ }
+ ScopedFastNativeObjectAccess soa(env);
+ return Thread::InternalStackTraceToStackFrameInfoArray(soa, mode, anchor,
+ startLevel, batchSize, startBufferIndex, frameBuffer);
+}
+
+static JNINativeMethod gMethods[] = {
+ FAST_NATIVE_METHOD(StackStreamFactory, nativeGetStackAnchor, "()Ljava/lang/Object;"),
+ FAST_NATIVE_METHOD(StackStreamFactory, nativeFetchStackFrameInfo, "(JLjava/lang/Object;III[Ljava/lang/Object;)I"),
+};
+
+void register_java_lang_StackStreamFactory(JNIEnv* env) {
+ REGISTER_NATIVE_METHODS("java/lang/StackStreamFactory");
+}
+
+} // namespace art
diff --git a/runtime/native/java_lang_StackStreamFactory.h b/runtime/native/java_lang_StackStreamFactory.h
new file mode 100644
index 0000000..2216871
--- /dev/null
+++ b/runtime/native/java_lang_StackStreamFactory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_java_lang_StackStreamFactory(JNIEnv* env);
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 94ca5b5..f70a188 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -88,6 +88,22 @@
soa.Decode<mirror::String>(java_this)->GetChars(start, end, char_array, index);
}
+static void String_fillBytesLatin1(JNIEnv* env, jobject java_this,
+ jbyteArray buffer, jint byteIndex) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(buffer)));
+ soa.Decode<mirror::String>(java_this)->FillBytesLatin1(byte_array, byteIndex);
+}
+
+static void String_fillBytesUTF16(JNIEnv* env, jobject java_this,
+ jbyteArray buffer, jint byteIndex) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(buffer)));
+ soa.Decode<mirror::String>(java_this)->FillBytesUTF16(byte_array, byteIndex);
+}
+
static jstring String_intern(JNIEnv* env, jobject java_this) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::String> result = soa.Decode<mirror::String>(java_this)->Intern();
@@ -125,6 +141,8 @@
FAST_NATIVE_METHOD(String, doReplace, "(CC)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, fastSubstring, "(II)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, getCharsNoCheck, "(II[CI)V"),
+ FAST_NATIVE_METHOD(String, fillBytesLatin1, "([BI)V"),
+ FAST_NATIVE_METHOD(String, fillBytesUTF16, "([BI)V"),
FAST_NATIVE_METHOD(String, intern, "()Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, toCharArray, "()[C"),
};
diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc
index 9086ee9..2fbebc0 100644
--- a/runtime/native/java_lang_StringFactory.cc
+++ b/runtime/native/java_lang_StringFactory.cc
@@ -56,6 +56,36 @@
return soa.AddLocalReference<jstring>(result);
}
+static jstring StringFactory_newStringFromUtf16Bytes(
+ JNIEnv* env, jclass, jbyteArray java_data, jint offset, jint char_count) {
+ ScopedFastNativeObjectAccess soa(env);
+ if (UNLIKELY(java_data == nullptr)) {
+ ThrowNullPointerException("data == null");
+ return nullptr;
+ }
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(java_data)));
+ int32_t data_size = byte_array->GetLength();
+ DCHECK_GE(data_size, 0);
+ if (offset < 0 ||
+ offset > data_size ||
+ static_cast<uint32_t>(char_count) > (static_cast<uint32_t>(data_size - offset) >> 1)) {
+ soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
+ "length=%d; regionStart=%d; bytePairLength=%d",
+ data_size,
+ offset,
+ char_count);
+ return nullptr;
+ }
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ ObjPtr<mirror::String> result = mirror::String::AllocFromUtf16ByteArray(soa.Self(),
+ char_count,
+ byte_array,
+ offset,
+ allocator_type);
+ return soa.AddLocalReference<jstring>(result);
+}
+
// The char array passed as `java_data` must not be a null reference.
static jstring StringFactory_newStringFromChars(JNIEnv* env, jclass, jint offset,
jint char_count, jcharArray java_data) {
@@ -269,6 +299,7 @@
FAST_NATIVE_METHOD(StringFactory, newStringFromChars, "(II[C)Ljava/lang/String;"),
FAST_NATIVE_METHOD(StringFactory, newStringFromString, "(Ljava/lang/String;)Ljava/lang/String;"),
FAST_NATIVE_METHOD(StringFactory, newStringFromUtf8Bytes, "([BII)Ljava/lang/String;"),
+ FAST_NATIVE_METHOD(StringFactory, newStringFromUtf16Bytes, "([BII)Ljava/lang/String;"),
};
void register_java_lang_StringFactory(JNIEnv* env) {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 11e02a2..b9c72b8 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -18,6 +18,8 @@
#include "base/zip_archive.h"
#include "class_linker.h"
+#include "base/transform_iterator.h"
+#include "base/stl_util.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file_loader.h"
#include "dex/utf.h"
@@ -25,13 +27,16 @@
#include "jni/jni_internal.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "native_util.h"
#include "nativehelper/jni_macros.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
#include "obj_ptr.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
+#include "string_array_utils.h"
+#include "thread-inl.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -49,14 +54,13 @@
}
static ObjPtr<mirror::Class> FindClassInPathClassLoader(ClassLinker* cl,
- ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> result;
- if (cl->FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result)) {
+ if (cl->FindClassInBaseDexClassLoader(self, descriptor, hash, class_loader, &result)) {
DCHECK(!self->IsExceptionPending());
return result;
}
@@ -93,12 +97,9 @@
if (c != nullptr && c->IsErroneous()) {
cl->ThrowEarlierClassFailure(c);
Thread* self = soa.Self();
- ObjPtr<mirror::Class> iae_class =
- self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
- ObjPtr<mirror::Class> ncdfe_class =
- self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
- ObjPtr<mirror::Class> exception = self->GetException()->GetClass();
- if (exception == iae_class || exception == ncdfe_class) {
+ ObjPtr<mirror::Class> exception_class = self->GetException()->GetClass();
+ if (exception_class == WellKnownClasses::java_lang_IllegalAccessError ||
+ exception_class == WellKnownClasses::java_lang_NoClassDefFoundError) {
self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
c->PrettyDescriptor().c_str());
}
@@ -112,7 +113,6 @@
// Try the common case.
StackHandleScope<1> hs(soa.Self());
c = VMClassLoader::FindClassInPathClassLoader(cl,
- soa,
soa.Self(),
descriptor.c_str(),
descriptor_hash,
@@ -131,28 +131,37 @@
* Returns an array of entries from the boot classpath that could contain resources.
*/
static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) {
- const std::vector<const DexFile*>& path =
- Runtime::Current()->GetClassLinker()->GetBootClassPath();
- jobjectArray array =
- env->NewObjectArray(path.size(), WellKnownClasses::java_lang_String, nullptr);
- if (array == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
- }
- for (size_t i = 0; i < path.size(); ++i) {
- const DexFile* dex_file = path[i];
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const std::vector<const DexFile*>& path = class_linker->GetBootClassPath();
+ auto is_base_dex = [](const DexFile* dex_file) {
+ return !DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str());
+ };
+ size_t jar_count = std::count_if(path.begin(), path.end(), is_base_dex);
+ const DexFile* last_dex_file = nullptr;
+ auto dchecked_is_base_dex = [&](const DexFile* dex_file) {
// For multidex locations, e.g., x.jar!classes2.dex, we want to look into x.jar.
- const std::string location(DexFileLoader::GetBaseLocation(dex_file->GetLocation()));
-
- ScopedLocalRef<jstring> javaPath(env, env->NewStringUTF(location.c_str()));
- if (javaPath.get() == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
+ // But we do not need to look into the base dex file more than once so we filter
+ // out multidex locations using the fact that they follow the base location.
+ if (kIsDebugBuild) {
+ if (is_base_dex(dex_file)) {
+ CHECK_EQ(DexFileLoader::GetBaseLocation(dex_file->GetLocation().c_str()),
+ dex_file->GetLocation());
+ } else {
+ CHECK(last_dex_file != nullptr);
+ CHECK_EQ(DexFileLoader::GetBaseLocation(dex_file->GetLocation().c_str()),
+ DexFileLoader::GetBaseLocation(last_dex_file->GetLocation().c_str()));
+ }
+ last_dex_file = dex_file;
}
- env->SetObjectArrayElement(array, i, javaPath.get());
- }
- return array;
+ return is_base_dex(dex_file);
+ };
+ auto get_location = [](const DexFile* dex_file) { return dex_file->GetLocation(); };
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(
+ soa.Self(),
+ jar_count,
+ MakeTransformRange(Filter(path, dchecked_is_base_dex), get_location)));
}
static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 1d362c0..4b2cc43 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -33,7 +33,6 @@
#include "native_util.h"
#include "reflection.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
namespace art {
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index fef16b9..87c9f6c 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -19,7 +19,7 @@
#include "android-base/stringprintf.h"
#include "nativehelper/jni_macros.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "class_root-inl.h"
#include "dex/dex_file_annotations.h"
#include "handle.h"
@@ -45,7 +45,7 @@
if (method->GetDeclaringClass()->IsProxyClass()) {
// Return an empty array instead of a null pointer.
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
return soa.AddLocalReference<jobjectArray>(empty_array);
@@ -128,7 +128,7 @@
// Workaround for dexers (d8/dx) that do not insert annotations
// for implicit parameters (b/68033708).
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
Handle<mirror::ObjectArray<mirror::Object>> empty_annotations = hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0));
if (empty_annotations.IsNull()) {
@@ -157,7 +157,7 @@
static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
Thread* self = soa.Self();
- StackHandleScope<8> hs(self);
+ StackHandleScope<6> hs(self);
Handle<mirror::Method> executable = hs.NewHandle(soa.Decode<mirror::Method>(javaMethod));
ArtMethod* art_method = executable.Get()->GetArtMethod();
@@ -197,57 +197,37 @@
// Instantiate a Parameter[] to hold the result.
Handle<mirror::Class> parameter_array_class =
hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Parameter__array));
- Handle<mirror::ObjectArray<mirror::Object>> parameter_array =
- hs.NewHandle(
- mirror::ObjectArray<mirror::Object>::Alloc(self,
- parameter_array_class.Get(),
- names_count));
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_reflect_Parameter__array));
+ Handle<mirror::ObjectArray<mirror::Object>> parameter_array = hs.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(self, parameter_array_class.Get(), names_count));
if (UNLIKELY(parameter_array == nullptr)) {
self->AssertPendingException();
return nullptr;
}
- Handle<mirror::Class> parameter_class =
- hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Parameter));
- ArtMethod* parameter_init =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Parameter_init);
+ ArtMethod* parameter_init = WellKnownClasses::java_lang_reflect_Parameter_init;
// Mutable handles used in the loop below to ensure cleanup without scaling the number of
// handles by the number of parameters.
MutableHandle<mirror::String> name = hs.NewHandle<mirror::String>(nullptr);
- MutableHandle<mirror::Object> parameter = hs.NewHandle<mirror::Object>(nullptr);
// Populate the Parameter[] to return.
for (int32_t parameter_index = 0; parameter_index < names_count; parameter_index++) {
name.Assign(names.Get()->Get(parameter_index));
int32_t modifiers = access_flags.Get()->Get(parameter_index);
- // Allocate / initialize the Parameter to add to parameter_array.
- parameter.Assign(parameter_class->AllocObject(self));
+ // Create the Parameter to add to parameter_array.
+ ObjPtr<mirror::Object> parameter = parameter_init->NewObject<'L', 'I', 'L', 'I'>(
+ self, name, modifiers, executable, parameter_index);
if (UNLIKELY(parameter == nullptr)) {
- self->AssertPendingOOMException();
+ DCHECK(self->IsExceptionPending());
return nullptr;
}
- uint32_t args[5] = { PointerToLowMemUInt32(parameter.Get()),
- PointerToLowMemUInt32(name.Get()),
- static_cast<uint32_t>(modifiers),
- PointerToLowMemUInt32(executable.Get()),
- static_cast<uint32_t>(parameter_index)
- };
- JValue result;
- static const char* method_signature = "VLILI"; // return + parameter types
- parameter_init->Invoke(self, args, sizeof(args), &result, method_signature);
- if (UNLIKELY(self->IsExceptionPending())) {
- return nullptr;
- }
-
- // Store the Parameter in the Parameter[].
- parameter_array.Get()->Set(parameter_index, parameter.Get());
- if (UNLIKELY(self->IsExceptionPending())) {
- return nullptr;
- }
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ parameter_array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+ parameter_index, parameter);
}
return soa.AddLocalReference<jobjectArray>(parameter_array.Get());
}
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index cfc4a29..946a4e4 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -491,7 +491,7 @@
if (field->GetDeclaringClass()->IsProxyClass()) {
// Return an empty array instead of a null pointer.
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
return soa.AddLocalReference<jobjectArray>(empty_array);
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index 2c0dd80..5f02ad0 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -31,7 +31,6 @@
#include "native_util.h"
#include "reflection.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
namespace art {
@@ -80,6 +79,7 @@
}
}
+NO_STACK_PROTECTOR
static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver,
jobjectArray javaArgs) {
ScopedFastNativeObjectAccess soa(env);
diff --git a/runtime/native/jdk_internal_misc_Unsafe.cc b/runtime/native/jdk_internal_misc_Unsafe.cc
index e708732..6e2f558 100644
--- a/runtime/native/jdk_internal_misc_Unsafe.cc
+++ b/runtime/native/jdk_internal_misc_Unsafe.cc
@@ -34,7 +34,7 @@
#include "art_field-inl.h"
#include "native_util.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -92,8 +92,12 @@
return Unsafe_compareAndSetLong(env, obj, javaObj, offset, expectedValue, newValue);
}
-static jboolean Unsafe_compareAndSetObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
- jobject javaExpectedValue, jobject javaNewValue) {
+static jboolean Unsafe_compareAndSetReference(JNIEnv* env,
+ jobject,
+ jobject javaObj,
+ jlong offset,
+ jobject javaExpectedValue,
+ jobject javaNewValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> expectedValue = soa.Decode<mirror::Object>(javaExpectedValue);
@@ -122,10 +126,10 @@
static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject obj, jobject javaObj, jlong offset,
jobject javaExpectedValue, jobject javaNewValue) {
- // compareAndSetObject has the same semantics as compareAndSwapObject, except for
+ // compareAndSetReference has the same semantics as compareAndSwapObject, except for
// being strict (volatile). Since this was implemented in a strict mode it can
// just call the volatile version unless it gets relaxed.
- return Unsafe_compareAndSetObject(env, obj, javaObj, offset, javaExpectedValue, javaNewValue);
+ return Unsafe_compareAndSetReference(env, obj, javaObj, offset, javaExpectedValue, javaNewValue);
}
static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
@@ -201,22 +205,22 @@
obj->SetField64<false>(MemberOffset(offset), newValue);
}
-static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+static jobject Unsafe_getReferenceVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> value = obj->GetFieldObjectVolatile<mirror::Object>(MemberOffset(offset));
return soa.AddLocalReference<jobject>(value);
}
-static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+static jobject Unsafe_getReference(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> value = obj->GetFieldObject<mirror::Object>(MemberOffset(offset));
return soa.AddLocalReference<jobject>(value);
}
-static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
- jobject javaNewValue) {
+static void Unsafe_putReference(
+ JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
@@ -224,8 +228,8 @@
obj->SetFieldObject<false>(MemberOffset(offset), newValue);
}
-static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
- jobject javaNewValue) {
+static void Unsafe_putReferenceVolatile(
+ JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
@@ -474,12 +478,14 @@
static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
art::ScopedFastNativeObjectAccess soa(env);
- if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+ ObjPtr<mirror::Object> mirror_thread = soa.Decode<mirror::Object>(jthread);
+ if (mirror_thread == nullptr ||
+ !mirror_thread->InstanceOf(WellKnownClasses::java_lang_Thread.Get())) {
ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
return;
}
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
- art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, mirror_thread);
if (thread != nullptr) {
thread->Unpark();
} else {
@@ -487,80 +493,82 @@
// or the thread has already terminated. Setting the field to true will be
// respected when the thread does start, and is harmless if the thread has
// already terminated.
- ArtField* unparked =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
// JNI must use non transactional mode.
- unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+ unparked->SetBoolean<false>(mirror_thread, JNI_TRUE);
}
}
static JNINativeMethod gMethods[] = {
- FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSetInt, "(Ljava/lang/Object;JII)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSetLong, "(Ljava/lang/Object;JJJ)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSetObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
- FAST_NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"),
- FAST_NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"),
- FAST_NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"),
- FAST_NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- FAST_NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- FAST_NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"),
- FAST_NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"),
- FAST_NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"),
- FAST_NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"),
- FAST_NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- FAST_NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- FAST_NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- FAST_NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "(Ljava/lang/Class;)I"),
- FAST_NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "(Ljava/lang/Class;)I"),
- FAST_NATIVE_METHOD(Unsafe, addressSize, "()I"),
- FAST_NATIVE_METHOD(Unsafe, pageSize, "()I"),
- FAST_NATIVE_METHOD(Unsafe, allocateMemory, "(J)J"),
- FAST_NATIVE_METHOD(Unsafe, freeMemory, "(J)V"),
- FAST_NATIVE_METHOD(Unsafe, setMemory, "(JJB)V"),
- FAST_NATIVE_METHOD(Unsafe, copyMemory0, "(Ljava/lang/Object;JLjava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, getBoolean, "(Ljava/lang/Object;J)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
+ FAST_NATIVE_METHOD(
+ Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSetInt, "(Ljava/lang/Object;JII)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSetLong, "(Ljava/lang/Object;JJJ)Z"),
+ FAST_NATIVE_METHOD(Unsafe,
+ compareAndSetReference,
+ "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
+ FAST_NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"),
+ FAST_NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"),
+ FAST_NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"),
+ FAST_NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getReferenceVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ FAST_NATIVE_METHOD(Unsafe, putReferenceVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ FAST_NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"),
+ FAST_NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"),
+ FAST_NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"),
+ FAST_NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"),
+ FAST_NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getReference, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ FAST_NATIVE_METHOD(Unsafe, putReference, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ FAST_NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ FAST_NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "(Ljava/lang/Class;)I"),
+ FAST_NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "(Ljava/lang/Class;)I"),
+ FAST_NATIVE_METHOD(Unsafe, addressSize, "()I"),
+ FAST_NATIVE_METHOD(Unsafe, pageSize, "()I"),
+ FAST_NATIVE_METHOD(Unsafe, allocateMemory, "(J)J"),
+ FAST_NATIVE_METHOD(Unsafe, freeMemory, "(J)V"),
+ FAST_NATIVE_METHOD(Unsafe, setMemory, "(JJB)V"),
+ FAST_NATIVE_METHOD(Unsafe, copyMemory0, "(Ljava/lang/Object;JLjava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getBoolean, "(Ljava/lang/Object;J)Z"),
- FAST_NATIVE_METHOD(Unsafe, getByte, "(Ljava/lang/Object;J)B"),
- FAST_NATIVE_METHOD(Unsafe, getChar, "(Ljava/lang/Object;J)C"),
- FAST_NATIVE_METHOD(Unsafe, getShort, "(Ljava/lang/Object;J)S"),
- FAST_NATIVE_METHOD(Unsafe, getFloat, "(Ljava/lang/Object;J)F"),
- FAST_NATIVE_METHOD(Unsafe, getDouble, "(Ljava/lang/Object;J)D"),
- FAST_NATIVE_METHOD(Unsafe, putBoolean, "(Ljava/lang/Object;JZ)V"),
- FAST_NATIVE_METHOD(Unsafe, putByte, "(Ljava/lang/Object;JB)V"),
- FAST_NATIVE_METHOD(Unsafe, putChar, "(Ljava/lang/Object;JC)V"),
- FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"),
- FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"),
- FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"),
- FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"),
- NATIVE_METHOD(Unsafe, park, "(ZJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getByte, "(Ljava/lang/Object;J)B"),
+ FAST_NATIVE_METHOD(Unsafe, getChar, "(Ljava/lang/Object;J)C"),
+ FAST_NATIVE_METHOD(Unsafe, getShort, "(Ljava/lang/Object;J)S"),
+ FAST_NATIVE_METHOD(Unsafe, getFloat, "(Ljava/lang/Object;J)F"),
+ FAST_NATIVE_METHOD(Unsafe, getDouble, "(Ljava/lang/Object;J)D"),
+ FAST_NATIVE_METHOD(Unsafe, putBoolean, "(Ljava/lang/Object;JZ)V"),
+ FAST_NATIVE_METHOD(Unsafe, putByte, "(Ljava/lang/Object;JB)V"),
+ FAST_NATIVE_METHOD(Unsafe, putChar, "(Ljava/lang/Object;JC)V"),
+ FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"),
+ FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"),
+ FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"),
+ FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, park, "(ZJ)V"),
- // Each of the getFoo variants are overloaded with a call that operates
- // directively on a native pointer.
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getByte, "(J)B", getByteJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getChar, "(J)C", getCharJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getShort, "(J)S", getShortJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getInt, "(J)I", getIntJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getLong, "(J)J", getLongJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getFloat, "(J)F", getFloatJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getDouble, "(J)D", getDoubleJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putByte, "(JB)V", putByteJB),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putChar, "(JC)V", putCharJC),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putShort, "(JS)V", putShortJS),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putInt, "(JI)V", putIntJI),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putLong, "(JJ)V", putLongJJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putFloat, "(JF)V", putFloatJF),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putDouble, "(JD)V", putDoubleJD),
+ // Each of the getFoo variants are overloaded with a call that operates
+ // directively on a native pointer.
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getByte, "(J)B", getByteJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getChar, "(J)C", getCharJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getShort, "(J)S", getShortJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getInt, "(J)I", getIntJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getLong, "(J)J", getLongJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getFloat, "(J)F", getFloatJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getDouble, "(J)D", getDoubleJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putByte, "(JB)V", putByteJB),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putChar, "(JC)V", putCharJC),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putShort, "(JS)V", putShortJS),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putInt, "(JI)V", putIntJI),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putLong, "(JJ)V", putLongJJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putFloat, "(JF)V", putFloatJF),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putDouble, "(JD)V", putDoubleJD),
- // CAS
- FAST_NATIVE_METHOD(Unsafe, loadFence, "()V"),
- FAST_NATIVE_METHOD(Unsafe, storeFence, "()V"),
- FAST_NATIVE_METHOD(Unsafe, fullFence, "()V"),
+ // CAS
+ FAST_NATIVE_METHOD(Unsafe, loadFence, "()V"),
+ FAST_NATIVE_METHOD(Unsafe, storeFence, "()V"),
+ FAST_NATIVE_METHOD(Unsafe, fullFence, "()V"),
};
void register_jdk_internal_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
index 92f355e..c53fd6e 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -50,24 +50,6 @@
}
}
-static void CharsetUtils_isoLatin1BytesToChars(JNIEnv* env, jclass, jbyteArray javaBytes,
- jint offset, jint length, jcharArray javaChars) {
- ScopedByteArrayRO bytes(env, javaBytes);
- if (bytes.get() == nullptr) {
- return;
- }
- ScopedCharArrayRW chars(env, javaChars);
- if (chars.get() == nullptr) {
- return;
- }
-
- const jbyte* src = &bytes[offset];
- jchar* dst = &chars[0];
- for (int i = length - 1; i >= 0; --i) {
- *dst++ = static_cast<jchar>(*src++ & 0xff);
- }
-}
-
/**
* Translates the given characters to US-ASCII or ISO-8859-1 bytes, using the fact that
* Unicode code points between U+0000 and U+007f inclusive are identical to US-ASCII, while
@@ -157,7 +139,6 @@
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(CharsetUtils, asciiBytesToChars, "([BII[C)V"),
- FAST_NATIVE_METHOD(CharsetUtils, isoLatin1BytesToChars, "([BII[C)V"),
FAST_NATIVE_METHOD(CharsetUtils, toAsciiBytes, "(Ljava/lang/String;II)[B"),
FAST_NATIVE_METHOD(CharsetUtils, toIsoLatin1Bytes, "(Ljava/lang/String;II)[B"),
FAST_NATIVE_METHOD(CharsetUtils, toUtf8Bytes, "(Ljava/lang/String;II)[B"),
diff --git a/runtime/native/string_array_utils.h b/runtime/native/string_array_utils.h
new file mode 100644
index 0000000..41d5093
--- /dev/null
+++ b/runtime/native/string_array_utils.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_STRING_ARRAY_UTILS_H_
+#define ART_RUNTIME_NATIVE_STRING_ARRAY_UTILS_H_
+
+#include "base/locks.h"
+#include "class_root-inl.h"
+#include "handle_scope-inl.h"
+#include "mirror/object_array-alloc-inl.h"
+#include "mirror/string.h"
+
+namespace art {
+
+namespace detail {
+
+inline const char* GetStringCStr(const char* str) { return str; }
+inline const char* GetStringCStr(const std::string& str) { return str.c_str(); }
+
+} // namespace detail
+
+template <typename Container>
+ObjPtr<mirror::ObjectArray<mirror::String>> CreateStringArray(
+ Thread* self, size_t size, const Container& entries) REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
+ mirror::ObjectArray<mirror::String>::Alloc(
+ self, GetClassRoot<mirror::ObjectArray<mirror::String>>(), size));
+ if (array == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ // Note: If the container's iterator returns a `std::string` by value, the `auto&&`
+ // binds as a const reference and extends the lifetime of the temporary object.
+ size_t pos = 0u;
+ for (auto&& entry : entries) {
+ ObjPtr<mirror::String> oentry =
+ mirror::String::AllocFromModifiedUtf8(self, detail::GetStringCStr(entry));
+ if (oentry == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ DCHECK_LT(pos, size);
+ array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+ pos, oentry);
+ ++pos;
+ }
+ DCHECK_EQ(pos, size);
+ return array.Get();
+}
+
+template <typename Container>
+ObjPtr<mirror::ObjectArray<mirror::String>> CreateStringArray(
+ Thread* self, const Container& entries) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return CreateStringArray(self, entries.size(), entries);
+}
+
+inline ObjPtr<mirror::ObjectArray<mirror::String>> CreateStringArray(
+ Thread* self, std::initializer_list<const char*> entries)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return CreateStringArray<std::initializer_list<const char*>>(self, entries);
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_STRING_ARRAY_UTILS_H_
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 1781a29..8a203ce 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -34,7 +34,7 @@
#include "art_field-inl.h"
#include "native_util.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -521,12 +521,14 @@
static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
art::ScopedFastNativeObjectAccess soa(env);
- if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+ ObjPtr<mirror::Object> mirror_thread = soa.Decode<mirror::Object>(jthread);
+ if (mirror_thread == nullptr ||
+ !mirror_thread->InstanceOf(WellKnownClasses::java_lang_Thread.Get())) {
ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
return;
}
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
- art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, mirror_thread);
if (thread != nullptr) {
thread->Unpark();
} else {
@@ -534,10 +536,9 @@
// or the thread has already terminated. Setting the field to true will be
// respected when the thread does start, and is harmless if the thread has
// already terminated.
- ArtField* unparked =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
// JNI must use non transactional mode.
- unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+ unparked->SetBoolean<false>(mirror_thread, JNI_TRUE);
}
}
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index b7c3665..be41a68 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -18,14 +18,14 @@
#include <memory>
#include <ostream>
+#include <string_view>
#include <stdio.h>
#include "art_method.h"
// For DumpNativeStack.
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
+#include <unwindstack/AndroidUnwinder.h>
#if defined(__linux__)
@@ -69,13 +69,16 @@
static constexpr bool kUseAddr2line = !kIsTargetBuild;
std::string FindAddr2line() {
-#ifdef ART_CLANG_PATH
+#if !defined(ART_TARGET) && !defined(ART_CLANG_PATH)
+ #error "ART_CLANG_PATH must be defined on host build"
+#endif
+#if defined(ART_CLANG_PATH)
const char* env_value = getenv("ANDROID_BUILD_TOP");
if (env_value != nullptr) {
return std::string(env_value) + "/" + ART_CLANG_PATH + "/bin/llvm-addr2line";
}
#endif
- return std::string("/usr/bin/addr2line");
+ return std::string("llvm-addr2line");
}
ALWAYS_INLINE
@@ -298,50 +301,51 @@
}
}
-static bool PcIsWithinQuickCode(ArtMethod* method, uintptr_t pc) NO_THREAD_SAFETY_ANALYSIS {
- const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
- if (entry_point == nullptr) {
- return pc == 0;
+// Remove method parameters by finding matching top-level parenthesis and removing them.
+// Since functions can be defined inside functions, this can remove multiple substrings.
+std::string StripParameters(std::string name) {
+ size_t end = name.size();
+ int nesting = 0;
+ for (ssize_t i = name.size() - 1; i > 0; i--) {
+ if (name[i] == ')' && nesting++ == 0) {
+ end = i + 1;
+ }
+ if (name[i] == '(' && --nesting == 0) {
+ name = name.erase(i, end - i);
+ }
}
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (class_linker->IsQuickGenericJniStub(entry_point) ||
- class_linker->IsQuickResolutionStub(entry_point) ||
- class_linker->IsQuickToInterpreterBridge(entry_point)) {
- return false;
- }
- // The backtrace library might have heuristically subracted instruction
- // size from the pc, to pretend the pc is at the calling instruction.
- if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) - pc <= 4) {
- return false;
- }
- uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(entry_point));
- uintptr_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].GetCodeSize();
- return code <= pc && pc <= (code + code_size);
+ return name;
}
void DumpNativeStack(std::ostream& os,
pid_t tid,
- BacktraceMap* existing_map,
+ const char* prefix,
+ ArtMethod* current_method,
+ void* ucontext_ptr,
+ bool skip_frames) {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ DumpNativeStack(os, unwinder, tid, prefix, current_method, ucontext_ptr, skip_frames);
+}
+
+void DumpNativeStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ pid_t tid,
const char* prefix,
ArtMethod* current_method,
void* ucontext_ptr,
bool skip_frames) {
// Historical note: This was disabled when running under Valgrind (b/18119146).
- BacktraceMap* map = existing_map;
- std::unique_ptr<BacktraceMap> tmp_map;
- if (map == nullptr) {
- tmp_map.reset(BacktraceMap::Create(getpid()));
- map = tmp_map.get();
+ unwindstack::AndroidUnwinderData data(!skip_frames /*show_all_frames*/);
+ bool unwind_ret;
+ if (ucontext_ptr != nullptr) {
+ unwind_ret = unwinder.Unwind(ucontext_ptr, data);
+ } else {
+ unwind_ret = unwinder.Unwind(tid, data);
}
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map));
- backtrace->SetSkipFrames(skip_frames);
- if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
- os << prefix << "(backtrace::Unwind failed for thread " << tid
- << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl;
- return;
- } else if (backtrace->NumFrames() == 0) {
- os << prefix << "(no native stack frames for thread " << tid << ")" << std::endl;
+ if (!unwind_ret) {
+ os << prefix << "(Unwind failed for thread " << tid << ": "
+ << data.GetErrorString() << ")" << std::endl;
return;
}
@@ -356,9 +360,9 @@
}
std::unique_ptr<Addr2linePipe> addr2line_state;
-
- for (Backtrace::const_iterator it = backtrace->begin();
- it != backtrace->end(); ++it) {
+ data.DemangleFunctionNames();
+ bool holds_mutator_lock = Locks::mutator_lock_->IsSharedHeld(Thread::Current());
+ for (const unwindstack::FrameData& frame : data.frames) {
// We produce output like this:
// ] #00 pc 000075bb8 /system/lib/libc.so (unwind_backtrace_thread+536)
// In order for parsing tools to continue to function, the stack dump
@@ -367,53 +371,57 @@
// The parsers require a single space before and after pc, and two spaces
// after the <RELATIVE_ADDR>. There can be any prefix data before the
// #XX. <RELATIVE_ADDR> has to be a hex number but with no 0x prefix.
- os << prefix << StringPrintf("#%02zu pc ", it->num);
+ os << prefix << StringPrintf("#%02zu pc ", frame.num);
bool try_addr2line = false;
- if (!BacktraceMap::IsValid(it->map)) {
- os << StringPrintf(Is64BitInstructionSet(kRuntimeISA) ? "%016" PRIx64 " ???"
- : "%08" PRIx64 " ???",
- it->pc);
+ if (frame.map_info == nullptr) {
+ os << StringPrintf("%08" PRIx64 " ???", frame.pc);
} else {
- os << StringPrintf(Is64BitInstructionSet(kRuntimeISA) ? "%016" PRIx64 " "
- : "%08" PRIx64 " ",
- it->rel_pc);
- if (it->map.name.empty()) {
- os << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start);
+ os << StringPrintf("%08" PRIx64 " ", frame.rel_pc);
+ const std::shared_ptr<unwindstack::MapInfo>& map_info = frame.map_info;
+ if (map_info->name().empty()) {
+ os << StringPrintf("<anonymous:%" PRIx64 ">", map_info->start());
} else {
- os << it->map.name;
+ os << map_info->name().c_str();
}
- if (it->map.offset != 0) {
- os << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
+ if (map_info->elf_start_offset() != 0) {
+ os << StringPrintf(" (offset %" PRIx64 ")", map_info->elf_start_offset());
}
os << " (";
- if (!it->func_name.empty()) {
- os << it->func_name;
- if (it->func_offset != 0) {
- os << "+" << it->func_offset;
+ if (!frame.function_name.empty()) {
+ // Remove parameters from the printed function name to improve signal/noise in the logs.
+ // Also, ANRs are often trimmed, so printing less means we get more useful data out.
+ // We can still symbolize the function based on the PC and build-id (including inlining).
+ os << StripParameters(frame.function_name.c_str());
+ if (frame.function_offset != 0) {
+ os << "+" << frame.function_offset;
}
// Functions found using the gdb jit interface will be in an empty
// map that cannot be found using addr2line.
- if (!it->map.name.empty()) {
+ if (!map_info->name().empty()) {
try_addr2line = true;
}
- } else if (current_method != nullptr &&
- Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
- PcIsWithinQuickCode(current_method, it->pc)) {
- const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode();
- os << current_method->JniLongName() << "+"
- << (it->pc - reinterpret_cast<uint64_t>(start_of_code));
+ } else if (current_method != nullptr && holds_mutator_lock) {
+ const OatQuickMethodHeader* header = current_method->GetOatQuickMethodHeader(frame.pc);
+ if (header != nullptr) {
+ const void* start_of_code = header->GetCode();
+ os << current_method->JniLongName() << "+"
+ << (frame.pc - reinterpret_cast<uint64_t>(start_of_code));
+ } else {
+ os << "???";
+ }
} else {
os << "???";
}
os << ")";
- std::string build_id = map->GetBuildId(it->pc);
+ std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
os << " (BuildId: " << build_id << ")";
}
}
os << std::endl;
if (try_addr2line && use_addr2line) {
- Addr2line(it->map.name, it->rel_pc, os, prefix, &addr2line_state);
+ // Guaranteed that map_info is not nullptr and name is non-empty.
+ Addr2line(frame.map_info->name(), frame.rel_pc, os, prefix, &addr2line_state);
}
}
@@ -426,7 +434,15 @@
void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,
pid_t tid ATTRIBUTE_UNUSED,
- BacktraceMap* existing_map ATTRIBUTE_UNUSED,
+ const char* prefix ATTRIBUTE_UNUSED,
+ ArtMethod* current_method ATTRIBUTE_UNUSED,
+ void* ucontext_ptr ATTRIBUTE_UNUSED,
+ bool skip_frames ATTRIBUTE_UNUSED) {
+}
+
+void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,
+ unwindstack::AndroidLocalUnwinder& existing_map ATTRIBUTE_UNUSED,
+ pid_t tid ATTRIBUTE_UNUSED,
const char* prefix ATTRIBUTE_UNUSED,
ArtMethod* current_method ATTRIBUTE_UNUSED,
void* ucontext_ptr ATTRIBUTE_UNUSED,
diff --git a/runtime/native_stack_dump.h b/runtime/native_stack_dump.h
index 4d4b36b..86a8ce2 100644
--- a/runtime/native_stack_dump.h
+++ b/runtime/native_stack_dump.h
@@ -23,16 +23,30 @@
#include "base/macros.h"
-class BacktraceMap;
+namespace unwindstack {
+class AndroidLocalUnwinder;
+} // namespace unwindstack
namespace art {
class ArtMethod;
+// Remove method parameters by finding matching top-level parenthesis and removing them.
+// Since functions can be defined inside functions, this can remove multiple substrings.
+std::string StripParameters(std::string name);
+
// Dumps the native stack for thread 'tid' to 'os'.
void DumpNativeStack(std::ostream& os,
pid_t tid,
- BacktraceMap* map = nullptr,
+ const char* prefix = "",
+ ArtMethod* current_method = nullptr,
+ void* ucontext = nullptr,
+ bool skip_frames = true)
+ NO_THREAD_SAFETY_ANALYSIS;
+
+void DumpNativeStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ pid_t tid,
const char* prefix = "",
ArtMethod* current_method = nullptr,
void* ucontext = nullptr,
diff --git a/runtime/native_stack_dump_test.cc b/runtime/native_stack_dump_test.cc
new file mode 100644
index 0000000..4446495
--- /dev/null
+++ b/runtime/native_stack_dump_test.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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 "native_stack_dump.h"
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+TEST(StripParametersTest, ValidInput) {
+ EXPECT_EQ(StripParameters("foo(int)"), "foo");
+ EXPECT_EQ(StripParameters("foo(int, std::string)"), "foo");
+ EXPECT_EQ(StripParameters("foo(int) const"), "foo const");
+ EXPECT_EQ(StripParameters("foo(int)::bar(int)"), "foo::bar");
+ EXPECT_EQ(StripParameters("foo<int>(int)"), "foo<int>");
+}
+
+TEST(StripParametersTest, InvalidInput) {
+ EXPECT_EQ(StripParameters("foo(int?"), "foo(int?");
+ EXPECT_EQ(StripParameters("foo?int)"), "foo?int)");
+ EXPECT_EQ(StripParameters("(foo(int)"), "(foo");
+ EXPECT_EQ(StripParameters(")foo(int)"), ")foo");
+ EXPECT_EQ(StripParameters("foo(((int)))"), "foo");
+}
+
+} // namespace art
diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc
index 12afa3a..fb2046c 100644
--- a/runtime/nterp_helpers.cc
+++ b/runtime/nterp_helpers.cc
@@ -120,9 +120,7 @@
static_cast<size_t>(InstructionSetPointerSize(isa));
}
-static uint16_t GetNumberOfOutRegs(ArtMethod* method, InstructionSet isa)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- CodeItemDataAccessor accessor(method->DexInstructionData());
+static uint16_t GetNumberOfOutRegs(const CodeItemDataAccessor& accessor, InstructionSet isa) {
uint16_t out_regs = accessor.OutsSize();
switch (isa) {
case InstructionSet::kX86: {
@@ -136,14 +134,23 @@
return out_regs;
}
-size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa) {
+static uint16_t GetNumberOfOutRegs(ArtMethod* method, InstructionSet isa)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CodeItemDataAccessor accessor(method->DexInstructionData());
+ return GetNumberOfOutRegs(accessor, isa);
+}
+
+// Note: There may be two pieces of alignment but there is no need to align
+// out args to `kPointerSize` separately before aligning to kStackAlignment.
+// This allows using the size without padding for the maximum frame size check
+// in `CanMethodUseNterp()`.
+static size_t NterpGetFrameSizeWithoutPadding(ArtMethod* method, InstructionSet isa)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
CodeItemDataAccessor accessor(method->DexInstructionData());
const uint16_t num_regs = accessor.RegistersSize();
- const uint16_t out_regs = GetNumberOfOutRegs(method, isa);
+ const uint16_t out_regs = GetNumberOfOutRegs(accessor, isa);
size_t pointer_size = static_cast<size_t>(InstructionSetPointerSize(isa));
- // Note: There may be two pieces of alignment but there is no need to align
- // out args to `kPointerSize` separately before aligning to kStackAlignment.
DCHECK(IsAlignedParam(kStackAlignment, pointer_size));
DCHECK(IsAlignedParam(NterpGetFrameEntrySize(isa), pointer_size));
DCHECK(IsAlignedParam(kVRegSize * 2, pointer_size));
@@ -154,7 +161,13 @@
pointer_size + // saved dex pc
(out_regs * kVRegSize) + // out arguments
pointer_size; // method
- return RoundUp(frame_size, kStackAlignment);
+ return frame_size;
+}
+
+// The frame size nterp will use for the given method.
+static inline size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return RoundUp(NterpGetFrameSizeWithoutPadding(method, isa), kStackAlignment);
}
QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) {
@@ -162,7 +175,7 @@
RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
uint32_t fp_spills =
RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
- return QuickMethodFrameInfo(NterpGetFrameSize(*frame), core_spills, fp_spills);
+ return QuickMethodFrameInfo(NterpGetFrameSize(*frame, kRuntimeISA), core_spills, fp_spills);
}
uintptr_t NterpGetRegistersArray(ArtMethod** frame) {
@@ -206,13 +219,20 @@
}
bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa) {
- return !method->IsNative() &&
- method->IsInvokable() &&
- !method->MustCountLocks() &&
+ uint32_t access_flags = method->GetAccessFlags();
+ if (ArtMethod::IsNative(access_flags) ||
+ !ArtMethod::IsInvokable(access_flags) ||
+ ArtMethod::MustCountLocks(access_flags) ||
// Proxy methods do not go through the JIT like other methods, so we don't
// run them with nterp.
- !method->IsProxyMethod() &&
- NterpGetFrameSize(method, isa) <= interpreter::kNterpMaxFrame;
+ method->IsProxyMethod()) {
+ return false;
+ }
+ // There is no need to add the alignment padding size for comparison with aligned limit.
+ size_t frame_size_without_padding = NterpGetFrameSizeWithoutPadding(method, isa);
+ DCHECK_EQ(NterpGetFrameSize(method, isa), RoundUp(frame_size_without_padding, kStackAlignment));
+ static_assert(IsAligned<kStackAlignment>(interpreter::kNterpMaxFrame));
+ return frame_size_without_padding <= interpreter::kNterpMaxFrame;
}
} // namespace art
diff --git a/runtime/nterp_helpers.h b/runtime/nterp_helpers.h
index 0f9e758..236059b 100644
--- a/runtime/nterp_helpers.h
+++ b/runtime/nterp_helpers.h
@@ -24,12 +24,6 @@
class ArtMethod;
/**
- * The frame size nterp will use for the given method.
- */
-size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa = kRuntimeISA)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
-/**
* Returns the QuickMethodFrameInfo of the given frame corresponding to the
* given method.
*/
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 64ed14a..2c7a73f 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -29,9 +29,6 @@
using android::base::StringPrintf;
-constexpr const char OatHeader::kTrueValue[];
-constexpr const char OatHeader::kFalseValue[];
-
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
if (variable_data != nullptr) {
@@ -470,4 +467,27 @@
key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_);
}
+const uint8_t* OatHeader::GetOatAddress(StubType type) const {
+ DCHECK_LE(type, StubType::kLast);
+ switch (type) {
+ // TODO: We could maybe clean this up if we stored them in an array in the oat header.
+ case StubType::kQuickGenericJNITrampoline:
+ return static_cast<const uint8_t*>(GetQuickGenericJniTrampoline());
+ case StubType::kJNIDlsymLookupTrampoline:
+ return static_cast<const uint8_t*>(GetJniDlsymLookupTrampoline());
+ case StubType::kJNIDlsymLookupCriticalTrampoline:
+ return static_cast<const uint8_t*>(GetJniDlsymLookupCriticalTrampoline());
+ case StubType::kQuickIMTConflictTrampoline:
+ return static_cast<const uint8_t*>(GetQuickImtConflictTrampoline());
+ case StubType::kQuickResolutionTrampoline:
+ return static_cast<const uint8_t*>(GetQuickResolutionTrampoline());
+ case StubType::kQuickToInterpreterBridge:
+ return static_cast<const uint8_t*>(GetQuickToInterpreterBridge());
+ case StubType::kNterpTrampoline:
+ return static_cast<const uint8_t*>(GetNterpTrampoline());
+ default:
+ UNREACHABLE();
+ }
+}
+
} // namespace art
diff --git a/runtime/oat.h b/runtime/oat.h
index 462d41c..e062baa 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -29,11 +29,23 @@
enum class InstructionSet;
class InstructionSetFeatures;
+enum class StubType {
+ kJNIDlsymLookupTrampoline,
+ kJNIDlsymLookupCriticalTrampoline,
+ kQuickGenericJNITrampoline,
+ kQuickIMTConflictTrampoline,
+ kQuickResolutionTrampoline,
+ kQuickToInterpreterBridge,
+ kNterpTrampoline,
+ kLast = kNterpTrampoline,
+};
+std::ostream& operator<<(std::ostream& stream, StubType stub_type);
+
class PACKED(4) OatHeader {
public:
static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
- // Last oat version changed reason: Revert^4 "bss support for inlining BCP into non-BCP".
- static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '2', '5', '\0' } };
+ // Last oat version changed reason: ARM64: Enable implicit suspend checks; compiled code check.
+ static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '3', '0', '\0' } };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
@@ -111,6 +123,8 @@
bool IsConcurrentCopying() const;
bool RequiresImage() const;
+ const uint8_t* GetOatAddress(StubType type) const;
+
private:
bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index cb5f94b..ec8f067 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -50,6 +50,7 @@
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
+#include "class_loader_context.h"
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
@@ -445,7 +446,7 @@
UNLIKELY(oat_file->Size() - index_bss_mapping_offset <
IndexBssMapping::ComputeSize(index_bss_mapping->size())))) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with unaligned or "
- " truncated %s bss mapping, offset %u of %zu, length %zu",
+ "truncated %s bss mapping, offset %u of %zu, length %zu",
oat_file->GetLocation().c_str(),
dex_file_index,
dex_file_location.c_str(),
@@ -465,15 +466,25 @@
const VdexFile* vdex_file,
const uint8_t** type_lookup_table_data,
std::string* error_msg) {
- if (type_lookup_table_start == nullptr ||
- reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0] == 0) {
+ if (type_lookup_table_start == nullptr) {
*type_lookup_table_data = nullptr;
return true;
}
- *type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
- size_t expected_table_size = TypeLookupTable::RawDataLength(header.class_defs_size_);
+ if (UNLIKELY(!vdex_file->Contains(type_lookup_table_start, sizeof(uint32_t)))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid type lookup table start %p of size %zu "
+ "not in [%p, %p]",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_start,
+ sizeof(uint32_t),
+ vdex_file->Begin(),
+ vdex_file->End());
+ return false;
+ }
+
size_t found_size = reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0];
+ size_t expected_table_size = TypeLookupTable::RawDataLength(header.class_defs_size_);
if (UNLIKELY(found_size != expected_table_size)) {
*error_msg =
StringPrintf("In vdex file '%s' unexpected type lookup table size: found %zu, expected %zu",
@@ -482,20 +493,20 @@
expected_table_size);
return false;
}
- if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data))) {
+
+ if (found_size == 0) {
+ *type_lookup_table_data = nullptr;
+ return true;
+ }
+
+ *type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data, found_size))) {
*error_msg =
- StringPrintf("In vdex file '%s' found invalid type lookup table pointer %p not in [%p, %p]",
+ StringPrintf("In vdex file '%s' found invalid type lookup table data %p of size %zu "
+ "not in [%p, %p]",
vdex_file->GetName().c_str(),
type_lookup_table_data,
- vdex_file->Begin(),
- vdex_file->End());
- return false;
- }
- if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data + expected_table_size - 1))) {
- *error_msg =
- StringPrintf("In vdex file '%s' found overflowing type lookup table %p not in [%p, %p]",
- vdex_file->GetName().c_str(),
- type_lookup_table_data + expected_table_size,
+ found_size,
vdex_file->Begin(),
vdex_file->End());
return false;
@@ -514,6 +525,16 @@
uint32_t i = 0;
const uint8_t* type_lookup_table_start = nullptr;
for (const DexFile* dex_file : dex_files) {
+ // Defensively verify external dex file checksum. `OatFileAssistant`
+ // expects this check to happen during oat file setup when the oat file
+ // does not contain dex code.
+ if (dex_file->GetLocationChecksum() != vdex_->GetLocationChecksum(i)) {
+ *error_msg = StringPrintf("Dex checksum does not match for %s, dex has %d, vdex has %d",
+ dex_file->GetLocation().c_str(),
+ dex_file->GetLocationChecksum(),
+ vdex_->GetLocationChecksum(i));
+ return false;
+ }
std::string dex_location = dex_file->GetLocation();
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location.c_str());
@@ -828,7 +849,9 @@
external_dex_files_.push_back(std::move(dex_file));
}
}
- // Defensively verify external dex file checksum.
+ // Defensively verify external dex file checksum. `OatFileAssistant`
+ // expects this check to happen during oat file setup when the oat file
+ // does not contain dex code.
if (dex_file_checksum != external_dex_files_[i]->GetLocationChecksum()) {
*error_msg = StringPrintf("In oat file '%s', dex file checksum 0x%08x does not match"
" checksum 0x%08x of external dex file '%s'",
@@ -1691,11 +1714,12 @@
static OatFileBackedByVdex* Open(const std::vector<const DexFile*>& dex_files,
std::unique_ptr<VdexFile>&& vdex_file,
- const std::string& location) {
+ const std::string& location,
+ ClassLoaderContext* context) {
std::unique_ptr<OatFileBackedByVdex> oat_file(new OatFileBackedByVdex(location));
// SetVdex will take ownership of the VdexFile.
oat_file->SetVdex(vdex_file.release());
- oat_file->SetupHeader(dex_files.size());
+ oat_file->SetupHeader(dex_files.size(), context);
// Initialize OatDexFiles.
std::string error_msg;
if (!oat_file->Setup(dex_files, &error_msg)) {
@@ -1708,6 +1732,7 @@
static OatFileBackedByVdex* Open(int zip_fd,
std::unique_ptr<VdexFile>&& unique_vdex_file,
const std::string& dex_location,
+ ClassLoaderContext* context,
std::string* error_msg) {
VdexFile* vdex_file = unique_vdex_file.get();
std::unique_ptr<OatFileBackedByVdex> oat_file(new OatFileBackedByVdex(vdex_file->GetName()));
@@ -1719,21 +1744,25 @@
for (const uint8_t* dex_file_start = vdex_file->GetNextDexFileData(nullptr, i);
dex_file_start != nullptr;
dex_file_start = vdex_file->GetNextDexFileData(dex_file_start, ++i)) {
- const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_start);
- if (UNLIKELY(!vdex_file->Contains(dex_file_start))) {
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start, sizeof(DexFile::Header)))) {
*error_msg =
- StringPrintf("In vdex file '%s' found invalid dex file pointer %p not in [%p, %p]",
+ StringPrintf("In vdex file '%s' found invalid dex header %p of size %zu "
+ "not in [%p, %p]",
dex_location.c_str(),
dex_file_start,
+ sizeof(DexFile::Header),
vdex_file->Begin(),
vdex_file->End());
return nullptr;
}
- if (UNLIKELY(!vdex_file->Contains(dex_file_start + header->file_size_ - 1))) {
+ const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_start);
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start, header->file_size_))) {
*error_msg =
- StringPrintf("In vdex file '%s' found overflowing dex file %p not in [%p, %p]",
+ StringPrintf("In vdex file '%s' found invalid dex file pointer %p of size %d "
+ "not in [%p, %p]",
dex_location.c_str(),
- dex_file_start + header->file_size_,
+ dex_file_start,
+ header->file_size_,
vdex_file->Begin(),
vdex_file->End());
return nullptr;
@@ -1772,7 +1801,7 @@
oat_file->oat_dex_files_.Put(canonical_key, oat_dex_file);
}
}
- oat_file->SetupHeader(oat_file->oat_dex_files_storage_.size());
+ oat_file->SetupHeader(oat_file->oat_dex_files_storage_.size(), context);
} else {
// No need for any verification when loading dex files as we already have
// a vdex file.
@@ -1796,7 +1825,7 @@
if (!loaded) {
return nullptr;
}
- oat_file->SetupHeader(oat_file->external_dex_files_.size());
+ oat_file->SetupHeader(oat_file->external_dex_files_.size(), context);
if (!oat_file->Setup(MakeNonOwningPointerVector(oat_file->external_dex_files_), error_msg)) {
return nullptr;
}
@@ -1805,7 +1834,7 @@
return oat_file.release();
}
- void SetupHeader(size_t number_of_dex_files) {
+ void SetupHeader(size_t number_of_dex_files, ClassLoaderContext* context) {
DCHECK(!IsExecutable());
// Create a fake OatHeader with a key store to help debugging.
@@ -1816,6 +1845,10 @@
store.Put(OatHeader::kCompilationReasonKey, "vdex");
store.Put(OatHeader::kConcurrentCopying,
gUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ if (context != nullptr) {
+ store.Put(OatHeader::kClassPathKey, context->EncodeContextForOatFile(""));
+ }
+
oat_header_.reset(OatHeader::Create(kRuntimeISA,
isa_features.get(),
number_of_dex_files,
@@ -1970,17 +2003,19 @@
OatFile* OatFile::OpenFromVdex(const std::vector<const DexFile*>& dex_files,
std::unique_ptr<VdexFile>&& vdex_file,
- const std::string& location) {
+ const std::string& location,
+ ClassLoaderContext* context) {
CheckLocation(location);
- return OatFileBackedByVdex::Open(dex_files, std::move(vdex_file), location);
+ return OatFileBackedByVdex::Open(dex_files, std::move(vdex_file), location, context);
}
OatFile* OatFile::OpenFromVdex(int zip_fd,
std::unique_ptr<VdexFile>&& vdex_file,
const std::string& location,
+ ClassLoaderContext* context,
std::string* error_msg) {
CheckLocation(location);
- return OatFileBackedByVdex::Open(zip_fd, std::move(vdex_file), location, error_msg);
+ return OatFileBackedByVdex::Open(zip_fd, std::move(vdex_file), location, context, error_msg);
}
OatFile::OatFile(const std::string& location, bool is_executable)
@@ -2241,7 +2276,7 @@
return OatFile::OatClass(oat_file_,
ClassStatus::kNotReady,
/* type= */ OatClassType::kNoneCompiled,
- /* bitmap_size= */ 0u,
+ /* num_methods= */ 0u,
/* bitmap_pointer= */ nullptr,
/* methods_pointer= */ nullptr);
}
@@ -2437,7 +2472,8 @@
}
std::string OatFile::GetClassLoaderContext() const {
- return GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey);
+ const char* value = GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey);
+ return (value == nullptr) ? "" : value;
}
const char* OatFile::GetCompilationReason() const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index c1b1acb..d52723a 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -39,6 +39,7 @@
class BitVector;
class DexFile;
+class ClassLoaderContext;
class ElfFile;
class DexLayoutSections;
template <class MirrorType> class GcRoot;
@@ -162,13 +163,15 @@
// the vdex does not have a dex section and accepts a vector of DexFiles separately.
static OatFile* OpenFromVdex(const std::vector<const DexFile*>& dex_files,
std::unique_ptr<VdexFile>&& vdex_file,
- const std::string& location);
+ const std::string& location,
+ ClassLoaderContext* context);
// Initialize OatFile instance from an already loaded VdexFile. The dex files
// will be opened through `zip_fd` or `dex_location` if `zip_fd` is -1.
static OatFile* OpenFromVdex(int zip_fd,
std::unique_ptr<VdexFile>&& vdex_file,
const std::string& location,
+ ClassLoaderContext* context,
std::string* error_msg);
// Return whether the `OatFile` uses a vdex-only file.
@@ -270,7 +273,7 @@
return OatClass(/* oat_file= */ nullptr,
ClassStatus::kErrorUnresolved,
OatClassType::kNoneCompiled,
- /* bitmap_size= */ 0,
+ /* num_methods= */ 0,
/* bitmap_pointer= */ nullptr,
/* methods_pointer= */ nullptr);
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index c225893..0312bad 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,15 +16,19 @@
#include "oat_file_assistant.h"
-#include <sstream>
-
#include <sys/stat.h>
-#include "zlib.h"
+
+#include <memory>
+#include <optional>
+#include <sstream>
+#include <vector>
#include "android-base/file.h"
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-
+#include "arch/instruction_set.h"
+#include "base/array_ref.h"
#include "base/compiler_filter.h"
#include "base/file_utils.h"
#include "base/logging.h" // For VLOG.
@@ -44,13 +48,16 @@
#include "gc/space/image_space.h"
#include "image.h"
#include "oat.h"
+#include "oat_file_assistant_context.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "vdex_file.h"
+#include "zlib.h"
namespace art {
-using android::base::StringPrintf;
+using ::android::base::ConsumePrefix;
+using ::android::base::StringPrintf;
static constexpr const char* kAnonymousDexPrefix = "Anonymous-DexFile@";
static constexpr const char* kVdexExtension = ".vdex";
@@ -82,22 +89,24 @@
const InstructionSet isa,
ClassLoaderContext* context,
bool load_executable,
- bool only_load_trusted_executable)
+ bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context)
: OatFileAssistant(dex_location,
isa,
context,
load_executable,
only_load_trusted_executable,
- /*vdex_fd=*/ -1,
- /*oat_fd=*/ -1,
- /*zip_fd=*/ -1) {}
-
+ ofa_context,
+ /*vdex_fd=*/-1,
+ /*oat_fd=*/-1,
+ /*zip_fd=*/-1) {}
OatFileAssistant::OatFileAssistant(const char* dex_location,
const InstructionSet isa,
ClassLoaderContext* context,
bool load_executable,
bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
int vdex_fd,
int oat_fd,
int zip_fd)
@@ -105,12 +114,12 @@
isa_(isa),
load_executable_(load_executable),
only_load_trusted_executable_(only_load_trusted_executable),
- odex_(this, /*is_oat_location=*/ false),
- oat_(this, /*is_oat_location=*/ true),
- vdex_for_odex_(this, /*is_oat_location=*/ false),
- vdex_for_oat_(this, /*is_oat_location=*/ true),
- dm_for_odex_(this, /*is_oat_location=*/ false),
- dm_for_oat_(this, /*is_oat_location=*/ true),
+ odex_(this, /*is_oat_location=*/false),
+ oat_(this, /*is_oat_location=*/true),
+ vdex_for_odex_(this, /*is_oat_location=*/false),
+ vdex_for_oat_(this, /*is_oat_location=*/true),
+ dm_for_odex_(this, /*is_oat_location=*/false),
+ dm_for_oat_(this, /*is_oat_location=*/true),
zip_fd_(zip_fd) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
CHECK_IMPLIES(load_executable, context != nullptr) << "Loading executable without a context";
@@ -127,12 +136,33 @@
dex_location_.assign(dex_location);
+ Runtime* runtime = Runtime::Current();
+
+ if (load_executable_ && runtime == nullptr) {
+ LOG(WARNING) << "OatFileAssistant: Load executable specified, "
+ << "but no active runtime is found. Will not attempt to load executable.";
+ load_executable_ = false;
+ }
+
if (load_executable_ && isa != kRuntimeISA) {
LOG(WARNING) << "OatFileAssistant: Load executable specified, "
<< "but isa is not kRuntimeISA. Will not attempt to load executable.";
load_executable_ = false;
}
+ if (ofa_context == nullptr) {
+ CHECK(runtime != nullptr) << "runtime_options is not provided, and no active runtime is found.";
+ ofa_context_ = std::make_unique<OatFileAssistantContext>(runtime);
+ } else {
+ ofa_context_ = ofa_context;
+ }
+
+ if (runtime == nullptr) {
+ // We need `MemMap` for mapping files. We don't have to initialize it when there is a runtime
+ // because the runtime initializes it.
+ MemMap::Init();
+ }
+
// Get the odex filename.
std::string error_msg;
std::string odex_file_name;
@@ -159,7 +189,11 @@
if (!UseFdToReadFiles()) {
// Get the oat filename.
std::string oat_file_name;
- if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
+ if (DexLocationToOatFilename(dex_location_,
+ isa_,
+ GetRuntimeOptions().deny_art_apex_data_files,
+ &oat_file_name,
+ &error_msg)) {
oat_.Reset(oat_file_name, /*use_fd=*/ false);
std::string vdex_file_name = GetVdexFilename(oat_file_name);
vdex_for_oat_.Reset(vdex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
@@ -190,6 +224,51 @@
}
}
+std::unique_ptr<OatFileAssistant> OatFileAssistant::Create(
+ const std::string& filename,
+ const std::string& isa_str,
+ const std::optional<std::string>& context_str,
+ bool load_executable,
+ bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
+ /*out*/ std::unique_ptr<ClassLoaderContext>* context,
+ /*out*/ std::string* error_msg) {
+ InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
+ if (isa == InstructionSet::kNone) {
+ *error_msg = StringPrintf("Instruction set '%s' is invalid", isa_str.c_str());
+ return nullptr;
+ }
+
+ std::unique_ptr<ClassLoaderContext> tmp_context = nullptr;
+ if (context_str.has_value()) {
+ tmp_context = ClassLoaderContext::Create(context_str->c_str());
+ if (tmp_context == nullptr) {
+ *error_msg = StringPrintf("Class loader context '%s' is invalid", context_str->c_str());
+ return nullptr;
+ }
+
+ if (!tmp_context->OpenDexFiles(android::base::Dirname(filename.c_str()),
+ /*context_fds=*/{},
+ /*only_read_checksums=*/true)) {
+ *error_msg =
+ StringPrintf("Failed to load class loader context files for '%s' with context '%s'",
+ filename.c_str(),
+ context_str->c_str());
+ return nullptr;
+ }
+ }
+
+ auto assistant = std::make_unique<OatFileAssistant>(filename.c_str(),
+ isa,
+ tmp_context.get(),
+ load_executable,
+ only_load_trusted_executable,
+ ofa_context);
+
+ *context = std::move(tmp_context);
+ return assistant;
+}
+
bool OatFileAssistant::UseFdToReadFiles() {
return zip_fd_ >= 0;
}
@@ -199,11 +278,9 @@
// specified by the user. This is okay, because the boot class path should
// be the same for all ISAs.
// TODO: Can we verify the boot class path is the same for all ISAs?
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- const auto& boot_class_path = class_linker->GetBootClassPath();
- for (size_t i = 0; i < boot_class_path.size(); i++) {
- if (boot_class_path[i]->GetLocation() == dex_location_) {
+ for (const std::string& boot_class_path_location :
+ GetRuntimeOptions().boot_class_path_locations) {
+ if (boot_class_path_location == dex_location_) {
VLOG(oat) << "Dex location " << dex_location_ << " is in boot class path";
return true;
}
@@ -211,19 +288,62 @@
return false;
}
-int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
+OatFileAssistant::DexOptTrigger OatFileAssistant::GetDexOptTrigger(
+ CompilerFilter::Filter target_compiler_filter, bool profile_changed, bool downgrade) {
+ if (downgrade) {
+ // The caller's intention is to downgrade the compiler filter. We should only re-compile if the
+ // target compiler filter is worse than the current one.
+ return DexOptTrigger{.targetFilterIsWorse = true};
+ }
+
+ // This is the usual case. The caller's intention is to see if a better oat file can be generated.
+ DexOptTrigger dexopt_trigger{
+ .targetFilterIsBetter = true, .primaryBootImageBecomesUsable = true, .needExtraction = true};
+ if (profile_changed && CompilerFilter::DependsOnProfile(target_compiler_filter)) {
+ // Since the profile has been changed, we should re-compile even if the compilation does not
+ // make the compiler filter better.
+ dexopt_trigger.targetFilterIsSame = true;
+ }
+ return dexopt_trigger;
+}
+
+int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed,
bool downgrade) {
OatFileInfo& info = GetBestInfo();
- DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target,
- profile_changed,
- downgrade);
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(
+ target_compiler_filter, GetDexOptTrigger(target_compiler_filter, profile_changed, downgrade));
+ if (dexopt_needed != kNoDexOptNeeded && (&info == &dm_for_oat_ || &info == &dm_for_odex_)) {
+ // The usable vdex file is in the DM file. This information cannot be encoded in the integer.
+ // Return kDex2OatFromScratch so that neither the vdex in the "oat" location nor the vdex in the
+ // "odex" location will be picked by installd.
+ return kDex2OatFromScratch;
+ }
if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
return dexopt_needed;
}
return -dexopt_needed;
}
+bool OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+ DexOptTrigger dexopt_trigger,
+ /*out*/ DexOptStatus* dexopt_status) {
+ OatFileInfo& info = GetBestInfo();
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target_compiler_filter, dexopt_trigger);
+ if (info.IsUseable()) {
+ if (&info == &dm_for_oat_ || &info == &dm_for_odex_) {
+ dexopt_status->location_ = kLocationDm;
+ } else if (info.IsOatLocation()) {
+ dexopt_status->location_ = kLocationOat;
+ } else {
+ dexopt_status->location_ = kLocationOdex;
+ }
+ } else {
+ dexopt_status->location_ = kLocationNoneOrError;
+ }
+ return dexopt_needed != kNoDexOptNeeded;
+}
+
bool OatFileAssistant::IsUpToDate() {
return GetBestInfo().Status() == kOatUpToDate;
}
@@ -328,13 +448,13 @@
return true;
}
-bool OatFileAssistant::HasDexFiles() {
+std::optional<bool> OatFileAssistant::HasDexFiles(std::string* error_msg) {
ScopedTrace trace("HasDexFiles");
- // Ensure GetRequiredDexChecksums has been run so that
- // has_original_dex_files_ is initialized. We don't care about the result of
- // GetRequiredDexChecksums.
- GetRequiredDexChecksums();
- return has_original_dex_files_;
+ const std::vector<std::uint32_t>* checksums = GetRequiredDexChecksums(error_msg);
+ if (checksums == nullptr) {
+ return std::nullopt;
+ }
+ return !checksums->empty();
}
OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
@@ -345,43 +465,18 @@
return oat_.Status();
}
-bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) {
- ScopedTrace trace("DexChecksumUpToDate(vdex)");
- const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
- if (required_dex_checksums == nullptr) {
- LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
+ if (!file.ContainsDexCode()) {
+ // We've already checked during oat file creation that the dex files loaded
+ // from external files have the same checksums as the ones in the vdex file.
return true;
}
-
- uint32_t number_of_dex_files = file.GetNumberOfDexFiles();
- if (required_dex_checksums->size() != number_of_dex_files) {
- *error_msg = StringPrintf("expected %zu dex files but found %u",
- required_dex_checksums->size(),
- number_of_dex_files);
+ ScopedTrace trace("DexChecksumUpToDate");
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(error_msg);
+ if (required_dex_checksums == nullptr) {
return false;
}
-
- for (uint32_t i = 0; i < number_of_dex_files; i++) {
- uint32_t expected_checksum = (*required_dex_checksums)[i];
- uint32_t actual_checksum = file.GetLocationChecksum(i);
- if (expected_checksum != actual_checksum) {
- std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str());
- *error_msg = StringPrintf("Dex checksum does not match for dex: %s."
- "Expected: %u, actual: %u",
- dex.c_str(),
- expected_checksum,
- actual_checksum);
- return false;
- }
- }
-
- return true;
-}
-
-bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
- ScopedTrace trace("DexChecksumUpToDate(oat)");
- const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
- if (required_dex_checksums == nullptr) {
+ if (required_dex_checksums->empty()) {
LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
return true;
}
@@ -425,8 +520,7 @@
// Verify the dex checksum.
std::string error_msg;
- VdexFile* vdex = file.GetVdexFile();
- if (!DexChecksumUpToDate(*vdex, &error_msg)) {
+ if (!DexChecksumUpToDate(file, &error_msg)) {
LOG(ERROR) << error_msg;
return kOatDexOutOfDate;
}
@@ -441,7 +535,8 @@
VLOG(oat) << "Oat image checksum does not match image checksum.";
return kOatBootImageOutOfDate;
}
- if (!gc::space::ImageSpace::ValidateApexVersions(file, &error_msg)) {
+ if (!gc::space::ImageSpace::ValidateApexVersions(
+ file, GetOatFileAssistantContext()->GetApexVersions(), &error_msg)) {
VLOG(oat) << error_msg;
return kOatBootImageOutOfDate;
}
@@ -449,14 +544,11 @@
VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
}
- // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
- DCHECK(required_dex_checksums_attempted_);
+ // The constraint is only enforced if the zip has uncompressed dex code.
if (only_load_trusted_executable_ &&
- !LocationIsTrusted(file.GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) &&
- file.ContainsDexCode() &&
- zip_file_only_contains_uncompressed_dex_) {
- LOG(ERROR) << "Not loading "
- << dex_location_
+ !LocationIsTrusted(file.GetLocation(), !GetRuntimeOptions().deny_art_apex_data_files) &&
+ file.ContainsDexCode() && ZipFileOnlyContainsUncompressedDex()) {
+ LOG(ERROR) << "Not loading " << dex_location_
<< ": oat file has dex code, but APK has uncompressed dex code";
return kOatDexOutOfDate;
}
@@ -472,6 +564,11 @@
InstructionSet isa,
/* out */ std::string* dex_location,
/* out */ std::string* vdex_filename) {
+ // Normally, OatFileAssistant should not assume that there is an active runtime. However, we
+ // reference the runtime here. This is okay because we are in a static function that is unrelated
+ // to other parts of OatFileAssistant.
+ DCHECK(Runtime::Current() != nullptr);
+
uint32_t checksum = adler32(0L, Z_NULL, 0);
for (const DexFile::Header* header : headers) {
checksum = adler32_combine(checksum,
@@ -569,13 +666,23 @@
InstructionSet isa,
std::string* oat_filename,
std::string* error_msg) {
+ DCHECK(Runtime::Current() != nullptr);
+ return DexLocationToOatFilename(
+ location, isa, Runtime::Current()->DenyArtApexDataFiles(), oat_filename, error_msg);
+}
+
+bool OatFileAssistant::DexLocationToOatFilename(const std::string& location,
+ InstructionSet isa,
+ bool deny_art_apex_data_files,
+ std::string* oat_filename,
+ std::string* error_msg) {
CHECK(oat_filename != nullptr);
CHECK(error_msg != nullptr);
// Check if `location` could have an oat file in the ART APEX data directory. If so, and the
// file exists, use it.
const std::string apex_data_file = GetApexDataOdexFilename(location, isa);
- if (!apex_data_file.empty() && !Runtime::Current()->DenyArtApexDataFiles()) {
+ if (!apex_data_file.empty() && !deny_art_apex_data_files) {
if (OS::FileExists(apex_data_file.c_str(), /*check_file_type=*/true)) {
*oat_filename = apex_data_file;
return true;
@@ -612,30 +719,133 @@
return GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), oat_filename, error_msg);
}
-const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
+const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums(std::string* error_msg) {
if (!required_dex_checksums_attempted_) {
required_dex_checksums_attempted_ = true;
- required_dex_checksums_found_ = false;
- cached_required_dex_checksums_.clear();
- std::string error_msg;
+ std::vector<uint32_t> checksums;
const ArtDexFileLoader dex_file_loader;
std::vector<std::string> dex_locations_ignored;
if (dex_file_loader.GetMultiDexChecksums(dex_location_.c_str(),
- &cached_required_dex_checksums_,
+ &checksums,
&dex_locations_ignored,
- &error_msg,
+ &cached_required_dex_checksums_error_,
zip_fd_,
&zip_file_only_contains_uncompressed_dex_)) {
- required_dex_checksums_found_ = true;
- has_original_dex_files_ = true;
- } else {
- // The only valid case here is for APKs without dex files.
- required_dex_checksums_found_ = false;
- has_original_dex_files_ = false;
- VLOG(oat) << "Could not get required checksum: " << error_msg;
+ if (checksums.empty()) {
+ // The only valid case here is for APKs without dex files.
+ VLOG(oat) << "No dex file found in " << dex_location_;
+ }
+
+ cached_required_dex_checksums_ = std::move(checksums);
}
}
- return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr;
+
+ if (cached_required_dex_checksums_.has_value()) {
+ return &cached_required_dex_checksums_.value();
+ } else {
+ *error_msg = cached_required_dex_checksums_error_;
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+}
+
+bool OatFileAssistant::ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context,
+ InstructionSet isa,
+ std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ /*out*/ std::string* error_msg) {
+ const std::vector<std::string>& bcp_locations =
+ ofa_context->GetRuntimeOptions().boot_class_path_locations;
+
+ if (oat_checksums.empty() || oat_boot_class_path.empty()) {
+ *error_msg = oat_checksums.empty() ? "Empty checksums" : "Empty boot class path";
+ return false;
+ }
+
+ size_t oat_bcp_size = gc::space::ImageSpace::CheckAndCountBCPComponents(
+ oat_boot_class_path, ArrayRef<const std::string>(bcp_locations), error_msg);
+ if (oat_bcp_size == static_cast<size_t>(-1)) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ DCHECK_LE(oat_bcp_size, bcp_locations.size());
+
+ size_t bcp_index = 0;
+ size_t boot_image_index = 0;
+ bool found_d = false;
+
+ while (bcp_index < oat_bcp_size) {
+ static_assert(gc::space::ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check");
+ static_assert(gc::space::ImageSpace::kDexFileChecksumPrefix == 'd', "Format prefix check");
+ if (StartsWith(oat_checksums, "i") && !found_d) {
+ const std::vector<OatFileAssistantContext::BootImageInfo>& boot_image_info_list =
+ ofa_context->GetBootImageInfoList(isa);
+ if (boot_image_index >= boot_image_info_list.size()) {
+ *error_msg = StringPrintf("Missing boot image for %s, remaining checksums: %s",
+ bcp_locations[bcp_index].c_str(),
+ std::string(oat_checksums).c_str());
+ return false;
+ }
+
+ const OatFileAssistantContext::BootImageInfo& boot_image_info =
+ boot_image_info_list[boot_image_index];
+ if (!ConsumePrefix(&oat_checksums, boot_image_info.checksum)) {
+ *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
+ std::string(oat_checksums).c_str(),
+ boot_image_info.checksum.c_str());
+ return false;
+ }
+
+ bcp_index += boot_image_info.component_count;
+ boot_image_index++;
+ } else if (StartsWith(oat_checksums, "d")) {
+ found_d = true;
+ const std::vector<std::string>* bcp_checksums =
+ ofa_context->GetBcpChecksums(bcp_index, error_msg);
+ if (bcp_checksums == nullptr) {
+ return false;
+ }
+ oat_checksums.remove_prefix(1u);
+ for (const std::string& checksum : *bcp_checksums) {
+ if (!ConsumePrefix(&oat_checksums, checksum)) {
+ *error_msg = StringPrintf(
+ "Dex checksum mismatch for bootclasspath file %s, expected %s to start with %s",
+ bcp_locations[bcp_index].c_str(),
+ std::string(oat_checksums).c_str(),
+ checksum.c_str());
+ return false;
+ }
+ }
+
+ bcp_index++;
+ } else {
+ *error_msg = StringPrintf("Unexpected checksums, expected %s to start with %s",
+ std::string(oat_checksums).c_str(),
+ found_d ? "'d'" : "'i' or 'd'");
+ return false;
+ }
+
+ if (bcp_index < oat_bcp_size) {
+ if (!ConsumePrefix(&oat_checksums, ":")) {
+ if (oat_checksums.empty()) {
+ *error_msg =
+ StringPrintf("Checksum too short, missing %zu components", oat_bcp_size - bcp_index);
+ } else {
+ *error_msg = StringPrintf("Missing ':' separator at start of %s",
+ std::string(oat_checksums).c_str());
+ }
+ return false;
+ }
+ }
+ }
+
+ if (!oat_checksums.empty()) {
+ *error_msg =
+ StringPrintf("Checksum too long, unexpected tail: %s", std::string(oat_checksums).c_str());
+ return false;
+ }
+
+ return true;
}
bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file) {
@@ -647,45 +857,26 @@
if (oat_boot_class_path_checksums == nullptr || oat_boot_class_path == nullptr) {
return false;
}
- std::string_view oat_boot_class_path_checksums_view(oat_boot_class_path_checksums);
- std::string_view oat_boot_class_path_view(oat_boot_class_path);
- if (oat_boot_class_path_view == cached_boot_class_path_ &&
- oat_boot_class_path_checksums_view == cached_boot_class_path_checksums_) {
- return true;
- }
- Runtime* runtime = Runtime::Current();
std::string error_msg;
- bool result = false;
- // Fast path when the runtime boot classpath cheksums and boot classpath
- // locations directly match.
- if (oat_boot_class_path_checksums_view == runtime->GetBootClassPathChecksums() &&
- isa_ == kRuntimeISA &&
- oat_boot_class_path_view == android::base::Join(runtime->GetBootClassPathLocations(), ":")) {
- result = true;
- } else {
- result = gc::space::ImageSpace::VerifyBootClassPathChecksums(
- oat_boot_class_path_checksums_view,
- oat_boot_class_path_view,
- ArrayRef<const std::string>(runtime->GetImageLocations()),
- ArrayRef<const std::string>(runtime->GetBootClassPathLocations()),
- ArrayRef<const std::string>(runtime->GetBootClassPath()),
- ArrayRef<const int>(runtime->GetBootClassPathFds()),
- isa_,
- &error_msg);
- }
+ bool result = ValidateBootClassPathChecksums(GetOatFileAssistantContext(),
+ isa_,
+ oat_boot_class_path_checksums,
+ oat_boot_class_path,
+ &error_msg);
if (!result) {
VLOG(oat) << "Failed to verify checksums of oat file " << oat_file.GetLocation()
<< " error: " << error_msg;
return false;
}
- // This checksum has been validated, so save it.
- cached_boot_class_path_ = oat_boot_class_path_view;
- cached_boot_class_path_checksums_ = oat_boot_class_path_checksums_view;
return true;
}
+bool OatFileAssistant::IsPrimaryBootImageUsable() {
+ return !GetOatFileAssistantContext()->GetBootImageInfoList(isa_).empty();
+}
+
OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
ScopedTrace trace("GetBestInfo");
// TODO(calin): Document the side effects of class loading when
@@ -806,24 +997,34 @@
}
OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
- CompilerFilter::Filter target,
- bool profile_changed,
- bool downgrade) {
-
+ CompilerFilter::Filter target_compiler_filter, const DexOptTrigger dexopt_trigger) {
if (IsUseable()) {
- return CompilerFilterIsOkay(target, profile_changed, downgrade)
- ? kNoDexOptNeeded
- : kDex2OatForFilter;
+ return ShouldRecompileForFilter(target_compiler_filter, dexopt_trigger) ? kDex2OatForFilter :
+ kNoDexOptNeeded;
+ }
+
+ // In this case, the oat file is not usable. If the caller doesn't seek for a better compiler
+ // filter (e.g., the caller wants to downgrade), then we should not recompile.
+ if (!dexopt_trigger.targetFilterIsBetter) {
+ return kNoDexOptNeeded;
}
if (Status() == kOatBootImageOutOfDate) {
return kDex2OatForBootImage;
}
- if (oat_file_assistant_->HasDexFiles()) {
- return kDex2OatFromScratch;
+ std::string error_msg;
+ std::optional<bool> has_dex_files = oat_file_assistant_->HasDexFiles(&error_msg);
+ if (has_dex_files.has_value()) {
+ if (*has_dex_files) {
+ return kDex2OatFromScratch;
+ } else {
+ // No dex file, so there is nothing we need to do.
+ return kNoDexOptNeeded;
+ }
} else {
- // No dex file, there is nothing we need to do.
+ // Unable to open the dex file, so there is nothing we can do.
+ LOG(WARNING) << error_msg;
return kNoDexOptNeeded;
}
}
@@ -838,7 +1039,8 @@
return nullptr;
}
- if (LocationIsOnArtApexData(filename_) && Runtime::Current()->DenyArtApexDataFiles()) {
+ if (LocationIsOnArtApexData(filename_) &&
+ oat_file_assistant_->GetRuntimeOptions().deny_art_apex_data_files) {
LOG(WARNING) << "OatFileAssistant rejected file " << filename_
<< ": ART apexdata is untrusted.";
return nullptr;
@@ -877,6 +1079,7 @@
file_.reset(OatFile::OpenFromVdex(zip_fd_,
std::move(vdex),
oat_file_assistant_->dex_location_,
+ oat_file_assistant_->context_,
&error_msg));
}
} else if (android::base::EndsWith(filename_, kDmExtension)) {
@@ -889,6 +1092,7 @@
file_.reset(OatFile::OpenFromVdex(zip_fd_,
std::move(vdex),
oat_file_assistant_->dex_location_,
+ oat_file_assistant_->context_,
&error_msg));
}
}
@@ -932,25 +1136,24 @@
return file_.get();
}
-bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay(
- CompilerFilter::Filter target, bool profile_changed, bool downgrade) {
+bool OatFileAssistant::OatFileInfo::ShouldRecompileForFilter(CompilerFilter::Filter target,
+ const DexOptTrigger dexopt_trigger) {
const OatFile* file = GetFile();
- if (file == nullptr) {
- return false;
- }
+ DCHECK(file != nullptr);
CompilerFilter::Filter current = file->GetCompilerFilter();
- if (profile_changed && CompilerFilter::DependsOnProfile(current)) {
- VLOG(oat) << "Compiler filter not okay because Profile changed";
- return false;
+ if (dexopt_trigger.targetFilterIsBetter && CompilerFilter::IsBetter(target, current)) {
+ return true;
+ }
+ if (dexopt_trigger.targetFilterIsSame && current == target) {
+ return true;
+ }
+ if (dexopt_trigger.targetFilterIsWorse && CompilerFilter::IsBetter(current, target)) {
+ return true;
}
- if (downgrade) {
- return !CompilerFilter::IsBetter(current, target);
- }
-
- if (CompilerFilter::DependsOnImageChecksum(current) &&
- CompilerFilter::IsAsGoodAs(current, target)) {
+ if (dexopt_trigger.primaryBootImageBecomesUsable &&
+ CompilerFilter::DependsOnImageChecksum(current)) {
// If the oat file has been compiled without an image, and the runtime is
// now running with an image loaded from disk, return that we need to
// re-compile. The recompilation will generate a better oat file, and with an app
@@ -959,16 +1162,26 @@
file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
if (oat_boot_class_path_checksums != nullptr &&
!StartsWith(oat_boot_class_path_checksums, "i") &&
- !Runtime::Current()->HasImageWithProfile()) {
+ oat_file_assistant_->IsPrimaryBootImageUsable()) {
DCHECK(!file->GetOatHeader().RequiresImage());
- return false;
+ return true;
}
}
- return CompilerFilter::IsAsGoodAs(current, target);
+ if (dexopt_trigger.needExtraction && !file->ContainsDexCode() &&
+ !oat_file_assistant_->ZipFileOnlyContainsUncompressedDex()) {
+ return true;
+ }
+
+ return false;
}
bool OatFileAssistant::ClassLoaderContextIsOkay(const OatFile& oat_file) const {
+ if (context_ == nullptr) {
+ // The caller requests to skip the check.
+ return true;
+ }
+
if (oat_file.IsBackedByVdexOnly()) {
// Only a vdex file, we don't depend on the class loader context.
return true;
@@ -980,12 +1193,6 @@
return true;
}
- if (context_ == nullptr) {
- // When no class loader context is provided (which happens for deprecated
- // DexFile APIs), just assume it is OK.
- return true;
- }
-
ClassLoaderContext::VerificationResult matches = context_->VerifyClassLoaderContextMatch(
oat_file.GetClassLoaderContext(),
/*verify_names=*/ true,
@@ -1042,17 +1249,19 @@
// TODO(calin): we could provide a more refined status here
// (e.g. run from uncompressed apk, run with vdex but not oat etc). It will allow us to
// track more experiments but adds extra complexity.
-void OatFileAssistant::GetOptimizationStatus(
- const std::string& filename,
- InstructionSet isa,
- std::string* out_compilation_filter,
- std::string* out_compilation_reason) {
+void OatFileAssistant::GetOptimizationStatus(const std::string& filename,
+ InstructionSet isa,
+ std::string* out_compilation_filter,
+ std::string* out_compilation_reason,
+ OatFileAssistantContext* ofa_context) {
// It may not be possible to load an oat file executable (e.g., selinux restrictions). Load
// non-executable and check the status manually.
OatFileAssistant oat_file_assistant(filename.c_str(),
isa,
- /* context= */ nullptr,
- /*load_executable=*/ false);
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/false,
+ ofa_context);
std::string out_odex_location; // unused
std::string out_odex_status; // unused
oat_file_assistant.GetOptimizationStatus(
@@ -1088,28 +1297,25 @@
OatStatus status = oat_file_info.Status();
const char* reason = oat_file->GetCompilationReason();
*out_compilation_reason = reason == nullptr ? "unknown" : reason;
+
+ // If the oat file is invalid, the vdex file will be picked, so the status is `kOatUpToDate`. If
+ // the vdex file is also invalid, then either `oat_file` is nullptr, or `status` is
+ // `kOatDexOutOfDate`.
+ DCHECK(status == kOatUpToDate || status == kOatDexOutOfDate);
+
switch (status) {
case kOatUpToDate:
*out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter());
*out_odex_status = "up-to-date";
return;
- case kOatCannotOpen: // This should never happen, but be robust.
- *out_compilation_filter = "error";
- *out_compilation_reason = "error";
- // This mostly happens when we cannot open the vdex file,
- // or the file is corrupt.
- *out_odex_status = "io-error-or-corruption";
- return;
-
+ case kOatCannotOpen:
case kOatBootImageOutOfDate:
- *out_compilation_filter = "run-from-apk-fallback";
- *out_odex_status = "boot-image-more-recent";
- return;
-
case kOatContextOutOfDate:
- *out_compilation_filter = "run-from-apk-fallback";
- *out_odex_status = "context-mismatch";
+ // These should never happen, but be robust.
+ *out_compilation_filter = "unexpected";
+ *out_compilation_reason = "unexpected";
+ *out_odex_status = "unexpected";
return;
case kOatDexOutOfDate:
@@ -1121,4 +1327,13 @@
UNREACHABLE();
}
+bool OatFileAssistant::ZipFileOnlyContainsUncompressedDex() {
+ // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
+ std::string error_msg;
+ if (GetRequiredDexChecksums(&error_msg) == nullptr) {
+ LOG(ERROR) << error_msg;
+ }
+ return zip_file_only_contains_uncompressed_dex_;
+}
+
} // namespace art
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index c243cc3..81cd231 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -19,16 +19,19 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <sstream>
#include <string>
+#include <variant>
-#include "base/compiler_filter.h"
#include "arch/instruction_set.h"
+#include "base/compiler_filter.h"
#include "base/os.h"
#include "base/scoped_flock.h"
#include "base/unix_file/fd_file.h"
#include "class_loader_context.h"
#include "oat_file.h"
+#include "oat_file_assistant_context.h"
namespace art {
@@ -89,6 +92,48 @@
kOatUpToDate,
};
+ // A bit field to represent the conditions where dexopt should be performed.
+ struct DexOptTrigger {
+ // Dexopt should be performed if the target compiler filter is better than the current compiler
+ // filter. See `CompilerFilter::IsBetter`.
+ bool targetFilterIsBetter : 1;
+ // Dexopt should be performed if the target compiler filter is the same as the current compiler
+ // filter.
+ bool targetFilterIsSame : 1;
+ // Dexopt should be performed if the target compiler filter is worse than the current compiler
+ // filter. See `CompilerFilter::IsBetter`.
+ bool targetFilterIsWorse : 1;
+ // Dexopt should be performed if the current oat file was compiled without a primary image,
+ // and the runtime is now running with a primary image loaded from disk.
+ bool primaryBootImageBecomesUsable : 1;
+ // Dexopt should be performed if the APK is compressed and the current oat/vdex file doesn't
+ // contain dex code.
+ bool needExtraction : 1;
+ };
+
+ // Represents the location of the current oat file and/or vdex file.
+ enum Location {
+ // Does not exist, or an error occurs.
+ kLocationNoneOrError = 0,
+ // In the global "dalvik-cache" folder.
+ kLocationOat = 1,
+ // In the "oat" folder next to the dex file.
+ kLocationOdex = 2,
+ // In the DM file. This means the only usable file is the vdex file.
+ kLocationDm = 3,
+ };
+
+ // Represents the status of the current oat file and/or vdex file.
+ class DexOptStatus {
+ public:
+ Location GetLocation() { return location_; }
+ bool IsVdexUsable() { return location_ != kLocationNoneOrError; }
+
+ private:
+ Location location_ = kLocationNoneOrError;
+ friend class OatFileAssistant;
+ };
+
// Constructs an OatFileAssistant object to assist the oat file
// corresponding to the given dex location with the target instruction set.
//
@@ -104,17 +149,23 @@
// device. For example, on an arm device, use arm or arm64. An oat file can
// be loaded executable only if the ISA matches the current runtime.
//
+ // context should be the class loader context to check against, or null to skip the check.
+ //
// load_executable should be true if the caller intends to try and load
// executable code for this dex location.
//
// only_load_trusted_executable should be true if the caller intends to have
// only oat files from trusted locations loaded executable. See IsTrustedLocation() for
// details on trusted locations.
+ //
+ // runtime_options should be provided with all the required fields filled if the caller intends to
+ // use OatFileAssistant without a runtime.
OatFileAssistant(const char* dex_location,
const InstructionSet isa,
ClassLoaderContext* context,
bool load_executable,
- bool only_load_trusted_executable = false);
+ bool only_load_trusted_executable = false,
+ OatFileAssistantContext* ofa_context = nullptr);
// Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
// provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
@@ -124,10 +175,25 @@
ClassLoaderContext* context,
bool load_executable,
bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
int vdex_fd,
int oat_fd,
int zip_fd);
+ // A convenient factory function that accepts ISA, class loader context, and compiler filter in
+ // strings. Returns the created instance and ClassLoaderContext on success, or returns nullptr and
+ // outputs an error message if it fails to parse the input strings.
+ // The returned ClassLoaderContext must live at least as long as the OatFileAssistant.
+ static std::unique_ptr<OatFileAssistant> Create(
+ const std::string& filename,
+ const std::string& isa_str,
+ const std::optional<std::string>& context_str,
+ bool load_executable,
+ bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
+ /*out*/ std::unique_ptr<ClassLoaderContext>* context,
+ /*out*/ std::string* error_msg);
+
// Returns true if the dex location refers to an element of the boot class
// path.
bool IsInBootClassPath();
@@ -148,10 +214,18 @@
// Returns a positive status code if the status refers to the oat file in
// the oat location. Returns a negative status code if the status refers to
// the oat file in the odex location.
+ //
+ // Deprecated. Use the other overload.
int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed = false,
bool downgrade = false);
+ // Returns true if dexopt needs to be performed with respect to the given target compilation
+ // filter and dexopt trigger. Also returns the status of the current oat file and/or vdex file.
+ bool GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+ const DexOptTrigger dexopt_trigger,
+ /*out*/ DexOptStatus* dexopt_status);
+
// Returns true if there is up-to-date code for this dex location,
// irrespective of the compiler filter of the up-to-date code.
bool IsUpToDate();
@@ -176,7 +250,7 @@
// - out_compilation_reason: the optimization reason. The reason might
// be "unknown" if the compiler artifacts were not annotated during optimizations.
// - out_odex_status: a human readable refined status of the validity of the odex file.
- // E.g. up-to-date, boot-image-more-recent, apk-more-recent.
+ // Possible values are: "up-to-date", "apk-more-recent", and "io-error-no-oat".
//
// This method will try to mimic the runtime effect of loading the dex file.
// For example, if there is no usable oat file, the compiler filter will be set
@@ -189,7 +263,8 @@
static void GetOptimizationStatus(const std::string& filename,
InstructionSet isa,
std::string* out_compilation_filter,
- std::string* out_compilation_reason);
+ std::string* out_compilation_reason,
+ OatFileAssistantContext* ofa_context = nullptr);
// Open and returns an image space associated with the oat file.
static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
@@ -212,8 +287,8 @@
const std::string& dex_location,
std::vector<std::unique_ptr<const DexFile>>* out_dex_files);
- // Returns whether this is an apk/zip wit a classes.dex entry.
- bool HasDexFiles();
+ // Returns whether this is an apk/zip wit a classes.dex entry, or nullopt if an error occurred.
+ std::optional<bool> HasDexFiles(std::string* error_msg);
// If the dex file has been installed with a compiled oat file alongside
// it, the compiled oat file will have the extension .odex, and is referred
@@ -253,15 +328,28 @@
// Returns false on error, in which case error_msg describes the error and
// oat_filename is not changed.
// Neither oat_filename nor error_msg may be null.
+ //
+ // Calling this function requires an active runtime.
static bool DexLocationToOatFilename(const std::string& location,
InstructionSet isa,
std::string* oat_filename,
std::string* error_msg);
+ // Same as above, but also takes `deny_art_apex_data_files` from input.
+ //
+ // Calling this function does not require an active runtime.
+ static bool DexLocationToOatFilename(const std::string& location,
+ InstructionSet isa,
+ bool deny_art_apex_data_files,
+ std::string* oat_filename,
+ std::string* error_msg);
+
// Computes the dex location and vdex filename. If the data directory of the process
// is known, creates an absolute path in that directory and tries to infer path
// of a corresponding vdex file. Otherwise only creates a basename dex_location
// from the combined checksums. Returns true if all out-arguments have been set.
+ //
+ // Calling this function requires an active runtime.
static bool AnonymousDexVdexLocation(const std::vector<const DexFile::Header*>& dex_headers,
InstructionSet isa,
/* out */ std::string* dex_location,
@@ -273,6 +361,16 @@
bool ClassLoaderContextIsOkay(const OatFile& oat_file) const;
+ // Validates the boot class path checksum of an OatFile.
+ bool ValidateBootClassPathChecksums(const OatFile& oat_file);
+
+ // Validates the given bootclasspath and bootclasspath checksums found in an oat header.
+ static bool ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context,
+ InstructionSet isa,
+ std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ /*out*/ std::string* error_msg);
+
private:
class OatFileInfo {
public:
@@ -298,15 +396,10 @@
// Returns the status of this oat file.
OatStatus Status();
- // Return the DexOptNeeded value for this oat file with respect to the
- // given target_compilation_filter.
- // profile_changed should be true to indicate the profile has recently
- // changed for this dex location.
- // downgrade should be true if the purpose of dexopt is to downgrade the
- // compiler filter.
+ // Return the DexOptNeeded value for this oat file with respect to the given target compilation
+ // filter and dexopt trigger.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
- bool profile_changed,
- bool downgrade);
+ const DexOptTrigger dexopt_trigger);
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
@@ -339,13 +432,10 @@
std::unique_ptr<OatFile> ReleaseFileForUse();
private:
- // Returns true if the compiler filter used to generate the file is at
- // least as good as the given target filter. profile_changed should be
- // true to indicate the profile has recently changed for this dex
- // location.
- // downgrade should be true if the purpose of dexopt is to downgrade the
- // compiler filter.
- bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed, bool downgrade);
+ // Returns true if the oat file is usable but at least one dexopt trigger is matched. This
+ // function should only be called if the oat file is usable.
+ bool ShouldRecompileForFilter(CompilerFilter::Filter target,
+ const DexOptTrigger dexopt_trigger);
// Release the loaded oat file.
// Returns null if the oat file hasn't been loaded.
@@ -387,11 +477,6 @@
// files are being read.
bool UseFdToReadFiles();
- // Returns true if the dex checksums in the given vdex file are up to date
- // with respect to the dex location. If the dex checksums are not up to
- // date, error_msg is updated with a message describing the problem.
- bool DexChecksumUpToDate(const VdexFile& file, std::string* error_msg);
-
// Returns true if the dex checksums in the given oat file are up to date
// with respect to the dex location. If the dex checksums are not up to
// date, error_msg is updated with a message describing the problem.
@@ -402,18 +487,45 @@
OatStatus GivenOatFileStatus(const OatFile& file);
// Gets the dex checksums required for an up-to-date oat file.
- // Returns cached_required_dex_checksums if the required checksums were
- // located. Returns null if the required checksums were not found. The
- // caller shouldn't clean up or free the returned pointer. This sets the
- // has_original_dex_files_ field to true if the checksums were found for the
- // dex_location_ dex file.
- const std::vector<uint32_t>* GetRequiredDexChecksums();
+ // Returns cached_required_dex_checksums if the required checksums were located. Returns an empty
+ // list if `dex_location_` refers to a zip and there is no dex file in it. Returns nullptr if an
+ // error occurred. The caller shouldn't clean up or free the returned pointer.
+ const std::vector<uint32_t>* GetRequiredDexChecksums(std::string* error_msg);
- // Validates the boot class path checksum of an OatFile.
- bool ValidateBootClassPathChecksums(const OatFile& oat_file);
+ // Returns whether there is at least one boot image usable.
+ bool IsPrimaryBootImageUsable();
+
+ // Returns the trigger for the deprecated overload of `GetDexOptNeeded`.
+ //
+ // Deprecated. Do not use in new code.
+ DexOptTrigger GetDexOptTrigger(CompilerFilter::Filter target_compiler_filter,
+ bool profile_changed,
+ bool downgrade);
+
+ // Returns the pointer to the owned or unowned instance of OatFileAssistantContext.
+ OatFileAssistantContext* GetOatFileAssistantContext() {
+ if (std::holds_alternative<OatFileAssistantContext*>(ofa_context_)) {
+ return std::get<OatFileAssistantContext*>(ofa_context_);
+ } else {
+ return std::get<std::unique_ptr<OatFileAssistantContext>>(ofa_context_).get();
+ }
+ }
+
+ // The runtime options taken from the active runtime or the input.
+ //
+ // All member functions should get runtime options from this variable rather than referencing the
+ // active runtime. This is to allow OatFileAssistant to function without an active runtime.
+ const OatFileAssistantContext::RuntimeOptions& GetRuntimeOptions() {
+ return GetOatFileAssistantContext()->GetRuntimeOptions();
+ }
+
+ // Returns whether the zip file only contains uncompressed dex.
+ bool ZipFileOnlyContainsUncompressedDex();
std::string dex_location_;
+ // The class loader context to check against, or null representing that the check should be
+ // skipped.
ClassLoaderContext* context_;
// Whether or not the parent directory of the dex file is writable.
@@ -428,16 +540,16 @@
// Whether only oat files from trusted locations are loaded executable.
const bool only_load_trusted_executable_ = false;
- // Whether the potential zip file only contains uncompressed dex.
- // Will be set during GetRequiredDexChecksums.
+
+ // Cached value of whether the potential zip file only contains uncompressed dex.
+ // This should be accessed only by the ZipFileOnlyContainsUncompressedDex() method.
bool zip_file_only_contains_uncompressed_dex_ = true;
// Cached value of the required dex checksums.
// This should be accessed only by the GetRequiredDexChecksums() method.
- std::vector<uint32_t> cached_required_dex_checksums_;
+ std::optional<std::vector<uint32_t>> cached_required_dex_checksums_;
+ std::string cached_required_dex_checksums_error_;
bool required_dex_checksums_attempted_ = false;
- bool required_dex_checksums_found_;
- bool has_original_dex_files_;
// The AOT-compiled file of an app when the APK of the app is in /data.
OatFileInfo odex_;
@@ -459,8 +571,8 @@
// File descriptor corresponding to apk, dex file, or zip.
int zip_fd_;
- std::string cached_boot_class_path_;
- std::string cached_boot_class_path_checksums_;
+ // Owned or unowned instance of OatFileAssistantContext.
+ std::variant<std::unique_ptr<OatFileAssistantContext>, OatFileAssistantContext*> ofa_context_;
friend class OatFileAssistantTest;
diff --git a/runtime/oat_file_assistant_context.cc b/runtime/oat_file_assistant_context.cc
new file mode 100644
index 0000000..4bd83b7
--- /dev/null
+++ b/runtime/oat_file_assistant_context.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 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 "oat_file_assistant_context.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "arch/instruction_set.h"
+#include "base/array_ref.h"
+#include "base/logging.h"
+#include "base/mem_map.h"
+#include "class_linker.h"
+#include "dex/art_dex_file_loader.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+
+namespace art {
+
+using ::android::base::StringPrintf;
+using ::art::gc::space::ImageSpace;
+
+OatFileAssistantContext::OatFileAssistantContext(
+ std::unique_ptr<OatFileAssistantContext::RuntimeOptions> runtime_options)
+ : runtime_options_(std::move(runtime_options)) {
+ DCHECK_EQ(runtime_options_->boot_class_path.size(),
+ runtime_options_->boot_class_path_locations.size());
+ DCHECK_IMPLIES(
+ runtime_options_->boot_class_path_fds != nullptr,
+ runtime_options_->boot_class_path.size() == runtime_options_->boot_class_path_fds->size());
+ // Opening dex files and boot images require MemMap.
+ MemMap::Init();
+}
+
+OatFileAssistantContext::OatFileAssistantContext(Runtime* runtime)
+ : OatFileAssistantContext(std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+ OatFileAssistantContext::RuntimeOptions{
+ .image_locations = runtime->GetImageLocations(),
+ .boot_class_path = runtime->GetBootClassPath(),
+ .boot_class_path_locations = runtime->GetBootClassPathLocations(),
+ .boot_class_path_fds = !runtime->GetBootClassPathFds().empty() ?
+ &runtime->GetBootClassPathFds() :
+ nullptr,
+ .deny_art_apex_data_files = runtime->DenyArtApexDataFiles(),
+ })) {
+ // Fetch boot image info from the runtime.
+ std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[kRuntimeISA];
+ for (const ImageSpace* image_space : runtime->GetHeap()->GetBootImageSpaces()) {
+ // We only need the checksum of the first component for each boot image. They are in image
+ // spaces that have a non-zero component count.
+ if (image_space->GetComponentCount() > 0) {
+ BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
+ boot_image_info.component_count = image_space->GetComponentCount();
+ ImageSpace::AppendImageChecksum(image_space->GetComponentCount(),
+ image_space->GetImageHeader().GetImageChecksum(),
+ &boot_image_info.checksum);
+ }
+ }
+
+ // Fetch BCP checksums from the runtime.
+ size_t bcp_index = 0;
+ std::vector<std::string>* current_bcp_checksums = nullptr;
+ for (const DexFile* dex_file : runtime->GetClassLinker()->GetBootClassPath()) {
+ if (!DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str())) {
+ DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
+ current_bcp_checksums = &bcp_checksums_by_index_[bcp_index++];
+ }
+ DCHECK_NE(current_bcp_checksums, nullptr);
+ current_bcp_checksums->push_back(StringPrintf("/%08x", dex_file->GetLocationChecksum()));
+ }
+ DCHECK_EQ(bcp_index, runtime_options_->boot_class_path.size());
+
+ // Fetch APEX versions from the runtime.
+ apex_versions_ = runtime->GetApexVersions();
+}
+
+const OatFileAssistantContext::RuntimeOptions& OatFileAssistantContext::GetRuntimeOptions() const {
+ return *runtime_options_;
+}
+
+bool OatFileAssistantContext::FetchAll(std::string* error_msg) {
+ std::vector<InstructionSet> isas = GetSupportedInstructionSets(error_msg);
+ if (isas.empty()) {
+ return false;
+ }
+ for (InstructionSet isa : isas) {
+ GetBootImageInfoList(isa);
+ }
+ for (size_t i = 0; i < runtime_options_->boot_class_path.size(); i++) {
+ if (GetBcpChecksums(i, error_msg) == nullptr) {
+ return false;
+ }
+ }
+ GetApexVersions();
+ return true;
+}
+
+const std::vector<OatFileAssistantContext::BootImageInfo>&
+OatFileAssistantContext::GetBootImageInfoList(InstructionSet isa) {
+ if (auto it = boot_image_info_list_by_isa_.find(isa); it != boot_image_info_list_by_isa_.end()) {
+ return it->second;
+ }
+
+ ImageSpace::BootImageLayout layout(
+ ArrayRef<const std::string>(runtime_options_->image_locations),
+ ArrayRef<const std::string>(runtime_options_->boot_class_path),
+ ArrayRef<const std::string>(runtime_options_->boot_class_path_locations),
+ runtime_options_->boot_class_path_fds != nullptr ?
+ ArrayRef<const int>(*runtime_options_->boot_class_path_fds) :
+ ArrayRef<const int>(),
+ /*boot_class_path_image_fds=*/ArrayRef<const int>(),
+ /*boot_class_path_vdex_fds=*/ArrayRef<const int>(),
+ /*boot_class_path_oat_fds=*/ArrayRef<const int>(),
+ &GetApexVersions());
+
+ std::string error_msg;
+ if (!layout.LoadFromSystem(isa, /*allow_in_memory_compilation=*/false, &error_msg)) {
+ // At this point, `layout` contains nothing.
+ VLOG(oat) << "Some error occurred when loading boot images for oat file validation: "
+ << error_msg;
+ // Create an empty entry so that we don't have to retry when the function is called again.
+ return boot_image_info_list_by_isa_[isa];
+ }
+
+ std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[isa];
+ for (const ImageSpace::BootImageLayout::ImageChunk& chunk : layout.GetChunks()) {
+ BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
+ boot_image_info.component_count = chunk.component_count;
+ ImageSpace::AppendImageChecksum(
+ chunk.component_count, chunk.checksum, &boot_image_info.checksum);
+ }
+ return boot_image_info_list;
+}
+
+const std::vector<std::string>* OatFileAssistantContext::GetBcpChecksums(size_t bcp_index,
+ std::string* error_msg) {
+ DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
+
+ if (auto it = bcp_checksums_by_index_.find(bcp_index); it != bcp_checksums_by_index_.end()) {
+ return &it->second;
+ }
+
+ std::vector<uint32_t> checksums;
+ std::vector<std::string> dex_locations;
+ ArtDexFileLoader dex_file_loader;
+ if (!dex_file_loader.GetMultiDexChecksums(
+ runtime_options_->boot_class_path[bcp_index].c_str(),
+ &checksums,
+ &dex_locations,
+ error_msg,
+ runtime_options_->boot_class_path_fds != nullptr ?
+ (*runtime_options_->boot_class_path_fds)[bcp_index] :
+ -1)) {
+ return nullptr;
+ }
+
+ DCHECK(!checksums.empty());
+ std::vector<std::string>& bcp_checksums = bcp_checksums_by_index_[bcp_index];
+ for (uint32_t checksum : checksums) {
+ bcp_checksums.push_back(StringPrintf("/%08x", checksum));
+ }
+ return &bcp_checksums;
+}
+
+const std::string& OatFileAssistantContext::GetApexVersions() {
+ if (apex_versions_.has_value()) {
+ return apex_versions_.value();
+ }
+
+ apex_versions_ = Runtime::GetApexVersions(
+ ArrayRef<const std::string>(runtime_options_->boot_class_path_locations));
+ return apex_versions_.value();
+}
+
+} // namespace art
diff --git a/runtime/oat_file_assistant_context.h b/runtime/oat_file_assistant_context.h
new file mode 100644
index 0000000..cc98c59
--- /dev/null
+++ b/runtime/oat_file_assistant_context.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
+#define ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "runtime.h"
+
+namespace art {
+
+// A helper class for OatFileAssistant that fetches and caches information including boot image
+// checksums, bootclasspath checksums, and APEX versions. The same instance can be reused across
+// OatFileAssistant calls on different dex files for different instruction sets.
+// This class is not thread-safe until `FetchAll` is called.
+class OatFileAssistantContext {
+ public:
+ // Options that a runtime would take.
+ // Note that the struct only keeps references, so the caller must keep the objects alive during
+ // the lifetime of OatFileAssistant.
+ struct RuntimeOptions {
+ // Required. See `-Ximage`.
+ const std::vector<std::string>& image_locations;
+ // Required. See `-Xbootclasspath`.
+ const std::vector<std::string>& boot_class_path;
+ // Required. See `-Xbootclasspath-locations`.
+ const std::vector<std::string>& boot_class_path_locations;
+ // Optional. See `-Xbootclasspathfds`.
+ const std::vector<int>* const boot_class_path_fds = nullptr;
+ // Optional. See `-Xdeny-art-apex-data-files`.
+ const bool deny_art_apex_data_files = false;
+ };
+
+ // Information about a boot image.
+ struct BootImageInfo {
+ // Number of BCP jars covered by the boot image.
+ size_t component_count;
+ // Checksum of the boot image. The format is "i;<component_count>/<checksum_in_8_digit_hex>"
+ std::string checksum;
+ };
+
+ // Constructs OatFileAssistantContext from runtime options. Does not fetch information on
+ // construction. Information will be fetched from disk when needed.
+ explicit OatFileAssistantContext(std::unique_ptr<RuntimeOptions> runtime_options);
+ // Constructs OatFileAssistantContext from a runtime instance. Fetches as much information as
+ // possible from the runtime. The rest information will be fetched from disk when needed.
+ explicit OatFileAssistantContext(Runtime* runtime);
+ // Returns runtime options.
+ const RuntimeOptions& GetRuntimeOptions() const;
+ // Fetches all information that hasn't been fetched from disk and caches it. All operations will
+ // be read-only after a successful call to this function.
+ bool FetchAll(std::string* error_msg);
+ // Returns information about the boot image of the given instruction set.
+ const std::vector<BootImageInfo>& GetBootImageInfoList(InstructionSet isa);
+ // Returns the checksums of the dex files in the BCP jar at the given index, or nullptr on error.
+ // The format of each checksum is "/<checksum_in_8_digit_hex>".
+ const std::vector<std::string>* GetBcpChecksums(size_t bcp_index, std::string* error_msg);
+ // Returns a string that represents the apex versions of boot classpath jars. See
+ // `Runtime::apex_versions_` for the encoding format.
+ const std::string& GetApexVersions();
+
+ private:
+ std::unique_ptr<RuntimeOptions> runtime_options_;
+ std::unordered_map<InstructionSet, std::vector<BootImageInfo>> boot_image_info_list_by_isa_;
+ std::unordered_map<size_t, std::vector<std::string>> bcp_checksums_by_index_;
+ std::optional<std::string> apex_versions_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 07998dd..45f7631 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -16,97 +16,157 @@
#include "oat_file_assistant.h"
+#include <fcntl.h>
+#include <gtest/gtest.h>
#include <sys/param.h>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <optional>
#include <string>
+#include <type_traits>
#include <vector>
-#include <fcntl.h>
-#include <gtest/gtest.h>
-
+#include "android-base/scopeguard.h"
#include "android-base/strings.h"
-
+#include "arch/instruction_set.h"
#include "art_field-inl.h"
#include "base/os.h"
#include "base/utils.h"
-#include "class_linker-inl.h"
+#include "class_linker.h"
#include "class_loader_context.h"
#include "common_runtime_test.h"
#include "dexopt_test.h"
-#include "hidden_api.h"
#include "oat.h"
#include "oat_file.h"
+#include "oat_file_assistant_context.h"
#include "oat_file_manager.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-current-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
namespace art {
-class OatFileAssistantTest : public DexoptTest {
+class OatFileAssistantBaseTest : public DexoptTest {};
+
+class OatFileAssistantTest : public OatFileAssistantBaseTest,
+ public testing::WithParamInterface<bool> {
public:
- void VerifyOptimizationStatus(OatFileAssistant* assistant,
- const std::string& file,
- const std::string& expected_filter,
+ void SetUp() override {
+ DexoptTest::SetUp();
+ with_runtime_ = GetParam();
+ ofa_context_ = CreateOatFileAssistantContext();
+ }
+
+ // Verifies all variants of `GetOptimizationStatus`.
+ //
+ // `expected_filter` can be either a value of `CompilerFilter::Filter` or a string.
+ // If `check_context` is true, only verifies the variants that checks class loader context.
+ template <typename T>
+ void VerifyOptimizationStatus(const std::string& file,
+ ClassLoaderContext* context,
+ const T& expected_filter,
const std::string& expected_reason,
- const std::string& expected_odex_status) {
- // Verify the static methods (called from PM for dexOptNeeded).
- std::string compilation_filter1;
- std::string compilation_reason1;
+ const std::string& expected_odex_status,
+ bool check_context = false) {
+ std::string expected_filter_name;
+ if constexpr (std::is_same_v<T, CompilerFilter::Filter>) {
+ expected_filter_name = CompilerFilter::NameOfFilter(expected_filter);
+ } else {
+ expected_filter_name = expected_filter;
+ }
- OatFileAssistant::GetOptimizationStatus(
- file, kRuntimeISA, &compilation_filter1, &compilation_reason1);
+ // Verify the static method (called from PM for dumpsys).
+ // This variant does not check class loader context.
+ if (!check_context) {
+ std::string compilation_filter;
+ std::string compilation_reason;
- ASSERT_EQ(expected_filter, compilation_filter1);
- ASSERT_EQ(expected_reason, compilation_reason1);
+ OatFileAssistant::GetOptimizationStatus(file,
+ kRuntimeISA,
+ &compilation_filter,
+ &compilation_reason,
+ MaybeGetOatFileAssistantContext());
- // Verify the instance methods (called at runtime for systrace).
- std::string odex_location2; // ignored
- std::string compilation_filter2;
- std::string compilation_reason2;
- std::string odex_status2;
+ ASSERT_EQ(expected_filter_name, compilation_filter);
+ ASSERT_EQ(expected_reason, compilation_reason);
+ }
+
+ // Verify the instance methods (called at runtime and from artd).
+ OatFileAssistant assistant = CreateOatFileAssistant(file.c_str(), context);
+ VerifyOptimizationStatusWithInstance(
+ &assistant, expected_filter_name, expected_reason, expected_odex_status);
+ }
+
+ void VerifyOptimizationStatusWithInstance(OatFileAssistant* assistant,
+ const std::string& expected_filter,
+ const std::string& expected_reason,
+ const std::string& expected_odex_status) {
+ std::string odex_location; // ignored
+ std::string compilation_filter;
+ std::string compilation_reason;
+ std::string odex_status;
assistant->GetOptimizationStatus(
- &odex_location2,
- &compilation_filter2,
- &compilation_reason2,
- &odex_status2);
+ &odex_location, &compilation_filter, &compilation_reason, &odex_status);
- ASSERT_EQ(expected_filter, compilation_filter2);
- ASSERT_EQ(expected_reason, compilation_reason2);
- ASSERT_EQ(expected_odex_status, odex_status2);
+ ASSERT_EQ(expected_filter, compilation_filter);
+ ASSERT_EQ(expected_reason, compilation_reason);
+ ASSERT_EQ(expected_odex_status, odex_status);
}
- void VerifyOptimizationStatus(OatFileAssistant* assistant,
- const std::string& file,
- CompilerFilter::Filter expected_filter,
- const std::string& expected_reason,
- const std::string& expected_odex_status) {
- VerifyOptimizationStatus(
- assistant,
- file,
- CompilerFilter::NameOfFilter(expected_filter),
- expected_reason,
- expected_odex_status);
- }
-
- void InsertNewBootClasspathEntry() {
- std::string extra_dex_filename = GetMultiDexSrc1();
- Runtime* runtime = Runtime::Current();
- runtime->boot_class_path_.push_back(extra_dex_filename);
- if (!runtime->boot_class_path_locations_.empty()) {
- runtime->boot_class_path_locations_.push_back(extra_dex_filename);
+ bool InsertNewBootClasspathEntry(const std::string& src, std::string* error_msg) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ ArtDexFileLoader dex_file_loader;
+ if (!dex_file_loader.Open(src.c_str(),
+ src,
+ /*verify=*/true,
+ /*verify_checksum=*/false,
+ error_msg,
+ &dex_files)) {
+ return false;
}
+
+ runtime_->AppendToBootClassPath(src, src, dex_files);
+ std::move(dex_files.begin(), dex_files.end(), std::back_inserter(opened_dex_files_));
+
+ return true;
}
- int GetDexOptNeeded(
- OatFileAssistant* assistant,
- CompilerFilter::Filter compiler_filter,
- bool profile_changed = false,
- bool downgrade = false) {
- return assistant->GetDexOptNeeded(
- compiler_filter,
- profile_changed,
- downgrade);
+ // Verifies the current version of `GetDexOptNeeded` (called from artd).
+ void VerifyGetDexOptNeeded(OatFileAssistant* assistant,
+ CompilerFilter::Filter compiler_filter,
+ OatFileAssistant::DexOptTrigger dexopt_trigger,
+ bool expected_dexopt_needed,
+ bool expected_is_vdex_usable,
+ OatFileAssistant::Location expected_location) {
+ OatFileAssistant::DexOptStatus status;
+ EXPECT_EQ(
+ assistant->GetDexOptNeeded(compiler_filter, dexopt_trigger, &status),
+ expected_dexopt_needed);
+ EXPECT_EQ(status.IsVdexUsable(), expected_is_vdex_usable);
+ EXPECT_EQ(status.GetLocation(), expected_location);
+ }
+
+ // Verifies all versions of `GetDexOptNeeded` with the default dexopt trigger.
+ void VerifyGetDexOptNeededDefault(OatFileAssistant* assistant,
+ CompilerFilter::Filter compiler_filter,
+ bool expected_dexopt_needed,
+ bool expected_is_vdex_usable,
+ OatFileAssistant::Location expected_location,
+ int expected_legacy_result) {
+ // Verify the current version (called from artd).
+ VerifyGetDexOptNeeded(assistant,
+ compiler_filter,
+ default_trigger_,
+ expected_dexopt_needed,
+ expected_is_vdex_usable,
+ expected_location);
+
+ // Verify the legacy version (called from PM).
+ EXPECT_EQ(
+ assistant->GetDexOptNeeded(compiler_filter, /*profile_changed=*/false, /*downgrade=*/false),
+ expected_legacy_result);
}
static std::unique_ptr<ClassLoaderContext> InitializeDefaultContext() {
@@ -115,7 +175,65 @@
return context;
}
+ // Temporarily disables the pointer to the current runtime if `with_runtime_` is false.
+ // Essentially simulates an environment where there is no active runtime.
+ android::base::ScopeGuard<std::function<void()>> ScopedMaybeWithoutRuntime() {
+ if (!with_runtime_) {
+ Runtime::TestOnlySetCurrent(nullptr);
+ }
+ return android::base::make_scope_guard(
+ [this]() { Runtime::TestOnlySetCurrent(runtime_.get()); });
+ }
+
+ std::unique_ptr<OatFileAssistantContext> CreateOatFileAssistantContext() {
+ return std::make_unique<OatFileAssistantContext>(
+ std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+ OatFileAssistantContext::RuntimeOptions{
+ .image_locations = runtime_->GetImageLocations(),
+ .boot_class_path = runtime_->GetBootClassPath(),
+ .boot_class_path_locations = runtime_->GetBootClassPathLocations(),
+ .boot_class_path_fds = !runtime_->GetBootClassPathFds().empty() ?
+ &runtime_->GetBootClassPathFds() :
+ nullptr,
+ .deny_art_apex_data_files = runtime_->DenyArtApexDataFiles(),
+ }));
+ }
+
+ OatFileAssistantContext* MaybeGetOatFileAssistantContext() {
+ return with_runtime_ ? nullptr : ofa_context_.get();
+ }
+
+ // A helper function to create OatFileAssistant with some default arguments.
+ OatFileAssistant CreateOatFileAssistant(const char* dex_location,
+ ClassLoaderContext* context = nullptr,
+ bool load_executable = false,
+ int vdex_fd = -1,
+ int oat_fd = -1,
+ int zip_fd = -1) {
+ return OatFileAssistant(dex_location,
+ kRuntimeISA,
+ context != nullptr ? context : default_context_.get(),
+ load_executable,
+ /*only_load_trusted_executable=*/false,
+ MaybeGetOatFileAssistantContext(),
+ vdex_fd,
+ oat_fd,
+ zip_fd);
+ }
+
+ void ExpectHasDexFiles(OatFileAssistant* oat_file_assistant, bool expected_value) {
+ std::string error_msg;
+ std::optional<bool> has_dex_files = oat_file_assistant->HasDexFiles(&error_msg);
+ ASSERT_TRUE(has_dex_files.has_value()) << error_msg;
+ EXPECT_EQ(*has_dex_files, expected_value);
+ }
+
std::unique_ptr<ClassLoaderContext> default_context_ = InitializeDefaultContext();
+ bool with_runtime_;
+ const OatFileAssistant::DexOptTrigger default_trigger_{
+ .targetFilterIsBetter = true, .primaryBootImageBecomesUsable = true, .needExtraction = true};
+ std::unique_ptr<OatFileAssistantContext> ofa_context_;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
};
class ScopedNonWritable {
@@ -154,7 +272,7 @@
// Case: We have a MultiDEX file and up-to-date ODEX file for it with relative
// encoded dex locations.
// Expect: The oat file status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
+TEST_P(OatFileAssistantTest, RelativeEncodedDexLocation) {
std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
@@ -172,21 +290,24 @@
std::string error_msg;
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Verify we can load both dex files.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
}
-TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
+TEST_P(OatFileAssistantTest, MakeUpToDateWithContext) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string context_location = GetScratchDir() + "/ContextDex.jar";
@@ -198,8 +319,6 @@
ASSERT_TRUE(context != nullptr);
ASSERT_TRUE(context->OpenDexFiles());
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, context.get(), false);
-
std::string error_msg;
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
@@ -207,6 +326,10 @@
args.push_back("--class-loader-context=" + context_str);
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), context.get());
+
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_NE(nullptr, oat_file.get());
ASSERT_NE(nullptr, oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
@@ -214,7 +337,7 @@
oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
}
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string context_location = GetScratchDir() + "/ContextDex.jar";
@@ -228,11 +351,6 @@
std::vector<int> context_fds;
ASSERT_TRUE(relative_context->OpenDexFiles(GetScratchDir(), context_fds));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- relative_context.get(),
- false);
-
std::string error_msg;
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
@@ -240,56 +358,80 @@
args.push_back("--class-loader-context=PCL[" + context_location + "]");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kDefaultCompilerFilter));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), relative_context.get());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kDefaultCompilerFilter,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
}
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, DexNoOat) {
+TEST_P(OatFileAssistantTest, DexNoOat) {
std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeedProfile));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "run-from-apk",
- "unknown",
- "io-error-no-oat");
+ dex_location, default_context_.get(), "run-from-apk", "unknown", "io-error-no-oat");
}
// Case: We have no DEX file and no OAT file.
// Expect: Status is kNoDexOptNeeded. Loading should fail, but not crash.
-TEST_F(OatFileAssistantTest, NoDexNoOat) {
+TEST_P(OatFileAssistantTest, NoDexNoOat) {
std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ std::string error_msg_ignored;
+ EXPECT_FALSE(oat_file_assistant.HasDexFiles(&error_msg_ignored).has_value());
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -298,85 +440,106 @@
// Case: We have a DEX file and an ODEX file, but no OAT file.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OdexUpToDate) {
+TEST_P(OatFileAssistantTest, OdexUpToDate) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
- // Force the use of oat location by making the dex parent not writable.
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- /*load_executable=*/ false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ // Force the use of oat location by making the dex parent not writable.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- CompilerFilter::kSpeed,
- "install",
- "up-to-date");
+ dex_location, default_context_.get(), CompilerFilter::kSpeed, "install", "up-to-date");
}
// Case: We have an ODEX file compiled against partial boot image.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OdexUpToDatePartialBootImage) {
+TEST_P(OatFileAssistantTest, OdexUpToDatePartialBootImage) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
// Insert an extra dex file to the boot class path.
- InsertNewBootClasspathEntry();
+ std::string error_msg;
+ ASSERT_TRUE(InsertNewBootClasspathEntry(GetMultiDexSrc1(), &error_msg)) << error_msg;
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Force the use of oat location by making the dex parent not writable.
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- /*load_executable=*/ false);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- CompilerFilter::kSpeed,
- "install",
- "up-to-date");
+ dex_location, default_context_.get(), CompilerFilter::kSpeed, "install", "up-to-date");
}
// Case: We have a DEX file and a PIC ODEX file, but no OAT file. We load the dex
// file via a symlink.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OdexUpToDateSymLink) {
+TEST_P(OatFileAssistantTest, OdexUpToDateSymLink) {
std::string scratch_dir = GetScratchDir();
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
@@ -389,29 +552,44 @@
ASSERT_EQ(0, symlink(scratch_dir.c_str(), link.c_str()));
dex_location = link + "/OdexUpToDate.jar";
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OatUpToDate) {
+TEST_P(OatFileAssistantTest, OatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -426,36 +604,47 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- CompilerFilter::kSpeed,
- "unknown",
- "up-to-date");
+ dex_location, default_context_.get(), CompilerFilter::kSpeed, "unknown", "up-to-date");
}
// Case: Passing valid file descriptors of updated odex/vdex files along with the dex file.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex";
@@ -470,32 +659,48 @@
android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY | O_CLOEXEC));
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- vdex_fd.get(),
- odex_fd.get(),
- zip_fd.get());
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ vdex_fd.get(),
+ odex_fd.get(),
+ zip_fd.get());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: Passing invalid odex fd and valid vdex and zip fds.
// Expect: The status should be kDex2OatForBootImage.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex";
@@ -509,30 +714,42 @@
android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY | O_CLOEXEC));
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- vdex_fd.get(),
- /* oat_fd= */ -1,
- zip_fd.get());
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ vdex_fd.get(),
+ /*oat_fd=*/-1,
+ zip_fd.get());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: Passing invalid vdex fd and valid odex and zip fds.
// Expect: The status should be kDex2OatFromScratch.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
@@ -545,48 +762,57 @@
android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY | O_CLOEXEC));
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- /* vdex_fd= */ -1,
- odex_fd.get(),
- zip_fd.get());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ /*vdex_fd=*/-1,
+ odex_fd.get(),
+ zip_fd.get());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: Passing invalid vdex and odex fd with valid zip fd.
// Expect: The status is kDex2oatFromScratch.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- /* vdex_fd= */ -1,
- /* oat_fd= */ -1,
- zip_fd);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ /*vdex_fd=*/-1,
+ /*oat_fd=*/-1,
+ zip_fd);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
-// Case: We have a DEX file and up-to-date VDEX file for it, but no
-// ODEX file.
-TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) {
+// Case: We have a DEX file and up-to-date VDEX file for it, but no ODEX file, and the DEX file is
+// compressed.
+TEST_P(OatFileAssistantTest, VdexUpToDateNoOdex) {
std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar";
std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat";
@@ -597,30 +823,32 @@
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
ASSERT_EQ(0, unlink(odex_location.c_str()));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
// Make sure we don't crash in this case when we dump the status. We don't
// care what the actual dumped value is.
oat_file_assistant.GetStatusDump();
- VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "verify",
- "vdex",
- "up-to-date");
+ VerifyOptimizationStatus(dex_location, default_context_.get(), "verify", "vdex", "up-to-date");
}
// Case: We have a DEX file and empty VDEX and ODEX files.
-TEST_F(OatFileAssistantTest, EmptyVdexOdex) {
+TEST_P(OatFileAssistantTest, EmptyVdexOdex) {
std::string dex_location = GetScratchDir() + "/EmptyVdexOdex.jar";
std::string odex_location = GetOdexDir() + "/EmptyVdexOdex.oat";
std::string vdex_location = GetOdexDir() + "/EmptyVdexOdex.vdex";
@@ -629,17 +857,20 @@
ScratchFile vdex_file(vdex_location.c_str());
ScratchFile odex_file(odex_location.c_str());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
}
// Case: We have a DEX file and up-to-date (OAT) VDEX file for it, but no OAT
// file.
-TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) {
+TEST_P(OatFileAssistantTest, VdexUpToDateNoOat) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -650,7 +881,8 @@
std::string oat_location;
std::string error_msg;
ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
- dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+ dex_location, kRuntimeISA, /* deny_art_apex_data_files= */false, &oat_location, &error_msg))
+ << error_msg;
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
@@ -658,19 +890,23 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
}
// Case: We have a DEX file and speed-profile OAT file for it.
// Expect: The status is kNoDexOptNeeded if the profile hasn't changed, but
// kDex2Oat if the profile has changed.
-TEST_F(OatFileAssistantTest, ProfileOatUpToDate) {
+TEST_P(OatFileAssistantTest, ProfileOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -684,29 +920,62 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
+ EXPECT_EQ(
+ OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, /*profile_changed=*/false));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeedProfile, false));
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify, /*profile_changed=*/false));
+
+ OatFileAssistant::DexOptTrigger profile_changed_trigger = default_trigger_;
+ profile_changed_trigger.targetFilterIsSame = true;
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ profile_changed_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
+ EXPECT_EQ(
+ OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, /*profile_changed=*/true));
+
+ // We should not recompile even if `profile_changed` is true because the compiler filter should
+ // not be downgraded.
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ profile_changed_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify, false));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeedProfile, true));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify, true));
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify, /*profile_changed=*/true));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a MultiDEX file and up-to-date OAT file for it.
// Expect: The status is kNoDexOptNeeded and we load all dex files.
-TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
+TEST_P(OatFileAssistantTest, MultiDexOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -720,18 +989,25 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ ExpectHasDexFiles(&oat_file_assistant, true);
// Verify we can load both dex files.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
@@ -739,7 +1015,7 @@
// Case: We have a MultiDEX file where the non-main multdex entry is out of date.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
+TEST_P(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -759,18 +1035,21 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and an OAT file out of date with respect to the
// dex checksum.
-TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
+TEST_P(OatFileAssistantTest, OatDexOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -788,31 +1067,34 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "run-from-apk-fallback",
- "unknown",
- "apk-more-recent");
+ dex_location, default_context_.get(), "run-from-apk-fallback", "unknown", "apk-more-recent");
}
// Case: We have a DEX file and an (ODEX) VDEX file out of date with respect
// to the dex checksum, but no ODEX file.
-TEST_F(OatFileAssistantTest, VdexDexOutOfDate) {
+TEST_P(OatFileAssistantTest, VdexDexOutOfDate) {
std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat";
@@ -821,18 +1103,21 @@
ASSERT_EQ(0, unlink(odex_location.c_str()));
Copy(GetDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
}
// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry
// is out of date and there is no corresponding ODEX file.
-TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
+TEST_P(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex";
@@ -841,18 +1126,21 @@
ASSERT_EQ(0, unlink(odex_location.c_str()));
Copy(GetMultiDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
}
// Case: We have a DEX file and an OAT file out of date with respect to the
// boot image.
-TEST_F(OatFileAssistantTest, OatImageOutOfDate) {
+TEST_P(OatFileAssistantTest, OatImageOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -869,35 +1157,70 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
+
+ VerifyOptimizationStatus(dex_location, default_context_.get(), "verify", "vdex", "up-to-date");
+}
+
+TEST_P(OatFileAssistantTest, OatContextOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--class-loader-context=PCL[" + context_location + "]");
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Update the context by overriding the jar file.
+ Copy(GetMultiDexSrc2(), context_location);
+
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create("PCL[" + context_location + "]");
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles());
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "verify",
- "vdex",
- "up-to-date");
+ dex_location.c_str(), context.get(), "verify", "vdex", "up-to-date", /*check_context=*/true);
}
// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
// respect to the boot image.
// It shouldn't matter that the OAT file is out of date, because it is
// verify-at-runtime.
-TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
+TEST_P(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -914,23 +1237,30 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and an ODEX file, but no OAT file.
-TEST_F(OatFileAssistantTest, DexOdexNoOat) {
+TEST_P(OatFileAssistantTest, DexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
@@ -938,21 +1268,28 @@
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
- // Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ // Verify the status.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
// We should still be able to get the non-executable odex file to run from.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -961,41 +1298,56 @@
// Case: We have a resource-only DEX file, no ODEX file and no
// OAT file. Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
+TEST_P(OatFileAssistantTest, ResourceOnlyDex) {
std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
Copy(GetResourceOnlySrc1(), dex_location);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, false);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, false);
}
// Case: We have a DEX file, an ODEX file and an OAT file.
// Expect: It shouldn't crash. We should load the odex file executable.
-TEST_F(OatFileAssistantTest, OdexOatOverlap) {
+TEST_P(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -1004,24 +1356,31 @@
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
- // Verify things don't go bad.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ // Verify things don't go bad.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1029,7 +1388,7 @@
// Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file.
// Expect: The status is kNoDexOptNeeded, because VerifyAtRuntime contains no code.
-TEST_F(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) {
+TEST_P(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex";
@@ -1037,26 +1396,33 @@
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kExtract);
- // Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ // Verify the status.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: We should load an executable dex file.
-TEST_F(OatFileAssistantTest, LoadOatUpToDate) {
+TEST_P(OatFileAssistantTest, LoadOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -1071,15 +1437,18 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1087,7 +1456,7 @@
// Case: We have a DEX file and up-to-date quicken OAT file for it.
// Expect: We should still load the oat file as executable.
-TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) {
+TEST_P(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -1102,15 +1471,18 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1118,7 +1490,7 @@
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: Loading non-executable should load the oat non-executable.
-TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
+TEST_P(OatFileAssistantTest, LoadNoExecOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -1134,15 +1506,18 @@
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_FALSE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1186,54 +1561,64 @@
// Case: Non-absolute path to Dex location.
// Expect: Not sure, but it shouldn't crash.
-TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) {
+TEST_P(OatFileAssistantTest, NonAbsoluteDexLocation) {
std::string abs_dex_location = GetScratchDir() + "/NonAbsoluteDexLocation.jar";
Copy(GetDexSrc1(), abs_dex_location);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
std::string dex_location = MakePathRelative(abs_dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// Case: Very short, non-existent Dex location.
// Expect: kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, ShortDexLocation) {
+TEST_P(OatFileAssistantTest, ShortDexLocation) {
std::string dex_location = "/xx";
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ std::string error_msg_ignored;
+ EXPECT_FALSE(oat_file_assistant.HasDexFiles(&error_msg_ignored).has_value());
}
// Case: Non-standard extension for dex file.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, LongDexExtension) {
+TEST_P(OatFileAssistantTest, LongDexExtension) {
std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
@@ -1243,7 +1628,7 @@
// A task to generate a dex location. Used by the RaceToGenerate test.
class RaceGenerateTask : public Task {
public:
- RaceGenerateTask(OatFileAssistantTest& test,
+ RaceGenerateTask(OatFileAssistantBaseTest& test,
const std::string& dex_location,
const std::string& oat_location,
Mutex* lock)
@@ -1251,8 +1636,7 @@
dex_location_(dex_location),
oat_location_(oat_location),
lock_(lock),
- loaded_oat_file_(nullptr)
- {}
+ loaded_oat_file_(nullptr) {}
void Run(Thread* self ATTRIBUTE_UNUSED) override {
// Load the dex files, and save a pointer to the loaded oat file, so that
@@ -1288,7 +1672,7 @@
}
private:
- OatFileAssistantTest& test_;
+ OatFileAssistantBaseTest& test_;
std::string dex_location_;
std::string oat_location_;
Mutex* lock_;
@@ -1297,7 +1681,7 @@
// Test the case where dex2oat invocations race with multiple processes trying to
// load the oat file.
-TEST_F(OatFileAssistantTest, RaceToGenerate) {
+TEST_F(OatFileAssistantBaseTest, RaceToGenerate) {
std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
@@ -1309,7 +1693,7 @@
// take a while to generate.
Copy(GetLibCoreDexFileNames()[0], dex_location);
- const size_t kNumThreads = 32;
+ const size_t kNumThreads = 16;
Thread* self = Thread::Current();
ThreadPool thread_pool("Oat file assistant test thread pool", kNumThreads);
std::vector<std::unique_ptr<RaceGenerateTask>> tasks;
@@ -1336,7 +1720,7 @@
// Case: We have a DEX file and an ODEX file, and no OAT file,
// Expect: We should load the odex file executable.
-TEST_F(OatFileAssistantTest, LoadDexOdexNoOat) {
+TEST_P(OatFileAssistantTest, LoadDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadDexOdexNoOat.odex";
@@ -1344,15 +1728,18 @@
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1360,7 +1747,7 @@
// Case: We have a MultiDEX file and an ODEX file, and no OAT file.
// Expect: We should load the odex file executable.
-TEST_F(OatFileAssistantTest, LoadMultiDexOdexNoOat) {
+TEST_P(OatFileAssistantTest, LoadMultiDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadMultiDexOdexNoOat.odex";
@@ -1368,15 +1755,18 @@
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
@@ -1402,7 +1792,7 @@
// Verify the dexopt status values from dalvik.system.DexFile
// match the OatFileAssistant::DexOptStatus values.
-TEST_F(OatFileAssistantTest, DexOptStatusValues) {
+TEST_F(OatFileAssistantBaseTest, DexOptStatusValues) {
std::pair<OatFileAssistant::DexOptNeeded, const char*> mapping[] = {
{OatFileAssistant::kNoDexOptNeeded, "NO_DEXOPT_NEEDED"},
{OatFileAssistant::kDex2OatFromScratch, "DEX2OAT_FROM_SCRATCH"},
@@ -1426,7 +1816,7 @@
}
}
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
@@ -1455,43 +1845,418 @@
ASSERT_TRUE(updated_context != nullptr);
std::vector<int> context_fds;
ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, updated_context.get(), false);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), updated_context.get());
// DexOptNeeded should advise compilation for filter when the context changes.
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kDefaultCompilerFilter));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kDefaultCompilerFilter,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
}
{
std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str);
ASSERT_TRUE(updated_context != nullptr);
std::vector<int> context_fds;
- ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, updated_context.get(), false);
- // Now check that DexOptNeeded does not advise compilation if we only extracted the file.
+ ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
args.push_back("--compiler-filter=extract");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), updated_context.get());
+ // Now check that DexOptNeeded does not advise compilation if we only extracted the file.
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
}
{
std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str);
ASSERT_TRUE(updated_context != nullptr);
std::vector<int> context_fds;
- ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, updated_context.get(), false);
- // Now check that DexOptNeeded does not advise compilation if we only verify the file.
+ ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
args.push_back("--compiler-filter=verify");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), updated_context.get());
+ // Now check that DexOptNeeded does not advise compilation if we only verify the file.
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
}
}
+// Case: We have a DEX file and speed-profile ODEX file for it. The caller's intention is to
+// downgrade the compiler filter.
+// Expect: Dexopt should be performed only if the target compiler filter is worse than the current
+// one.
+TEST_P(OatFileAssistantTest, Downgrade) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger downgrade_trigger{.targetFilterIsWorse = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeed, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeedProfile, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kVerify, /*profile_changed=*/false, /*downgrade=*/true));
+}
+
+// Case: We have a DEX file but we don't have an ODEX file for it. The caller's intention is to
+// downgrade the compiler filter.
+// Expect: Dexopt should never be performed regardless of the target compiler filter.
+TEST_P(OatFileAssistantTest, DowngradeNoOdex) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger downgrade_trigger{.targetFilterIsWorse = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeed, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeedProfile, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kVerify, /*profile_changed=*/false, /*downgrade=*/true));
+}
+
+// Case: We have a DEX file and speed-profile ODEX file for it. The legacy version is called with
+// both `profile_changed` and `downgrade` being true. This won't happen in the real case. Just to be
+// complete.
+// Expect: The behavior should be as `profile_changed` is false and `downgrade` is true.
+TEST_P(OatFileAssistantTest, ProfileChangedDowngrade) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeed, /*profile_changed=*/true, /*downgrade=*/true));
+
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeedProfile, /*profile_changed=*/true, /*downgrade=*/true));
+
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kVerify, /*profile_changed=*/true, /*downgrade=*/true));
+}
+
+// Case: We have a DEX file and speed-profile ODEX file for it. The caller's intention is to force
+// the compilation.
+// Expect: Dexopt should be performed regardless of the target compiler filter. The VDEX file is
+// usable.
+//
+// The legacy version does not support this case. Historically, Package Manager does not take the
+// result from OatFileAssistant for forced compilation. It uses an arbitrary non-zero value instead.
+// Therefore, we don't test the legacy version here.
+TEST_P(OatFileAssistantTest, Force) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger force_trigger{.targetFilterIsBetter = true,
+ .targetFilterIsSame = true,
+ .targetFilterIsWorse = true,
+ .primaryBootImageBecomesUsable = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+}
+
+// Case: We have a DEX file but we don't have an ODEX file for it. The caller's intention is to
+// force the compilation.
+// Expect: Dexopt should be performed regardless of the target compiler filter. No VDEX file is
+// usable.
+//
+// The legacy version does not support this case. Historically, Package Manager does not take the
+// result from OatFileAssistant for forced compilation. It uses an arbitrary non-zero value instead.
+// Therefore, we don't test the legacy version here.
+TEST_P(OatFileAssistantTest, ForceNoOdex) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger force_trigger{.targetFilterIsBetter = true,
+ .targetFilterIsSame = true,
+ .targetFilterIsWorse = true,
+ .primaryBootImageBecomesUsable = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+}
+
+// Case: We have a DEX file and a DM file for it, and the DEX file is uncompressed.
+// Expect: Dexopt should be performed if the compiler filter is better than "verify". The location
+// should be kLocationDm.
+//
+// The legacy version should return kDex2OatFromScratch if the target compiler filter is better than
+// "verify".
+TEST_P(OatFileAssistantTest, DmUpToDateDexUncompressed) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
+ Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
+
+ // Generate temporary ODEX and VDEX files in order to create the DM file from.
+ GenerateOdexForTest(
+ dex_location, odex_location, CompilerFilter::kVerify, "install", {"--copy-dex-files=false"});
+
+ CreateDexMetadata(vdex_location, dm_location);
+
+ // Cleanup the temporary files.
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+ ASSERT_EQ(0, unlink(vdex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
+// Case: We have a DEX file and a DM file for it, and the DEX file is compressed.
+// Expect: Dexopt should be performed regardless of the compiler filter. The location
+// should be kLocationDm.
+TEST_P(OatFileAssistantTest, DmUpToDateDexCompressed) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ // Generate temporary ODEX and VDEX files in order to create the DM file from.
+ GenerateOdexForTest(
+ dex_location, odex_location, CompilerFilter::kVerify, "install", {"--copy-dex-files=false"});
+
+ CreateDexMetadata(vdex_location, dm_location);
+
+ // Cleanup the temporary files.
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+ ASSERT_EQ(0, unlink(vdex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
+// Case: We have an ODEX file, but the DEX file is gone.
+// Expect: No dexopt is needed, as there's nothing we can do.
+TEST_P(OatFileAssistantTest, OdexNoDex) {
+ std::string dex_location = GetScratchDir() + "/OdexNoDex.jar";
+ std::string odex_location = GetOdexDir() + "/OdexNoDex.oat";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ ASSERT_EQ(0, unlink(dex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+}
+
+// Case: We have a VDEX file, but the DEX file is gone.
+// Expect: No dexopt is needed, as there's nothing we can do.
+TEST_P(OatFileAssistantTest, VdexNoDex) {
+ std::string dex_location = GetScratchDir() + "/VdexNoDex.jar";
+ std::string odex_location = GetOdexDir() + "/VdexNoDex.oat";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+ ASSERT_EQ(0, unlink(dex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+}
+
// Test that GetLocation of a dex file is the same whether the dex
// filed is backed by an oat file or not.
-TEST_F(OatFileAssistantTest, GetDexLocation) {
+TEST_F(OatFileAssistantBaseTest, GetDexLocation) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string oat_location = GetOdexDir() + "/TestDex.odex";
std::string art_location = GetOdexDir() + "/TestDex.art";
@@ -1539,7 +2304,7 @@
// Test that a dex file on the platform location gets the right hiddenapi domain,
// regardless of whether it has a backing oat file.
-TEST_F(OatFileAssistantTest, SystemFrameworkDir) {
+TEST_F(OatFileAssistantBaseTest, SystemFrameworkDir) {
std::string filebase = "OatFileAssistantTestSystemFrameworkDir";
std::string dex_location = GetAndroidRoot() + "/framework/" + filebase + ".jar";
Copy(GetDexSrc1(), dex_location);
@@ -1619,7 +2384,7 @@
}
// Make sure OAT files that require app images are not loaded as executable.
-TEST_F(OatFileAssistantTest, LoadOatNoArt) {
+TEST_F(OatFileAssistantBaseTest, LoadOatNoArt) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string art_location = GetOdexDir() + "/TestDex.art";
@@ -1653,7 +2418,7 @@
EXPECT_FALSE(oat_file->IsExecutable());
}
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithApexVersions) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
Copy(GetDexSrc1(), dex_location);
@@ -1667,8 +2432,9 @@
args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions());
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, default_context_.get(), false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
}
@@ -1681,8 +2447,9 @@
args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions().substr(0, 1));
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, default_context_.get(), false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
}
@@ -1695,12 +2462,161 @@
args.push_back("--apex-versions=/1/2/3/4");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, default_context_.get(), false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OdexFileStatus());
}
}
+TEST_P(OatFileAssistantTest, Create) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ std::unique_ptr<OatFileAssistant> oat_file_assistant =
+ OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg);
+ ASSERT_NE(oat_file_assistant, nullptr);
+
+ // Verify that the created instance is usable.
+ VerifyOptimizationStatusWithInstance(oat_file_assistant.get(), "speed", "install", "up-to-date");
+}
+
+TEST_P(OatFileAssistantTest, CreateWithNullContext) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ std::unique_ptr<OatFileAssistant> oat_file_assistant =
+ OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/std::nullopt,
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg);
+ ASSERT_NE(oat_file_assistant, nullptr);
+ ASSERT_EQ(context, nullptr);
+
+ // Verify that the created instance is usable.
+ VerifyOptimizationStatusWithInstance(oat_file_assistant.get(), "speed", "install", "up-to-date");
+}
+
+TEST_P(OatFileAssistantTest, ErrorOnInvalidIsaString) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ /*isa_str=*/"foo",
+ default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg),
+ nullptr);
+ EXPECT_EQ(error_msg, "Instruction set 'foo' is invalid");
+}
+
+TEST_P(OatFileAssistantTest, ErrorOnInvalidContextString) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/"foo",
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg),
+ nullptr);
+ EXPECT_EQ(error_msg, "Class loader context 'foo' is invalid");
+}
+
+TEST_P(OatFileAssistantTest, ErrorOnInvalidContextFile) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ // Create a broken context file.
+ std::string context_location = GetScratchDir() + "/BrokenContext.jar";
+ std::ofstream output(context_location);
+ output.close();
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/"PCL[" + context_location + "]",
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg),
+ nullptr);
+ EXPECT_EQ(error_msg,
+ "Failed to load class loader context files for '" + dex_location +
+ "' with context 'PCL[" + context_location + "]'");
+}
+
+// Verifies that `OatFileAssistant::ValidateBootClassPathChecksums` accepts the checksum string
+// produced by `gc::space::ImageSpace::GetBootClassPathChecksums`.
+TEST_P(OatFileAssistantTest, ValidateBootClassPathChecksums) {
+ std::string error_msg;
+ auto create_and_verify = [&]() {
+ std::string checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
+ ArrayRef<gc::space::ImageSpace* const>(runtime_->GetHeap()->GetBootImageSpaces()),
+ ArrayRef<const DexFile* const>(runtime_->GetClassLinker()->GetBootClassPath()));
+ std::string bcp_locations = android::base::Join(runtime_->GetBootClassPathLocations(), ':');
+
+ ofa_context_ = CreateOatFileAssistantContext();
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+ return OatFileAssistant::ValidateBootClassPathChecksums(
+ ofa_context_.get(), kRuntimeISA, checksums, bcp_locations, &error_msg);
+ };
+
+ ASSERT_TRUE(create_and_verify()) << error_msg;
+
+ for (const std::string& src : {GetDexSrc1(), GetDexSrc2()}) {
+ ASSERT_TRUE(InsertNewBootClasspathEntry(src, &error_msg)) << error_msg;
+ ASSERT_TRUE(create_and_verify()) << error_msg;
+ }
+}
+
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
@@ -1713,4 +2629,7 @@
// - Dex is stripped, don't have odex.
// - Oat file corrupted after status check, before reload unexecutable
// because it's unrelocated and no dex2oat
+
+INSTANTIATE_TEST_SUITE_P(WithOrWithoutRuntime, OatFileAssistantTest, testing::Values(true, false));
+
} // namespace art
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index ecf3a04..b0bcd2f 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -50,6 +50,7 @@
#include "oat_file.h"
#include "oat_file_assistant.h"
#include "obj_ptr-inl.h"
+#include "runtime_image.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
@@ -167,7 +168,7 @@
bool OatFileManager::ShouldLoadAppImage(const OatFile* source_oat_file) const {
Runtime* const runtime = Runtime::Current();
- return kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable());
+ return kEnableAppImage && (!runtime->IsJavaDebuggableAtInit() || source_oat_file->IsDebuggable());
}
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
@@ -196,11 +197,11 @@
LOG(WARNING) << "Opening an oat file without a class loader. "
<< "Are you using the deprecated DexFile APIs?";
} else if (context != nullptr) {
- OatFileAssistant oat_file_assistant(dex_location,
- kRuntimeISA,
- context.get(),
- runtime->GetOatFilesExecutable(),
- only_use_system_oat_files_);
+ auto oat_file_assistant = std::make_unique<OatFileAssistant>(dex_location,
+ kRuntimeISA,
+ context.get(),
+ runtime->GetOatFilesExecutable(),
+ only_use_system_oat_files_);
// Get the current optimization status for trace debugging.
// Implementation detail note: GetOptimizationStatus will select the same
@@ -210,17 +211,8 @@
std::string compilation_filter;
std::string compilation_reason;
std::string odex_status;
- oat_file_assistant.GetOptimizationStatus(
- &odex_location,
- &compilation_filter,
- &compilation_reason,
- &odex_status);
-
- Runtime::Current()->GetAppInfo()->RegisterOdexStatus(
- dex_location,
- compilation_filter,
- compilation_reason,
- odex_status);
+ oat_file_assistant->GetOptimizationStatus(
+ &odex_location, &compilation_filter, &compilation_reason, &odex_status);
ScopedTrace odex_loading(StringPrintf(
"location=%s status=%s filter=%s reason=%s",
@@ -240,7 +232,7 @@
code_type == AppInfo::CodeType::kSplitApk;
// Proceed with oat file loading.
- std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
+ std::unique_ptr<const OatFile> oat_file(oat_file_assistant->GetBestOatFile().release());
VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
<< (oat_file != nullptr ? oat_file->GetLocation() : "")
<< " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
@@ -251,74 +243,96 @@
<< " best_oat_file-location=" << oat_file->GetLocation();
if (oat_file != nullptr) {
+ bool compilation_enabled =
+ CompilerFilter::IsAotCompilationEnabled(oat_file->GetCompilerFilter());
// Load the dex files from the oat file.
bool added_image_space = false;
- if (oat_file->IsExecutable()) {
- if (should_madvise_vdex_and_odex) {
- VLOG(oat) << "Madvising oat file: " << oat_file->GetLocation();
- size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
- Runtime::MadviseFileForRange(madvise_size_limit,
- oat_file->Size(),
- oat_file->Begin(),
- oat_file->End(),
- oat_file->GetLocation());
- }
+ if (should_madvise_vdex_and_odex) {
+ VLOG(oat) << "Madvising oat file: " << oat_file->GetLocation();
+ size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
+ Runtime::MadviseFileForRange(madvise_size_limit,
+ oat_file->Size(),
+ oat_file->Begin(),
+ oat_file->End(),
+ oat_file->GetLocation());
+ }
- ScopedTrace app_image_timing("AppImage:Loading");
+ ScopedTrace app_image_timing("AppImage:Loading");
- // We need to throw away the image space if we are debuggable but the oat-file source of the
- // image is not otherwise we might get classes with inlined methods or other such things.
- std::unique_ptr<gc::space::ImageSpace> image_space;
- if (ShouldLoadAppImage(oat_file.get())) {
- image_space = oat_file_assistant.OpenImageSpace(oat_file.get());
+ // We need to throw away the image space if we are debuggable but the oat-file source of the
+ // image is not otherwise we might get classes with inlined methods or other such things.
+ std::unique_ptr<gc::space::ImageSpace> image_space;
+ if (ShouldLoadAppImage(oat_file.get())) {
+ if (oat_file->IsExecutable()) {
+ // App images generated by the compiler can only be used if the oat file
+ // is executable.
+ image_space = oat_file_assistant->OpenImageSpace(oat_file.get());
}
- if (image_space != nullptr) {
+ if (image_space == nullptr && !compilation_enabled) {
+ std::string art_file = RuntimeImage::GetRuntimeImagePath(dex_location);
+ std::string error_msg;
ScopedObjectAccess soa(self);
- StackHandleScope<1> hs(self);
- Handle<mirror::ClassLoader> h_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
- // Can not load app image without class loader.
- if (h_loader != nullptr) {
- std::string temp_error_msg;
- // Add image space has a race condition since other threads could be reading from the
- // spaces array.
+ image_space = gc::space::ImageSpace::CreateFromAppImage(
+ art_file.c_str(), oat_file.get(), &error_msg);
+ if (image_space == nullptr) {
+ VLOG(image) << "Could not load runtime generated app image: " << error_msg;
+ }
+ }
+ }
+ if (image_space != nullptr) {
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ // Can not load app image without class loader.
+ if (h_loader != nullptr) {
+ std::string temp_error_msg;
+ // Add image space has a race condition since other threads could be reading from the
+ // spaces array.
+ {
+ ScopedThreadSuspension sts(self, ThreadState::kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseAddRemoveAppImageSpace,
+ gc::kCollectorTypeAddRemoveAppImageSpace);
+ ScopedSuspendAll ssa("Add image space");
+ runtime->GetHeap()->AddSpace(image_space.get());
+ }
+ {
+ ScopedTrace image_space_timing("Adding image space");
+ added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
+ h_loader,
+ context.get(),
+ /*out*/&dex_files,
+ /*out*/&temp_error_msg);
+ }
+ if (added_image_space) {
+ // Successfully added image space to heap, release the map so that it does not get
+ // freed.
+ image_space.release(); // NOLINT b/117926937
+
+ // Register for tracking.
+ for (const auto& dex_file : dex_files) {
+ dex::tracking::RegisterDexFile(dex_file.get());
+ }
+
+ if (!compilation_enabled) {
+ // Update the filter we are going to report to 'speed-profile'.
+ // Ideally, we would also update the compiler filter of the odex
+ // file, but at this point it's just too late.
+ compilation_filter = CompilerFilter::NameOfFilter(CompilerFilter::kSpeedProfile);
+ }
+ } else {
+ LOG(INFO) << "Failed to add image file: " << temp_error_msg;
+ dex_files.clear();
{
ScopedThreadSuspension sts(self, ThreadState::kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseAddRemoveAppImageSpace,
gc::kCollectorTypeAddRemoveAppImageSpace);
- ScopedSuspendAll ssa("Add image space");
- runtime->GetHeap()->AddSpace(image_space.get());
+ ScopedSuspendAll ssa("Remove image space");
+ runtime->GetHeap()->RemoveSpace(image_space.get());
}
- {
- ScopedTrace image_space_timing("Adding image space");
- added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
- h_loader,
- /*out*/&dex_files,
- /*out*/&temp_error_msg);
- }
- if (added_image_space) {
- // Successfully added image space to heap, release the map so that it does not get
- // freed.
- image_space.release(); // NOLINT b/117926937
-
- // Register for tracking.
- for (const auto& dex_file : dex_files) {
- dex::tracking::RegisterDexFile(dex_file.get());
- }
- } else {
- LOG(INFO) << "Failed to add image file " << temp_error_msg;
- dex_files.clear();
- {
- ScopedThreadSuspension sts(self, ThreadState::kSuspended);
- gc::ScopedGCCriticalSection gcs(self,
- gc::kGcCauseAddRemoveAppImageSpace,
- gc::kCollectorTypeAddRemoveAppImageSpace);
- ScopedSuspendAll ssa("Remove image space");
- runtime->GetHeap()->RemoveSpace(image_space.get());
- }
- // Non-fatal, don't update error_msg.
- }
+ // Non-fatal, don't update error_msg.
}
}
}
@@ -330,12 +344,13 @@
<< oat_file->GetLocation()
<< " non-executable as it requires an image which we failed to load";
// file as non-executable.
- OatFileAssistant nonexecutable_oat_file_assistant(dex_location,
- kRuntimeISA,
- context.get(),
- /*load_executable=*/false,
- only_use_system_oat_files_);
- oat_file.reset(nonexecutable_oat_file_assistant.GetBestOatFile().release());
+ auto nonexecutable_oat_file_assistant =
+ std::make_unique<OatFileAssistant>(dex_location,
+ kRuntimeISA,
+ context.get(),
+ /*load_executable=*/false,
+ only_use_system_oat_files_);
+ oat_file.reset(nonexecutable_oat_file_assistant->GetBestOatFile().release());
// The file could be deleted concurrently (for example background
// dexopt, or secondary oat file being deleted by the app).
@@ -345,7 +360,7 @@
}
if (oat_file != nullptr) {
- dex_files = oat_file_assistant.LoadDexFiles(*oat_file.get(), dex_location);
+ dex_files = oat_file_assistant->LoadDexFiles(*oat_file.get(), dex_location);
// Register for tracking.
for (const auto& dex_file : dex_files) {
@@ -386,7 +401,7 @@
// If so, report an error with the current stack trace.
// Most likely the developer didn't intend to do this because it will waste
// performance and memory.
- if (oat_file_assistant.GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) {
+ if (oat_file_assistant->GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) {
std::set<const DexFile*> already_exists_in_classpath =
context->CheckForDuplicateDexFiles(MakeNonOwningPointerVector(dex_files));
if (!already_exists_in_classpath.empty()) {
@@ -419,6 +434,12 @@
}
}
}
+
+ Runtime::Current()->GetAppInfo()->RegisterOdexStatus(
+ dex_location,
+ compilation_filter,
+ compilation_reason,
+ odex_status);
}
// If we arrive here with an empty dex files list, it means we fail to load
@@ -570,7 +591,8 @@
// Initialize an OatFile instance backed by the loaded vdex.
std::unique_ptr<OatFile> oat_file(OatFile::OpenFromVdex(MakeNonOwningPointerVector(dex_files),
std::move(vdex_file),
- dex_location));
+ dex_location,
+ context.get()));
if (oat_file != nullptr) {
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
*out_oat_file = RegisterOatFile(std::move(oat_file));
@@ -691,7 +713,7 @@
h_loader)));
if (h_class == nullptr) {
- CHECK(self->IsExceptionPending());
+ DCHECK(self->IsExceptionPending());
self->ClearException();
continue;
}
@@ -702,15 +724,14 @@
continue;
}
- CHECK(h_class->IsResolved()) << h_class->PrettyDescriptor();
+ DCHECK(h_class->IsResolved()) << h_class->PrettyDescriptor();
class_linker->VerifyClass(self, &verifier_deps, h_class);
- if (h_class->IsErroneous()) {
- // ClassLinker::VerifyClass throws, which isn't useful here.
- CHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
+ if (self->IsExceptionPending()) {
+ // ClassLinker::VerifyClass can throw, but the exception isn't useful here.
+ self->ClearException();
}
- CHECK(h_class->IsVerified() || h_class->IsErroneous())
+ DCHECK(h_class->IsVerified() || h_class->IsErroneous())
<< h_class->PrettyDescriptor() << ": state=" << h_class->GetStatus();
if (h_class->IsVerified()) {
@@ -838,6 +859,15 @@
verification_thread_pool_.reset(nullptr);
}
+void OatFileManager::WaitForBackgroundVerificationTasksToFinish() {
+ if (verification_thread_pool_ == nullptr) {
+ return;
+ }
+
+ Thread* const self = Thread::Current();
+ verification_thread_pool_->Wait(self, /* do_work= */ true, /* may_hold_locks= */ false);
+}
+
void OatFileManager::WaitForBackgroundVerificationTasks() {
if (verification_thread_pool_ != nullptr) {
Thread* const self = Thread::Current();
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index b73ac58..e09390b 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -134,6 +134,9 @@
// If allocated, delete a thread pool of background verification threads.
void DeleteThreadPool();
+ // Wait for any ongoing background verification tasks to finish.
+ void WaitForBackgroundVerificationTasksToFinish();
+
// Wait for all background verification tasks to finish. This is only used by tests.
void WaitForBackgroundVerificationTasks();
diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc
index 8fbf02a..f0764da 100644
--- a/runtime/oat_quick_method_header.cc
+++ b/runtime/oat_quick_method_header.cc
@@ -57,33 +57,61 @@
uintptr_t OatQuickMethodHeader::ToNativeQuickPc(ArtMethod* method,
const uint32_t dex_pc,
- bool is_for_catch_handler,
bool abort_on_failure) const {
const void* entry_point = GetEntryPoint();
DCHECK(!method->IsNative());
+ // For catch handlers use the ArrayRef<const uint32_t> version of ToNativeQuickPc.
+ DCHECK(!IsNterpMethodHeader());
+ DCHECK(IsOptimized());
+ // Search for the dex-to-pc mapping in stack maps.
+ CodeInfo code_info = CodeInfo::DecodeInlineInfoOnly(this);
+
+ StackMap stack_map = code_info.GetStackMapForDexPc(dex_pc);
+ if (stack_map.IsValid()) {
+ return reinterpret_cast<uintptr_t>(entry_point) + stack_map.GetNativePcOffset(kRuntimeISA);
+ }
+ if (abort_on_failure) {
+ ScopedObjectAccess soa(Thread::Current());
+ LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc << " in "
+ << method->PrettyMethod();
+ }
+ return UINTPTR_MAX;
+}
+
+uintptr_t OatQuickMethodHeader::ToNativeQuickPcForCatchHandlers(
+ ArtMethod* method,
+ ArrayRef<const uint32_t> dex_pc_list,
+ /* out */ uint32_t* stack_map_row,
+ bool abort_on_failure) const {
+ const void* entry_point = GetEntryPoint();
+ DCHECK(!method->IsNative());
if (IsNterpMethodHeader()) {
- // This should only be called on an nterp frame for getting a catch handler.
- CHECK(is_for_catch_handler);
return NterpGetCatchHandler();
}
DCHECK(IsOptimized());
// Search for the dex-to-pc mapping in stack maps.
CodeInfo code_info = CodeInfo::DecodeInlineInfoOnly(this);
- // All stack maps are stored in the same CodeItem section, safepoint stack
- // maps first, then catch stack maps. We use `is_for_catch_handler` to select
- // the order of iteration.
- StackMap stack_map =
- LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc)
- : code_info.GetStackMapForDexPc(dex_pc);
+ StackMap stack_map = code_info.GetCatchStackMapForDexPc(dex_pc_list);
+ *stack_map_row = stack_map.Row();
if (stack_map.IsValid()) {
return reinterpret_cast<uintptr_t>(entry_point) +
stack_map.GetNativePcOffset(kRuntimeISA);
}
if (abort_on_failure) {
+ std::stringstream ss;
+ bool first = true;
+ ss << "Failed to find native offset for dex pcs (from outermost to innermost) " << std::hex;
+ for (auto dex_pc : dex_pc_list) {
+ if (!first) {
+ ss << ", ";
+ }
+ first = false;
+ ss << "0x" << dex_pc;
+ }
ScopedObjectAccess soa(Thread::Current());
- LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc
- << " in " << method->PrettyMethod();
+ ss << " in " << method->PrettyMethod();
+ LOG(FATAL) << ss.str();
}
return UINTPTR_MAX;
}
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index e347588..769d67f 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -28,6 +28,11 @@
class ArtMethod;
+// Size in bytes of the should_deoptimize flag on stack.
+// We just need 4 bytes for our purpose regardless of the architecture. Frame size
+// calculation will automatically do alignment for the final frame size.
+static constexpr size_t kShouldDeoptimizeFlagSize = 4;
+
// OatQuickMethodHeader precedes the raw code chunk generated by the compiler.
class PACKED(4) OatQuickMethodHeader {
public:
@@ -47,8 +52,8 @@
static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) {
uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr);
uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_);
- DCHECK(IsAlignedParam(code, GetInstructionSetAlignment(kRuntimeISA)) ||
- IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA)))
+ DCHECK(IsAlignedParam(code, GetInstructionSetCodeAlignment(kRuntimeISA)) ||
+ IsAlignedParam(header, GetInstructionSetCodeAlignment(kRuntimeISA)))
<< std::hex << code << " " << std::hex << header;
return reinterpret_cast<OatQuickMethodHeader*>(header);
}
@@ -58,7 +63,7 @@
}
static size_t InstructionAlignedSize() {
- return RoundUp(sizeof(OatQuickMethodHeader), GetInstructionSetAlignment(kRuntimeISA));
+ return RoundUp(sizeof(OatQuickMethodHeader), GetInstructionSetCodeAlignment(kRuntimeISA));
}
OatQuickMethodHeader(const OatQuickMethodHeader&) = default;
@@ -145,11 +150,28 @@
return CodeInfo::DecodeFrameInfo(GetOptimizedCodeInfoPtr());
}
+ size_t GetShouldDeoptimizeFlagOffset() const {
+ DCHECK(IsOptimized());
+ QuickMethodFrameInfo frame_info = GetFrameInfo();
+ size_t frame_size = frame_info.FrameSizeInBytes();
+ size_t core_spill_size =
+ POPCOUNT(frame_info.CoreSpillMask()) * GetBytesPerGprSpillLocation(kRuntimeISA);
+ size_t fpu_spill_size =
+ POPCOUNT(frame_info.FpSpillMask()) * GetBytesPerFprSpillLocation(kRuntimeISA);
+ return frame_size - core_spill_size - fpu_spill_size - kShouldDeoptimizeFlagSize;
+ }
+
+ // For non-catch handlers. Only used in test code.
uintptr_t ToNativeQuickPc(ArtMethod* method,
const uint32_t dex_pc,
- bool is_for_catch_handler,
bool abort_on_failure = true) const;
+ // For catch handlers.
+ uintptr_t ToNativeQuickPcForCatchHandlers(ArtMethod* method,
+ ArrayRef<const uint32_t> dex_pc_list,
+ /* out */ uint32_t* stack_map_row,
+ bool abort_on_failure = true) const;
+
uint32_t ToDexPc(ArtMethod** frame,
const uintptr_t pc,
bool abort_on_failure = true) const
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 4d24482..b682e60 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -82,6 +82,7 @@
DCHECK_EQ(hiddenapi_policy_valuemap.size(),
static_cast<size_t>(hiddenapi::EnforcementPolicy::kMax) + 1);
+ // clang-format off
parser_builder->
SetCategory("standard")
.Define({"-classpath _", "-cp _"})
@@ -183,6 +184,10 @@
.IntoKey(M::Image)
.Define("-Xforcejitzygote")
.IntoKey(M::ForceJitZygote)
+ .Define("-Xallowinmemorycompilation")
+ .WithHelp("Allows compiling the boot classpath in memory when the given boot image is"
+ "unusable. This option is set by default for Zygote.")
+ .IntoKey(M::AllowInMemoryCompilation)
.Define("-Xprimaryzygote")
.IntoKey(M::PrimaryZygote)
.Define("-Xbootclasspath-locations:_")
@@ -351,6 +356,12 @@
.IntoKey(M::MethodTraceFileSize)
.Define("-Xmethod-trace-stream")
.IntoKey(M::MethodTraceStreaming)
+ .Define("-Xmethod-trace-clock:_")
+ .WithType<TraceClockSource>()
+ .WithValueMap({{"threadcpuclock", TraceClockSource::kThreadCpu},
+ {"wallclock", TraceClockSource::kWall},
+ {"dualclock", TraceClockSource::kDual}})
+ .IntoKey(M::MethodTraceClock)
.Define("-Xcompiler:_")
.WithType<std::string>()
.IntoKey(M::Compiler)
@@ -464,18 +475,44 @@
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::PerfettoJavaHeapStackProf);
+ // clang-format on
- FlagBase::AddFlagsToCmdlineParser(parser_builder.get());
+ FlagBase::AddFlagsToCmdlineParser(parser_builder.get());
- parser_builder->Ignore({
- "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
- "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
- "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap",
- "-Xverifyopt:_", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:_",
- "-Xincludeselectedmethod",
- "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:_", "-Xjitoffset:_",
- "-Xjitosrthreshold:_", "-Xjitconfig:_", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
- "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"})
+ parser_builder
+ ->Ignore({"-ea",
+ "-da",
+ "-enableassertions",
+ "-disableassertions",
+ "--runtime-arg",
+ "-esa",
+ "-dsa",
+ "-enablesystemassertions",
+ "-disablesystemassertions",
+ "-Xrs",
+ "-Xint:_",
+ "-Xdexopt:_",
+ "-Xnoquithandler",
+ "-Xjnigreflimit:_",
+ "-Xgenregmap",
+ "-Xnogenregmap",
+ "-Xverifyopt:_",
+ "-Xcheckdexsum",
+ "-Xincludeselectedop",
+ "-Xjitop:_",
+ "-Xincludeselectedmethod",
+ "-Xjitblocking",
+ "-Xjitmethod:_",
+ "-Xjitclass:_",
+ "-Xjitoffset:_",
+ "-Xjitosrthreshold:_",
+ "-Xjitconfig:_",
+ "-Xjitcheckcg",
+ "-Xjitverbose",
+ "-Xjitprofile",
+ "-Xjitdisableopt",
+ "-Xjitsuspendpoll",
+ "-XX:mainThreadStackSize=_"})
.IgnoreUnrecognized(ignore_unrecognized)
.OrderCategories({"standard", "extended", "Dalvik", "ART"});
@@ -732,9 +769,11 @@
Exit(0);
}
// If `boot.art` exists in the ART APEX, it will be used. Otherwise, Everything will be JITed.
- args.Set(M::Image,
- ParseStringList<':'>{{"boot.art!/apex/com.android.art/etc/boot-image.prof",
- "/nonx/boot-framework.art!/system/etc/boot-image.prof"}});
+ args.Set(M::Image, ParseStringList<':'>::Split(GetJitZygoteBootImageLocation()));
+ }
+
+ if (args.Exists(M::Zygote)) {
+ args.Set(M::AllowInMemoryCompilation, Unit());
}
if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index d055186..ac8ec56 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -30,15 +30,8 @@
class ProxyTest : public CommonRuntimeTest {
protected:
- void SetUp() override {
- CommonRuntimeTest::SetUp();
- // The creation of a Proxy class uses WellKnownClasses. These are not normally initialized by
- // CommonRuntimeTest so we need to do that now.
- WellKnownClasses::Clear();
- WellKnownClasses::Init(art::Thread::Current()->GetJniEnv());
- // Since we aren't actually calling any of the native functions we can just immediately call
- // LateInit after calling Init.
- WellKnownClasses::LateInit(art::Thread::Current()->GetJniEnv());
+ ProxyTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
}
};
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 2a6929a..fd57b4a 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -15,10 +15,14 @@
*/
#include "quick_exception_handler.h"
+
#include <ios>
+#include <queue>
+#include <sstream>
#include "arch/context.h"
#include "art_method-inl.h"
+#include "base/array_ref.h"
#include "base/enums.h"
#include "base/globals.h"
#include "base/logging.h" // For VLOG_IS_ON.
@@ -49,13 +53,10 @@
: self_(self),
context_(self->GetLongJumpContext()),
is_deoptimization_(is_deoptimization),
- method_tracing_active_(is_deoptimization ||
- Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()),
handler_quick_frame_(nullptr),
handler_quick_frame_pc_(0),
handler_method_header_(nullptr),
handler_quick_arg0_(0),
- handler_dex_pc_(0),
clear_exception_(false),
handler_frame_depth_(kInvalidFrameDepth),
full_fragment_done_(false) {}
@@ -67,12 +68,15 @@
Context* context,
Handle<mirror::Throwable>* exception,
QuickExceptionHandler* exception_handler,
- uint32_t skip_frames)
+ uint32_t skip_frames,
+ bool skip_top_unwind_callback)
REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
exception_(exception),
exception_handler_(exception_handler),
- skip_frames_(skip_frames) {
+ skip_frames_(skip_frames),
+ skip_unwind_callback_(skip_top_unwind_callback) {
+ DCHECK_IMPLIES(skip_unwind_callback_, skip_frames_ == 0);
}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -95,7 +99,24 @@
DCHECK(method->IsCalleeSaveMethod());
return true;
}
- return HandleTryItems(method);
+ bool continue_stack_walk = HandleTryItems(method);
+ // Collect methods for which MethodUnwind callback needs to be invoked. MethodUnwind callback
+ // can potentially throw, so we want to call these after we find the catch block.
+ // We stop the stack walk when we find the catch block. If we are ending the stack walk we don't
+ // have to unwind this method so don't record it.
+ if (continue_stack_walk && !skip_unwind_callback_) {
+ // Skip unwind callback is only used when method exit callback has thrown an exception. In
+ // that case, we should have runtime method (artMethodExitHook) on top of stack and the
+ // second should be the method for which method exit was called.
+ DCHECK_IMPLIES(skip_unwind_callback_, GetFrameDepth() == 2);
+ unwound_methods_.push(method);
+ }
+ skip_unwind_callback_ = false;
+ return continue_stack_walk;
+ }
+
+ std::queue<ArtMethod*>& GetUnwoundMethods() {
+ return unwound_methods_;
}
private:
@@ -112,10 +133,12 @@
uint32_t found_dex_pc = method->FindCatchBlock(to_find, dex_pc, &clear_exception);
exception_handler_->SetClearException(clear_exception);
if (found_dex_pc != dex::kDexNoIndex) {
- exception_handler_->SetHandlerDexPc(found_dex_pc);
+ exception_handler_->SetHandlerDexPcList(ComputeDexPcList(found_dex_pc));
+ uint32_t stack_map_row = -1;
exception_handler_->SetHandlerQuickFramePc(
- GetCurrentOatQuickMethodHeader()->ToNativeQuickPc(
- method, found_dex_pc, /* is_for_catch_handler= */ true));
+ GetCurrentOatQuickMethodHeader()->ToNativeQuickPcForCatchHandlers(
+ method, exception_handler_->GetHandlerDexPcList(), &stack_map_row));
+ exception_handler_->SetCatchStackMapRow(stack_map_row);
exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
exception_handler_->SetHandlerMethodHeader(GetCurrentOatQuickMethodHeader());
return false; // End stack walk.
@@ -139,20 +162,29 @@
QuickExceptionHandler* const exception_handler_;
// The number of frames to skip searching for catches in.
uint32_t skip_frames_;
+ // The list of methods we would skip to reach the catch block. We record these to call
+ // MethodUnwind callbacks.
+ std::queue<ArtMethod*> unwound_methods_;
+ // Specifies if the unwind callback should be ignored for method at the top of the stack.
+ bool skip_unwind_callback_;
DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
};
// Finds the appropriate exception catch after calling all method exit instrumentation functions.
-// Note that this might change the exception being thrown.
-void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) {
+// Note that this might change the exception being thrown. If is_method_exit_exception is true
+// skip the method unwind call for the method on top of the stack as the exception was thrown by
+// method exit callback.
+void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception,
+ bool is_method_exit_exception) {
DCHECK(!is_deoptimization_);
- instrumentation::InstrumentationStackPopper popper(self_);
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
// The number of total frames we have so far popped.
uint32_t already_popped = 0;
bool popped_to_top = true;
StackHandleScope<1> hs(self_);
MutableHandle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
+ bool skip_top_unwind_callback = is_method_exit_exception;
// Sending the instrumentation events (done by the InstrumentationStackPopper) can cause new
// exceptions to be thrown which will override the current exception. Therefore we need to perform
// the search for a catch in a loop until we have successfully popped all the way to a catch or
@@ -166,11 +198,15 @@
}
// Walk the stack to find catch handler.
- CatchBlockStackVisitor visitor(self_, context_,
+ CatchBlockStackVisitor visitor(self_,
+ context_,
&exception_ref,
this,
- /*skip_frames=*/already_popped);
+ /*skip_frames=*/already_popped,
+ skip_top_unwind_callback);
visitor.WalkStack(true);
+ skip_top_unwind_callback = false;
+
uint32_t new_pop_count = handler_frame_depth_;
DCHECK_GE(new_pop_count, already_popped);
already_popped = new_pop_count;
@@ -181,10 +217,25 @@
}
if (GetHandlerMethod() != nullptr) {
const DexFile* dex_file = GetHandlerMethod()->GetDexFile();
- int line_number =
- annotations::GetLineNumFromPC(dex_file, GetHandlerMethod(), handler_dex_pc_);
+ DCHECK(handler_dex_pc_list_.has_value());
+ DCHECK_GE(handler_dex_pc_list_->size(), 1u);
+ int line_number = annotations::GetLineNumFromPC(
+ dex_file, GetHandlerMethod(), handler_dex_pc_list_->front());
+
+ // We may have an inlined method. If so, we can add some extra logging.
+ std::stringstream ss;
+ ArtMethod* maybe_inlined_method = visitor.GetMethod();
+ if (maybe_inlined_method != GetHandlerMethod()) {
+ const DexFile* inlined_dex_file = maybe_inlined_method->GetDexFile();
+ DCHECK_GE(handler_dex_pc_list_->size(), 2u);
+ int inlined_line_number = annotations::GetLineNumFromPC(
+ inlined_dex_file, maybe_inlined_method, handler_dex_pc_list_->back());
+ ss << " which ends up calling inlined method " << maybe_inlined_method->PrettyMethod()
+ << " (line: " << inlined_line_number << ")";
+ }
+
LOG(INFO) << "Handler: " << GetHandlerMethod()->PrettyMethod() << " (line: "
- << line_number << ")";
+ << line_number << ")" << ss.str();
}
}
// Exception was cleared as part of delivery.
@@ -195,9 +246,11 @@
handler_method_header_->IsOptimized()) {
SetCatchEnvironmentForOptimizedHandler(&visitor);
}
- popped_to_top =
- popper.PopFramesTo(reinterpret_cast<uintptr_t>(handler_quick_frame_), exception_ref);
+ popped_to_top = instr->ProcessMethodUnwindCallbacks(self_,
+ visitor.GetUnwoundMethods(),
+ exception_ref);
} while (!popped_to_top);
+
if (!clear_exception_) {
// Put exception back in root set with clear throw location.
self_->SetException(exception_ref.Get());
@@ -245,15 +298,18 @@
self_->DumpStack(LOG_STREAM(INFO) << "Setting catch phis: ");
}
- CodeItemDataAccessor accessor(GetHandlerMethod()->DexInstructionData());
- const size_t number_of_vregs = accessor.RegistersSize();
CodeInfo code_info(handler_method_header_);
// Find stack map of the catch block.
- StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc());
+ ArrayRef<const uint32_t> dex_pc_list = GetHandlerDexPcList();
+ DCHECK_GE(dex_pc_list.size(), 1u);
+ StackMap catch_stack_map = code_info.GetStackMapAt(GetCatchStackMapRow());
DCHECK(catch_stack_map.IsValid());
- DexRegisterMap catch_vreg_map = code_info.GetDexRegisterMapOf(catch_stack_map);
- DCHECK_EQ(catch_vreg_map.size(), number_of_vregs);
+ DCHECK_EQ(catch_stack_map.Row(), code_info.GetCatchStackMapForDexPc(dex_pc_list).Row());
+ const uint32_t catch_depth = dex_pc_list.size() - 1;
+ const size_t number_of_registers = stack_visitor->GetNumberOfRegisters(&code_info, catch_depth);
+ DexRegisterMap catch_vreg_map =
+ code_info.GetDexRegisterMapOf(catch_stack_map, /* first= */ 0, number_of_registers);
if (!catch_vreg_map.HasAnyLiveDexRegisters()) {
return;
@@ -263,26 +319,47 @@
StackMap throw_stack_map =
code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset());
DCHECK(throw_stack_map.IsValid());
- DexRegisterMap throw_vreg_map = code_info.GetDexRegisterMapOf(throw_stack_map);
- DCHECK_EQ(throw_vreg_map.size(), number_of_vregs);
+ const uint32_t throw_depth = stack_visitor->InlineDepth();
+ DCHECK_EQ(throw_depth, catch_depth);
+ DexRegisterMap throw_vreg_map =
+ code_info.GetDexRegisterMapOf(throw_stack_map, /* first= */ 0, number_of_registers);
+ DCHECK_EQ(throw_vreg_map.size(), catch_vreg_map.size());
- // Copy values between them.
- for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
- DexRegisterLocation::Kind catch_location = catch_vreg_map[vreg].GetKind();
- if (catch_location == DexRegisterLocation::Kind::kNone) {
+ // First vreg that it is part of the catch's environment.
+ const size_t catch_vreg_start = catch_depth == 0
+ ? 0
+ : stack_visitor->GetNumberOfRegisters(&code_info, catch_depth - 1);
+
+ // We don't need to copy anything in the parent's environment.
+ for (size_t vreg = 0; vreg < catch_vreg_start; ++vreg) {
+ DexRegisterLocation::Kind catch_location_kind = catch_vreg_map[vreg].GetKind();
+ DCHECK(catch_location_kind == DexRegisterLocation::Kind::kNone ||
+ catch_location_kind == DexRegisterLocation::Kind::kConstant ||
+ catch_location_kind == DexRegisterLocation::Kind::kInStack)
+ << "Unexpected catch_location_kind: " << catch_location_kind;
+ }
+
+ // Copy values between the throw and the catch.
+ for (size_t vreg = catch_vreg_start; vreg < catch_vreg_map.size(); ++vreg) {
+ DexRegisterLocation::Kind catch_location_kind = catch_vreg_map[vreg].GetKind();
+ if (catch_location_kind == DexRegisterLocation::Kind::kNone) {
continue;
}
- DCHECK(catch_location == DexRegisterLocation::Kind::kInStack);
- // Get vreg value from its current location.
+ // Consistency checks.
+ DCHECK_EQ(catch_location_kind, DexRegisterLocation::Kind::kInStack);
uint32_t vreg_value;
VRegKind vreg_kind = ToVRegKind(throw_vreg_map[vreg].GetKind());
- bool get_vreg_success =
- stack_visitor->GetVReg(stack_visitor->GetMethod(),
- vreg,
- vreg_kind,
- &vreg_value,
- throw_vreg_map[vreg]);
+ DCHECK_NE(vreg_kind, kReferenceVReg)
+ << "The fast path in GetVReg doesn't expect a kReferenceVReg.";
+
+ // Get vreg value from its current location.
+ bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(),
+ vreg,
+ vreg_kind,
+ &vreg_value,
+ throw_vreg_map[vreg],
+ /* need_full_register_list= */ true);
CHECK(get_vreg_success) << "VReg " << vreg << " was optimized out ("
<< "method=" << ArtMethod::PrettyMethod(stack_visitor->GetMethod())
<< ", dex_pc=" << stack_visitor->GetDexPc() << ", "
@@ -303,8 +380,8 @@
DeoptimizeStackVisitor(Thread* self,
Context* context,
QuickExceptionHandler* exception_handler,
- bool single_frame)
- REQUIRES_SHARED(Locks::mutator_lock_)
+ bool single_frame,
+ bool skip_method_exit_callbacks) REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
exception_handler_(exception_handler),
prev_shadow_frame_(nullptr),
@@ -313,8 +390,8 @@
single_frame_done_(false),
single_frame_deopt_method_(nullptr),
single_frame_deopt_quick_method_header_(nullptr),
- callee_method_(nullptr) {
- }
+ callee_method_(nullptr),
+ skip_method_exit_callbacks_(skip_method_exit_callbacks) {}
ArtMethod* GetSingleFrameDeoptMethod() const {
return single_frame_deopt_method_;
@@ -361,13 +438,15 @@
return true;
} else if (method->IsNative()) {
// If we return from JNI with a pending exception and want to deoptimize, we need to skip
- // the native method.
- // The top method is a runtime method, the native method comes next.
- CHECK_EQ(GetFrameDepth(), 1U);
+ // the native method. The top method is a runtime method, the native method comes next.
+ // We also deoptimize due to method instrumentation reasons from method entry / exit
+ // callbacks. In these cases native method is at the top of stack.
+ CHECK((GetFrameDepth() == 1U) || (GetFrameDepth() == 0U));
callee_method_ = method;
return true;
} else if (!single_frame_deopt_ &&
- !Runtime::Current()->IsAsyncDeoptimizeable(GetCurrentQuickFramePc())) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(GetOuterMethod(),
+ GetCurrentQuickFramePc())) {
// We hit some code that's not deoptimizeable. However, Single-frame deoptimization triggered
// from compiled code is always allowed since HDeoptimize always saves the full environment.
LOG(WARNING) << "Got request to deoptimize un-deoptimizable method "
@@ -382,7 +461,7 @@
CodeItemDataAccessor accessor(method->DexInstructionData());
const size_t num_regs = accessor.RegistersSize();
if (new_frame == nullptr) {
- new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, nullptr, method, GetDexPc());
+ new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, method, GetDexPc());
updated_vregs = nullptr;
} else {
updated_vregs = GetThread()->GetUpdatedVRegFlags(frame_id);
@@ -393,6 +472,25 @@
} else {
HandleOptimizingDeoptimization(method, new_frame, updated_vregs);
}
+ // Update if method exit event needs to be reported. We should report exit event only if we
+ // have reported an entry event. So tell interpreter if/ an entry event was reported.
+ bool supports_exit_events =
+ Runtime::Current()->GetInstrumentation()->MethodSupportsExitEvents(
+ method, GetCurrentOatQuickMethodHeader());
+ new_frame->SetSkipMethodExitEvents(!supports_exit_events);
+ // If we are deoptimizing after method exit callback we shouldn't call the method exit
+ // callbacks again for the top frame. We may have to deopt after the callback if the callback
+ // either throws or performs other actions that require a deopt.
+ // We only need to skip for the top frame and the rest of the frames should still run the
+ // callbacks. So only do this check for the top frame.
+ if (GetFrameDepth() == 0U && skip_method_exit_callbacks_) {
+ new_frame->SetSkipMethodExitEvents(true);
+ // This exception was raised by method exit callbacks and we shouldn't report it to
+ // listeners for these exceptions.
+ if (GetThread()->IsExceptionPending()) {
+ new_frame->SetSkipNextExceptionEvent(true);
+ }
+ }
if (updated_vregs != nullptr) {
// Calling Thread::RemoveDebuggerShadowFrameMapping will also delete the updated_vregs
// array so this must come after we processed the frame.
@@ -549,6 +647,10 @@
ArtMethod* single_frame_deopt_method_;
const OatQuickMethodHeader* single_frame_deopt_quick_method_header_;
ArtMethod* callee_method_;
+ // This specifies if method exit callbacks should be skipped for the top frame. We may request
+ // a deopt after running method exit callbacks if the callback throws or requests events that
+ // need a deopt.
+ bool skip_method_exit_callbacks_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor);
};
@@ -568,13 +670,13 @@
}
}
-void QuickExceptionHandler::DeoptimizeStack() {
+void QuickExceptionHandler::DeoptimizeStack(bool skip_method_exit_callbacks) {
DCHECK(is_deoptimization_);
if (kDebugExceptionDelivery) {
self_->DumpStack(LOG_STREAM(INFO) << "Deoptimizing: ");
}
- DeoptimizeStackVisitor visitor(self_, context_, this, false);
+ DeoptimizeStackVisitor visitor(self_, context_, this, false, skip_method_exit_callbacks);
visitor.WalkStack(true);
PrepareForLongJumpToInvokeStubOrInterpreterBridge();
}
@@ -582,7 +684,10 @@
void QuickExceptionHandler::DeoptimizeSingleFrame(DeoptimizationKind kind) {
DCHECK(is_deoptimization_);
- DeoptimizeStackVisitor visitor(self_, context_, this, true);
+ // This deopt is requested while still executing the method. We haven't run method exit callbacks
+ // yet, so don't skip them.
+ DeoptimizeStackVisitor visitor(
+ self_, context_, this, true, /* skip_method_exit_callbacks= */ false);
visitor.WalkStack(true);
// Compiled code made an explicit deoptimization.
@@ -613,21 +718,8 @@
PrepareForLongJumpToInvokeStubOrInterpreterBridge();
}
-void QuickExceptionHandler::DeoptimizePartialFragmentFixup(uintptr_t return_pc) {
- // At this point, the instrumentation stack has been updated. We need to install
- // the real return pc on stack, in case instrumentation stub is stored there,
- // so that the interpreter bridge code can return to the right place. JITed
- // frames in Java debuggable runtimes may not have an instrumentation stub, so
- // update the PC only when required.
- uintptr_t* pc_addr = reinterpret_cast<uintptr_t*>(handler_quick_frame_);
- CHECK(pc_addr != nullptr);
- pc_addr--;
- if (return_pc != 0 &&
- (*reinterpret_cast<uintptr_t*>(pc_addr)) ==
- reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc())) {
- *reinterpret_cast<uintptr_t*>(pc_addr) = return_pc;
- }
-
+void QuickExceptionHandler::DeoptimizePartialFragmentFixup() {
+ CHECK(handler_quick_frame_ != nullptr);
// Architecture-dependent work. This is to get the LR right for x86 and x86-64.
if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) {
// On x86, the return address is on the stack, so just reuse it. Otherwise we would have to
@@ -637,17 +729,6 @@
}
}
-uintptr_t QuickExceptionHandler::UpdateInstrumentationStack() {
- DCHECK(is_deoptimization_) << "Non-deoptimization handlers should use FindCatch";
- uintptr_t return_pc = 0;
- if (method_tracing_active_) {
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- return_pc = instrumentation->PopFramesForDeoptimization(
- self_, reinterpret_cast<uintptr_t>(handler_quick_frame_));
- }
- return return_pc;
-}
-
void QuickExceptionHandler::DoLongJump(bool smash_caller_saves) {
// Place context back on thread so it will be available when we continue.
self_->ReleaseLongJumpContext(context_);
@@ -661,9 +742,14 @@
if (!is_deoptimization_ &&
handler_method_header_ != nullptr &&
handler_method_header_->IsNterpMethodHeader()) {
+ // Interpreter procceses one method at a time i.e. not inlining
+ DCHECK(handler_dex_pc_list_.has_value());
+ DCHECK_EQ(handler_dex_pc_list_->size(), 1u) << "We shouldn't have any inlined frames.";
context_->SetNterpDexPC(reinterpret_cast<uintptr_t>(
- GetHandlerMethod()->DexInstructions().Insns() + handler_dex_pc_));
+ GetHandlerMethod()->DexInstructions().Insns() + handler_dex_pc_list_->front()));
}
+ // Clear the dex_pc list so as not to leak memory.
+ handler_dex_pc_list_.reset();
context_->DoLongJump();
UNREACHABLE();
}
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 4ff981d..f3ce624 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -18,10 +18,14 @@
#define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
#include <android-base/logging.h>
+#include <cstdint>
+#include <optional>
+#include "base/array_ref.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "deoptimization_kind.h"
+#include "stack_map.h"
#include "stack_reference.h"
namespace art {
@@ -49,12 +53,16 @@
// Find the catch handler for the given exception and call all required Instrumentation methods.
// Note this might result in the exception being caught being different from 'exception'.
- void FindCatch(ObjPtr<mirror::Throwable> exception) REQUIRES_SHARED(Locks::mutator_lock_);
+ void FindCatch(ObjPtr<mirror::Throwable> exception, bool is_method_exit_exception)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Deoptimize the stack to the upcall/some code that's not deoptimizeable. For
// every compiled frame, we create a "copy" shadow frame that will be executed
// with the interpreter.
- void DeoptimizeStack() REQUIRES_SHARED(Locks::mutator_lock_);
+ // skip_method_exit_callbacks specifies if we should skip method exit callbacks for the top frame.
+ // It is set if a deopt is needed after calling method exit callback for ex: if the callback
+ // throws or performs other actions that require a deopt.
+ void DeoptimizeStack(bool skip_method_exit_callbacks) REQUIRES_SHARED(Locks::mutator_lock_);
// Deoptimize a single frame. It's directly triggered from compiled code. It
// has the following properties:
@@ -67,13 +75,7 @@
// on whether that single frame covers full or partial fragment.
void DeoptimizeSingleFrame(DeoptimizationKind kind) REQUIRES_SHARED(Locks::mutator_lock_);
- void DeoptimizePartialFragmentFixup(uintptr_t return_pc)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Update the instrumentation stack by removing all methods that will be unwound
- // by the exception being thrown.
- // Return the return pc of the last frame that's unwound.
- uintptr_t UpdateInstrumentationStack() REQUIRES_SHARED(Locks::mutator_lock_);
+ void DeoptimizePartialFragmentFixup() REQUIRES_SHARED(Locks::mutator_lock_);
// Set up environment before delivering an exception to optimized code.
void SetCatchEnvironmentForOptimizedHandler(StackVisitor* stack_visitor)
@@ -102,12 +104,21 @@
return *handler_quick_frame_;
}
- uint32_t GetHandlerDexPc() const {
- return handler_dex_pc_;
+ ArrayRef<const uint32_t> GetHandlerDexPcList() const {
+ DCHECK(handler_dex_pc_list_.has_value());
+ return ArrayRef<const uint32_t>(handler_dex_pc_list_.value());
}
- void SetHandlerDexPc(uint32_t dex_pc) {
- handler_dex_pc_ = dex_pc;
+ void SetHandlerDexPcList(std::vector<uint32_t>&& handler_dex_pc_list) {
+ handler_dex_pc_list_ = handler_dex_pc_list;
+ }
+
+ uint32_t GetCatchStackMapRow() const {
+ return catch_stack_map_row_;
+ }
+
+ void SetCatchStackMapRow(uint32_t stack_map_row) {
+ catch_stack_map_row_ = stack_map_row;
}
bool GetClearException() const {
@@ -140,8 +151,6 @@
Context* const context_;
// Should we deoptimize the stack?
const bool is_deoptimization_;
- // Is method tracing active?
- const bool method_tracing_active_;
// Quick frame with found handler or last frame if no handler found.
ArtMethod** handler_quick_frame_;
// PC to branch to for the handler.
@@ -150,8 +159,12 @@
const OatQuickMethodHeader* handler_method_header_;
// The value for argument 0.
uintptr_t handler_quick_arg0_;
- // The handler's dex PC, zero implies an uncaught exception.
- uint32_t handler_dex_pc_;
+ // The handler's dex PC list including the inline dex_pcs. The dex_pcs are ordered from outermost
+ // to innermost. An empty list implies an uncaught exception.
+ // Marked as optional so that we can make sure we destroy it before doing a long jump.
+ std::optional<std::vector<uint32_t>> handler_dex_pc_list_;
+ // StackMap row corresponding to the found catch.
+ uint32_t catch_stack_map_row_;
// Should the exception be cleared as the catch block has no move-exception?
bool clear_exception_;
// Frame depth of the catch handler or the upcall.
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index ff4693f..36c6f44 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -38,9 +38,8 @@
if (gUseReadBarrier && with_read_barrier) {
if (kCheckDebugDisallowReadBarrierCount) {
Thread* const self = Thread::Current();
- if (self != nullptr) {
- CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
- }
+ CHECK(self != nullptr);
+ CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
}
if (kUseBakerReadBarrier) {
// fake_address_dependency (must be zero) is used to create artificial data dependency from
@@ -112,9 +111,8 @@
if (gUseReadBarrier && with_read_barrier) {
if (kCheckDebugDisallowReadBarrierCount) {
Thread* const self = Thread::Current();
- if (self != nullptr) {
- CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
- }
+ CHECK(self != nullptr);
+ CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
}
if (kUseBakerReadBarrier) {
// TODO: separate the read barrier code from the collector code more.
@@ -157,9 +155,8 @@
if (gUseReadBarrier && with_read_barrier) {
if (kCheckDebugDisallowReadBarrierCount) {
Thread* const self = Thread::Current();
- if (self != nullptr) {
- CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
- }
+ CHECK(self != nullptr);
+ CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
}
if (kUseBakerReadBarrier) {
// TODO: separate the read barrier code from the collector code more.
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 2ffd866..b204533 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -28,6 +28,9 @@
#include "obj_ptr.h"
namespace art {
+namespace jni {
+class LocalReferenceTable;
+} // namespace jni
namespace mirror {
class Object;
} // namespace mirror
@@ -61,6 +64,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::alloc_tracker_lock_);
friend class IndirectReferenceTable; // For Dump.
+ friend class jni::LocalReferenceTable; // For Dump.
std::string name_;
Table entries_;
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index 7a1e668..420543e 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -22,6 +22,7 @@
#include "art_method-inl.h"
#include "class_linker.h"
+#include "class_root-inl.h"
#include "common_runtime_test.h"
#include "dex/primitive.h"
#include "handle_scope-inl.h"
@@ -34,13 +35,17 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-#include "well_known_classes.h"
namespace art {
using android::base::StringPrintf;
-class ReferenceTableTest : public CommonRuntimeTest {};
+class ReferenceTableTest : public CommonRuntimeTest {
+ protected:
+ ReferenceTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
static ObjPtr<mirror::Object> CreateWeakReference(ObjPtr<mirror::Object> referent)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -194,18 +199,12 @@
// avoids having to create the low-level args array ourselves.
Handle<mirror::Object> h_with_trace;
{
- jmethodID substr = soa.Env()->GetMethodID(WellKnownClasses::java_lang_String,
- "substring",
- "(II)Ljava/lang/String;");
+ ArtMethod* substr = GetClassRoot<mirror::String>()->FindClassMethod(
+ "substring", "(II)Ljava/lang/String;", kRuntimePointerSize);
ASSERT_TRUE(substr != nullptr);
- jobject jobj = soa.Env()->AddLocalReference<jobject>(h_without_trace.Get());
- ASSERT_TRUE(jobj != nullptr);
- jobject result = soa.Env()->CallObjectMethod(jobj,
- substr,
- static_cast<jint>(0),
- static_cast<jint>(4));
- ASSERT_TRUE(result != nullptr);
- h_with_trace = hs.NewHandle(soa.Self()->DecodeJObject(result));
+ h_with_trace = hs.NewHandle(
+ substr->InvokeFinal<'L', 'I', 'I'>(soa.Self(), h_without_trace.Get(), 0, 4));
+ ASSERT_TRUE(h_with_trace != nullptr);
}
Handle<mirror::Object> h_ref;
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index a7290a2..bfe9c8f 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -17,7 +17,7 @@
#include "reflection-inl.h"
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/enums.h"
#include "class_linker.h"
#include "common_throws.h"
@@ -502,17 +502,17 @@
<< soa.Self()->GetException()->GetClass()->PrettyDescriptor();
} else {
// If we get another exception when we are trying to wrap, then just use that instead.
- ScopedLocalRef<jthrowable> th(soa.Env(), soa.Env()->ExceptionOccurred());
+ StackHandleScope<2u> hs(soa.Self());
+ Handle<mirror::Throwable> cause = hs.NewHandle(soa.Self()->GetException());
soa.Self()->ClearException();
- jobject exception_instance =
- soa.Env()->NewObject(WellKnownClasses::java_lang_reflect_InvocationTargetException,
- WellKnownClasses::java_lang_reflect_InvocationTargetException_init,
- th.get());
+ Handle<mirror::Object> exception_instance =
+ WellKnownClasses::java_lang_reflect_InvocationTargetException_init->NewObject<'L'>(
+ hs, soa.Self(), cause);
if (exception_instance == nullptr) {
soa.Self()->AssertPendingException();
return false;
}
- soa.Env()->Throw(reinterpret_cast<jthrowable>(exception_instance));
+ soa.Self()->SetException(exception_instance->AsThrowable());
}
return false;
}
@@ -523,6 +523,7 @@
} // anonymous namespace
template <>
+NO_STACK_PROTECTOR
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj,
ArtMethod* method,
@@ -534,7 +535,7 @@
ThrowStackOverflowError(soa.Self());
return JValue();
}
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -555,6 +556,7 @@
}
template <>
+NO_STACK_PROTECTOR
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj,
jmethodID mid,
@@ -575,7 +577,7 @@
ThrowStackOverflowError(soa.Self());
return JValue();
}
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -618,7 +620,7 @@
}
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, interface_method);
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -662,7 +664,7 @@
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, interface_method);
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -838,7 +840,7 @@
return nullptr;
}
- jmethodID m = nullptr;
+ ArtMethod* m = nullptr;
const char* shorty;
switch (src_class) {
case Primitive::kPrimBoolean:
@@ -889,11 +891,8 @@
arg_array.Append(value.GetI());
}
- jni::DecodeArtMethod(m)->Invoke(soa.Self(),
- arg_array.GetArray(),
- arg_array.GetNumBytes(),
- &result,
- shorty);
+ DCHECK(m->GetDeclaringClass()->IsInitialized()); // By `ClassLinker::RunRootClinits()`.
+ m->Invoke(soa.Self(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, shorty);
return result.GetL();
}
@@ -1067,8 +1066,8 @@
IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref);
if (kind == kLocal) {
self->GetJniEnv()->UpdateLocal(obj, result);
- } else if (kind == kJniTransitionOrInvalid) {
- LOG(FATAL) << "Unsupported UpdateReference for kind kJniTransitionOrInvalid";
+ } else if (kind == kJniTransition) {
+ LOG(FATAL) << "Unsupported UpdateReference for kind kJniTransition";
} else if (kind == kGlobal) {
self->GetJniEnv()->GetVm()->UpdateGlobal(self, ref, result);
} else {
diff --git a/runtime/reflection.h b/runtime/reflection.h
index b0e27da..13dc8e1 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -99,6 +99,7 @@
// num_frames is number of frames we look up for access check.
template<PointerSize pointer_size>
+NO_STACK_PROTECTOR
jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa,
jobject method,
jobject receiver,
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index 8a76855..4a24d2a 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -21,7 +21,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
-#include "common_compiler_test.h"
+#include "common_runtime_test.h"
#include "dex/descriptors_names.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_internal.h"
@@ -31,11 +31,10 @@
namespace art {
-// TODO: Convert to CommonRuntimeTest. Currently CompileDirectMethod is used in one test.
-class ReflectionTest : public CommonCompilerTest {
+class ReflectionTest : public CommonRuntimeTest {
protected:
void SetUp() override {
- CommonCompilerTest::SetUp();
+ CommonRuntimeTest::SetUp();
vm_ = Runtime::Current()->GetJavaVM();
@@ -76,7 +75,7 @@
void TearDown() override {
CleanUpJniEnv();
- CommonCompilerTest::TearDown();
+ CommonRuntimeTest::TearDown();
}
jclass GetPrimitiveClass(char descriptor) {
@@ -511,33 +510,6 @@
jclass sioobe_;
};
-TEST_F(ReflectionTest, StaticMainMethod) {
- ScopedObjectAccess soa(Thread::Current());
- jobject jclass_loader = LoadDex("Main");
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
- CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
-
- ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
- ASSERT_TRUE(klass != nullptr);
-
- ArtMethod* method = klass->FindClassMethod("main",
- "([Ljava/lang/String;)V",
- kRuntimePointerSize);
- ASSERT_TRUE(method != nullptr);
- ASSERT_TRUE(method->IsStatic());
-
- // Start runtime.
- bool started = runtime_->Start();
- CHECK(started);
- soa.Self()->TransitionFromSuspendedToRunnable();
-
- jvalue args[1];
- args[0].l = nullptr;
- InvokeWithJValues(soa, nullptr, jni::EncodeArtMethod(method), args);
-}
-
TEST_F(ReflectionTest, StaticNopMethod) {
InvokeNopMethod(true);
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 73a337a..2aed680 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -16,6 +16,8 @@
#include "runtime.h"
+#include <utility>
+
#ifdef __linux__
#include <sys/prctl.h>
#endif
@@ -124,6 +126,7 @@
#include "native/dalvik_system_ZygoteHooks.h"
#include "native/java_lang_Class.h"
#include "native/java_lang_Object.h"
+#include "native/java_lang_StackStreamFactory.h"
#include "native/java_lang_String.h"
#include "native/java_lang_StringFactory.h"
#include "native/java_lang_System.h"
@@ -151,6 +154,7 @@
#include "native_bridge_art_interface.h"
#include "native_stack_dump.h"
#include "nativehelper/scoped_local_ref.h"
+#include "nterp_helpers.h"
#include "oat.h"
#include "oat_file_manager.h"
#include "oat_quick_method_header.h"
@@ -161,6 +165,7 @@
#include "reflection.h"
#include "runtime_callbacks.h"
#include "runtime_common.h"
+#include "runtime_image.h"
#include "runtime_intrinsics.h"
#include "runtime_options.h"
#include "scoped_thread_state_change-inl.h"
@@ -174,7 +179,7 @@
#include "transaction.h"
#include "vdex_file.h"
#include "verifier/class_verifier.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
#ifdef ART_TARGET_ANDROID
#include <android/api-level.h>
@@ -207,6 +212,7 @@
Trace::TraceOutputMode trace_output_mode;
std::string trace_file;
size_t trace_file_size;
+ TraceClockSource clock_source;
};
namespace {
@@ -285,7 +291,7 @@
is_native_debuggable_(false),
async_exceptions_thrown_(false),
non_standard_exits_enabled_(false),
- is_java_debuggable_(false),
+ runtime_debug_state_(RuntimeDebugState::kNonJavaDebuggable),
monitor_timeout_enable_(false),
monitor_timeout_ns_(0),
zygote_max_failed_boots_(0),
@@ -308,7 +314,8 @@
verifier_logging_threshold_ms_(100),
verifier_missing_kthrow_fatal_(false),
perfetto_hprof_enabled_(false),
- perfetto_javaheapprof_enabled_(false) {
+ perfetto_javaheapprof_enabled_(false),
+ out_of_memory_error_hook_(nullptr) {
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
CheckConstants();
@@ -429,8 +436,8 @@
if (IsFinishedStarting()) {
ScopedTrace trace2("Waiting for Daemons");
self->ClearException();
- self->GetJniEnv()->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
- WellKnownClasses::java_lang_Daemons_stop);
+ ScopedObjectAccess soa(self);
+ WellKnownClasses::java_lang_Daemons_stop->InvokeStatic<'V'>(self);
}
// Shutdown any trace running.
@@ -443,11 +450,8 @@
callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kDeath);
}
- if (attach_shutdown_thread) {
- DetachCurrentThread(/* should_run_callbacks= */ false);
- self = nullptr;
- }
-
+ // Delete thread pools before detaching the current thread in case tasks
+ // getting deleted need to have access to Thread::Current.
heap_->DeleteThreadPool();
if (oat_file_manager_ != nullptr) {
oat_file_manager_->DeleteThreadPool();
@@ -455,6 +459,11 @@
DeleteThreadPool();
CHECK(thread_pool_ == nullptr);
+ if (attach_shutdown_thread) {
+ DetachCurrentThread(/* should_run_callbacks= */ false);
+ self = nullptr;
+ }
+
// Make sure our internal threads are dead before we start tearing down things they're using.
GetRuntimeCallbacks()->StopDebugger();
// Deletion ordering is tricky. Null out everything we've deleted.
@@ -507,8 +516,8 @@
monitor_pool_ = nullptr;
delete class_linker_;
class_linker_ = nullptr;
- delete small_irt_allocator_;
- small_irt_allocator_ = nullptr;
+ delete small_lrt_allocator_;
+ small_lrt_allocator_ = nullptr;
delete heap_;
heap_ = nullptr;
delete intern_table_;
@@ -522,6 +531,7 @@
// Destroy allocators before shutting down the MemMap because they may use it.
java_vm_.reset();
linear_alloc_.reset();
+ startup_linear_alloc_.reset();
linear_alloc_arena_pool_.reset();
arena_pool_.reset();
jit_arena_pool_.reset();
@@ -549,7 +559,7 @@
os << "Runtime aborting...\n";
if (Runtime::Current() == nullptr) {
os << "(Runtime does not yet exist!)\n";
- DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), " native: ", nullptr);
return;
}
Thread* self = Thread::Current();
@@ -561,7 +571,7 @@
if (self == nullptr) {
os << "(Aborting thread was not attached to runtime!)\n";
- DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), " native: ", nullptr);
} else {
os << "Aborting thread:\n";
if (Locks::mutator_lock_->IsExclusiveHeld(self) || Locks::mutator_lock_->IsSharedHeld(self)) {
@@ -703,38 +713,52 @@
// notreached
}
-class FindNativeMethodsVisitor : public ClassVisitor {
+/**
+ * Update entrypoints (native and Java) of methods before the first fork. This
+ * helps sharing pages where ArtMethods are allocated between the zygote and
+ * forked apps.
+ */
+class UpdateMethodsPreFirstForkVisitor : public ClassVisitor {
public:
- FindNativeMethodsVisitor(Thread* self, ClassLinker* class_linker)
+ UpdateMethodsPreFirstForkVisitor(Thread* self, ClassLinker* class_linker)
: vm_(down_cast<JNIEnvExt*>(self->GetJniEnv())->GetVm()),
self_(self),
- class_linker_(class_linker) {}
+ class_linker_(class_linker),
+ can_use_nterp_(interpreter::CanRuntimeUseNterp()) {}
bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) {
bool is_initialized = klass->IsVisiblyInitialized();
for (ArtMethod& method : klass->GetDeclaredMethods(kRuntimePointerSize)) {
- if (method.IsNative() && (is_initialized || !NeedsClinitCheckBeforeCall(&method))) {
- const void* existing = method.GetEntryPointFromJni();
- if (method.IsCriticalNative()
- ? class_linker_->IsJniDlsymLookupCriticalStub(existing)
- : class_linker_->IsJniDlsymLookupStub(existing)) {
- const void* native_code =
- vm_->FindCodeForNativeMethod(&method, /*error_msg=*/ nullptr, /*can_suspend=*/ false);
- if (native_code != nullptr) {
- class_linker_->RegisterNative(self_, &method, native_code);
+ if (is_initialized || !method.NeedsClinitCheckBeforeCall()) {
+ if (method.IsNative()) {
+ const void* existing = method.GetEntryPointFromJni();
+ if (method.IsCriticalNative()
+ ? class_linker_->IsJniDlsymLookupCriticalStub(existing)
+ : class_linker_->IsJniDlsymLookupStub(existing)) {
+ const void* native_code =
+ vm_->FindCodeForNativeMethod(&method, /*error_msg=*/ nullptr, /*can_suspend=*/ false);
+ if (native_code != nullptr) {
+ class_linker_->RegisterNative(self_, &method, native_code);
+ }
}
}
+ } else if (can_use_nterp_) {
+ const void* existing = method.GetEntryPointFromQuickCompiledCode();
+ if (class_linker_->IsQuickResolutionStub(existing) && CanMethodUseNterp(&method)) {
+ method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpWithClinitEntryPoint());
+ }
}
}
return true;
}
private:
- JavaVMExt* vm_;
- Thread* self_;
- ClassLinker* class_linker_;
+ JavaVMExt* const vm_;
+ Thread* const self_;
+ ClassLinker* const class_linker_;
+ const bool can_use_nterp_;
- DISALLOW_COPY_AND_ASSIGN(FindNativeMethodsVisitor);
+ DISALLOW_COPY_AND_ASSIGN(UpdateMethodsPreFirstForkVisitor);
};
void Runtime::PreZygoteFork() {
@@ -742,14 +766,16 @@
GetJit()->PreZygoteFork();
}
if (!heap_->HasZygoteSpace()) {
+ Thread* self = Thread::Current();
// This is the first fork. Update ArtMethods in the boot classpath now to
// avoid having forked apps dirty the memory.
- ScopedObjectAccess soa(Thread::Current());
+
// Ensure we call FixupStaticTrampolines on all methods that are
// initialized.
- class_linker_->MakeInitializedClassesVisiblyInitialized(soa.Self(), /*wait=*/ true);
- // Update native method JNI entrypoints.
- FindNativeMethodsVisitor visitor(soa.Self(), class_linker_);
+ class_linker_->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
+
+ ScopedObjectAccess soa(self);
+ UpdateMethodsPreFirstForkVisitor visitor(self, class_linker_);
class_linker_->VisitClasses(&visitor);
}
heap_->PreZygoteFork();
@@ -821,6 +847,18 @@
return runtime != nullptr && runtime->IsStarted() && !runtime->IsShuttingDownLocked();
}
+void Runtime::AddGeneratedCodeRange(const void* start, size_t size) {
+ if (HandlesSignalsInCompiledCode()) {
+ fault_manager.AddGeneratedCodeRange(start, size);
+ }
+}
+
+void Runtime::RemoveGeneratedCodeRange(const void* start, size_t size) {
+ if (HandlesSignalsInCompiledCode()) {
+ fault_manager.RemoveGeneratedCodeRange(start, size);
+ }
+}
+
bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
// TODO: acquire a static mutex on Runtime to avoid racing.
if (Runtime::instance_ != nullptr) {
@@ -850,43 +888,34 @@
}
ScopedObjectAccess soa(Thread::Current());
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ ClassLinker* cl = runtime->GetClassLinker();
auto pointer_size = cl->GetImagePointerSize();
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> class_loader_class(
- hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ClassLoader)));
- CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true));
+ ObjPtr<mirror::Class> class_loader_class = GetClassRoot<mirror::ClassLoader>(cl);
+ DCHECK(class_loader_class->IsInitialized()); // Class roots have been initialized.
ArtMethod* getSystemClassLoader = class_loader_class->FindClassMethod(
"getSystemClassLoader", "()Ljava/lang/ClassLoader;", pointer_size);
CHECK(getSystemClassLoader != nullptr);
CHECK(getSystemClassLoader->IsStatic());
- JValue result = InvokeWithJValues(soa,
- nullptr,
- getSystemClassLoader,
- nullptr);
- JNIEnv* env = soa.Self()->GetJniEnv();
- ScopedLocalRef<jobject> system_class_loader(env, soa.AddLocalReference<jobject>(result.GetL()));
- CHECK(system_class_loader.get() != nullptr);
+ ObjPtr<mirror::Object> system_class_loader = getSystemClassLoader->InvokeStatic<'L'>(soa.Self());
+ CHECK(system_class_loader != nullptr);
- soa.Self()->SetClassLoaderOverride(system_class_loader.get());
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ jobject g_system_class_loader =
+ runtime->GetJavaVM()->AddGlobalRef(soa.Self(), system_class_loader);
+ soa.Self()->SetClassLoaderOverride(g_system_class_loader);
- Handle<mirror::Class> thread_class(
- hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread)));
- CHECK(cl->EnsureInitialized(soa.Self(), thread_class, true, true));
-
+ ObjPtr<mirror::Class> thread_class = WellKnownClasses::java_lang_Thread.Get();
ArtField* contextClassLoader =
thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
CHECK(contextClassLoader != nullptr);
// We can't run in a transaction yet.
- contextClassLoader->SetObject<false>(
- soa.Self()->GetPeer(),
- soa.Decode<mirror::ClassLoader>(system_class_loader.get()).Ptr());
+ contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), system_class_loader);
- return env->NewGlobalRef(system_class_loader.get());
+ return g_system_class_loader;
}
std::string Runtime::GetCompilerExecutable() const {
@@ -938,26 +967,12 @@
// Restore main thread state to kNative as expected by native code.
Thread* self = Thread::Current();
- self->TransitionFromRunnableToSuspended(ThreadState::kNative);
-
started_ = true;
- if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) {
- ScopedObjectAccess soa(self);
- StackHandleScope<3> hs(soa.Self());
+ class_linker_->RunEarlyRootClinits(self);
+ InitializeIntrinsics();
- ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots = GetClassLinker()->GetClassRoots();
- auto class_class(hs.NewHandle<mirror::Class>(GetClassRoot<mirror::Class>(class_roots)));
- auto string_class(hs.NewHandle<mirror::Class>(GetClassRoot<mirror::String>(class_roots)));
- auto field_class(hs.NewHandle<mirror::Class>(GetClassRoot<mirror::Field>(class_roots)));
-
- class_linker_->EnsureInitialized(soa.Self(), class_class, true, true);
- class_linker_->EnsureInitialized(soa.Self(), string_class, true, true);
- self->AssertNoPendingException();
- // Field class is needed for register_java_net_InetAddress in libcore, b/28153851.
- class_linker_->EnsureInitialized(soa.Self(), field_class, true, true);
- self->AssertNoPendingException();
- }
+ self->TransitionFromRunnableToSuspended(ThreadState::kNative);
// InitNativeMethods needs to be after started_ so that the classes
// it touches will have methods linked to the oat file if necessary.
@@ -966,11 +981,6 @@
InitNativeMethods();
}
- // IntializeIntrinsics needs to be called after the WellKnownClasses::Init in InitNativeMethods
- // because in checking the invocation types of intrinsic methods ArtMethod::GetInvokeType()
- // needs the SignaturePolymorphic annotation class which is initialized in WellKnownClasses::Init.
- InitializeIntrinsics();
-
// InitializeCorePlatformApiPrivateFields() needs to be called after well known class
// initializtion in InitNativeMethods().
art::hiddenapi::InitializeCorePlatformApiPrivateFields();
@@ -993,8 +1003,23 @@
if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
}
- CreateJitCodeCache(/*rwx_memory_allowed=*/true);
CreateJit();
+#ifdef ADDRESS_SANITIZER
+ // (b/238730394): In older implementations of sanitizer + glibc there is a race between
+ // pthread_create and dlopen that could cause a deadlock. pthread_create interceptor in ASAN
+ // uses dl_pthread_iterator with a callback that could request a dl_load_lock via call to
+ // __tls_get_addr [1]. dl_pthread_iterate would already hold dl_load_lock so this could cause a
+ // deadlock. __tls_get_addr needs a dl_load_lock only when there is a dlopen happening in
+ // parallel. As a workaround we wait for the pthread_create (i.e JIT thread pool creation) to
+ // finish before going to the next phase. Creating a system class loader could need a dlopen so
+ // we wait here till threads are initialized.
+ // [1] https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp#L408
+ // See this for more context: https://reviews.llvm.org/D98926
+ // TODO(b/238730394): Revisit this workaround once we migrate to musl libc.
+ if (jit_ != nullptr) {
+ jit_->GetThreadPool()->WaitForWorkersToBeCreated();
+ }
+#endif
}
// Send the start phase event. We have to wait till here as this is when the main thread peer
@@ -1020,24 +1045,14 @@
GetInstructionSetString(kRuntimeISA));
}
- StartDaemonThreads();
-
- // Make sure the environment is still clean (no lingering local refs from starting daemon
- // threads).
{
ScopedObjectAccess soa(self);
+ StartDaemonThreads();
self->GetJniEnv()->AssertLocalsEmpty();
- }
- // Send the initialized phase event. Send it after starting the Daemon threads so that agents
- // cannot delay the daemon threads from starting forever.
- {
- ScopedObjectAccess soa(self);
+ // Send the initialized phase event. Send it after starting the Daemon threads so that agents
+ // cannot delay the daemon threads from starting forever.
callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit);
- }
-
- {
- ScopedObjectAccess soa(self);
self->GetJniEnv()->AssertLocalsEmpty();
}
@@ -1046,9 +1061,20 @@
if (trace_config_.get() != nullptr && trace_config_->trace_file != "") {
ScopedThreadStateChange tsc(self, ThreadState::kWaitingForMethodTracingStart);
+ int flags = 0;
+ if (trace_config_->clock_source == TraceClockSource::kDual) {
+ flags = Trace::TraceFlag::kTraceClockSourceWallClock |
+ Trace::TraceFlag::kTraceClockSourceThreadCpu;
+ } else if (trace_config_->clock_source == TraceClockSource::kWall) {
+ flags = Trace::TraceFlag::kTraceClockSourceWallClock;
+ } else if (TraceClockSource::kThreadCpu == trace_config_->clock_source) {
+ flags = Trace::TraceFlag::kTraceClockSourceThreadCpu;
+ } else {
+ LOG(ERROR) << "Unexpected clock source";
+ }
Trace::Start(trace_config_->trace_file.c_str(),
static_cast<int>(trace_config_->trace_file_size),
- 0,
+ flags,
trace_config_->trace_output_mode,
trace_config_->trace_mode,
0);
@@ -1133,8 +1159,8 @@
std::vector<std::string> jars = android::base::Split(system_server_classpath, ":");
app_info_.RegisterAppInfo("android",
jars,
- /*cur_profile_path=*/ "",
- /*ref_profile_path=*/ "",
+ /*profile_output_filename=*/ "",
+ /*ref_profile_filename=*/ "",
AppInfo::CodeType::kPrimaryApk);
}
@@ -1249,15 +1275,11 @@
Thread* self = Thread::Current();
- // Must be in the kNative state for calling native methods.
- CHECK_EQ(self->GetState(), ThreadState::kNative);
+ DCHECK_EQ(self->GetState(), ThreadState::kRunnable);
- JNIEnv* env = self->GetJniEnv();
- env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
- WellKnownClasses::java_lang_Daemons_start);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- LOG(FATAL) << "Error starting java.lang.Daemons";
+ WellKnownClasses::java_lang_Daemons_start->InvokeStatic<'V'>(self);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ LOG(FATAL) << "Error starting java.lang.Daemons: " << self->GetException()->Dump();
}
VLOG(startup) << "Runtime::StartDaemonThreads exiting";
@@ -1333,9 +1355,9 @@
detailMessageField->SetObject</* kTransactionActive= */ false>(exception->Read(), message);
}
-void Runtime::InitializeApexVersions() {
+std::string Runtime::GetApexVersions(ArrayRef<const std::string> boot_class_path_locations) {
std::vector<std::string_view> bcp_apexes;
- for (std::string_view jar : Runtime::Current()->GetBootClassPathLocations()) {
+ for (std::string_view jar : boot_class_path_locations) {
std::string_view apex = ApexNameFromLocation(jar);
if (!apex.empty()) {
bcp_apexes.push_back(apex);
@@ -1343,20 +1365,20 @@
}
static const char* kApexFileName = "/apex/apex-info-list.xml";
// Start with empty markers.
- apex_versions_ = std::string(bcp_apexes.size(), '/');
+ std::string empty_apex_versions(bcp_apexes.size(), '/');
// When running on host or chroot, we just use empty markers.
if (!kIsTargetBuild || !OS::FileExists(kApexFileName)) {
- return;
+ return empty_apex_versions;
}
#ifdef ART_TARGET_ANDROID
if (access(kApexFileName, R_OK) != 0) {
PLOG(WARNING) << "Failed to read " << kApexFileName;
- return;
+ return empty_apex_versions;
}
auto info_list = apex::readApexInfoList(kApexFileName);
if (!info_list.has_value()) {
LOG(WARNING) << "Failed to parse " << kApexFileName;
- return;
+ return empty_apex_versions;
}
std::string result;
@@ -1380,10 +1402,17 @@
android::base::StringAppendF(&result, "/%" PRIu64, version);
}
}
- apex_versions_ = result;
+ return result;
+#else
+ return empty_apex_versions; // Not an Android build.
#endif
}
+void Runtime::InitializeApexVersions() {
+ apex_versions_ =
+ GetApexVersions(ArrayRef<const std::string>(Runtime::Current()->GetBootClassPathLocations()));
+}
+
void Runtime::ReloadAllFlags(const std::string& caller) {
FlagBase::ReloadAllFlags(caller);
}
@@ -1495,6 +1524,7 @@
is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit);
+ allow_in_memory_compilation_ = runtime_options.Exists(Opt::AllowInMemoryCompilation);
if (is_zygote_ || runtime_options.Exists(Opt::OnlyUseTrustedOatFiles)) {
oat_file_manager_->SetOnlyUseTrustedOatFiles();
@@ -1510,7 +1540,7 @@
compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
for (const std::string& option : Runtime::Current()->GetCompilerOptions()) {
if (option == "--debuggable") {
- SetJavaDebuggable(true);
+ SetRuntimeDebugState(RuntimeDebugState::kJavaDebuggableAtInit);
break;
}
}
@@ -1557,6 +1587,8 @@
<< (core_platform_api_policy_ == hiddenapi::EnforcementPolicy::kEnabled ? "true" : "false");
}
+ // Dex2Oat's Runtime does not need the signal chain or the fault handler
+ // and it passes the `NoSigChain` option to `Runtime` to indicate this.
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
@@ -1721,8 +1753,9 @@
linear_alloc_arena_pool_.reset(new MemMapArenaPool(low_4gb));
}
linear_alloc_.reset(CreateLinearAlloc());
+ startup_linear_alloc_.reset(CreateLinearAlloc());
- small_irt_allocator_ = new SmallIrtAllocator();
+ small_lrt_allocator_ = new jni::SmallLrtAllocator();
BlockSignals();
InitPlatformSignalHandlers();
@@ -1730,9 +1763,7 @@
// Change the implicit checks flags based on runtime architecture.
switch (kRuntimeISA) {
case InstructionSet::kArm64:
- // TODO: Implicit suspend checks are currently disabled to facilitate search
- // for unrelated memory use regressions. Bug: 213757852.
- implicit_suspend_checks_ = false;
+ implicit_suspend_checks_ = true;
FALLTHROUGH_INTENDED;
case InstructionSet::kArm:
case InstructionSet::kThumb2:
@@ -1748,10 +1779,9 @@
}
if (!no_sig_chain_) {
- // Dex2Oat's Runtime does not need the signal chain or the fault handler.
- if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {
- fault_manager.Init();
+ fault_manager.Init();
+ if (HandlesSignalsInCompiledCode()) {
// These need to be in a specific order. The null point check handler must be
// after the suspend check and stack overflow check handlers.
//
@@ -1772,6 +1802,12 @@
if (kEnableJavaStackTraceHandler) {
new JavaStackTraceHandler(&fault_manager);
}
+
+ if (interpreter::CanRuntimeUseNterp()) {
+ // Nterp code can use signal handling just like the compiled managed code.
+ OatQuickMethodHeader* nterp_header = OatQuickMethodHeader::NterpMethodHeader;
+ fault_manager.AddGeneratedCodeRange(nterp_header->GetCode(), nterp_header->GetCodeSize());
+ }
}
}
@@ -1904,9 +1940,10 @@
trace_config_->trace_output_mode = runtime_options.Exists(Opt::MethodTraceStreaming) ?
Trace::TraceOutputMode::kStreaming :
Trace::TraceOutputMode::kFile;
+ trace_config_->clock_source = runtime_options.GetOrDefault(Opt::MethodTraceClock);
}
- // TODO: move this to just be an Trace::Start argument
+ // TODO: Remove this in a follow up CL. This isn't used anywhere.
Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
if (GetHeap()->HasBootImageSpace()) {
@@ -2153,10 +2190,6 @@
// Set up the native methods provided by the runtime itself.
RegisterRuntimeNativeMethods(env);
- // Initialize classes used in JNI. The initialization requires runtime native
- // methods to be loaded first.
- WellKnownClasses::Init(env);
-
// Then set up libjavacore / libopenjdk / libicu_jni ,which are just
// a regular JNI libraries with a regular JNI_OnLoad. Most JNI libraries can
// just use System.loadLibrary, but libcore can't because it's the library
@@ -2165,20 +2198,26 @@
// By setting calling class to java.lang.Object, the caller location for these
// JNI libs is core-oj.jar in the ART APEX, and hence they are loaded from the
// com_android_art linker namespace.
+ jclass java_lang_Object;
+ {
+ ScopedObjectAccess soa(self);
+ java_lang_Object = reinterpret_cast<jclass>(
+ GetJavaVM()->AddGlobalRef(self, GetClassRoot<mirror::Object>(GetClassLinker())));
+ }
// libicu_jni has to be initialized before libopenjdk{d} due to runtime dependency from
// libopenjdk{d} to Icu4cMetadata native methods in libicu_jni. See http://b/143888405
{
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(
- env, "libicu_jni.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+ env, "libicu_jni.so", nullptr, java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"libicu_jni.so\": " << error_msg;
}
}
{
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(
- env, "libjavacore.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+ env, "libjavacore.so", nullptr, java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
}
}
@@ -2188,10 +2227,11 @@
: "libopenjdk.so";
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(
- env, kOpenJdkLibrary, nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+ env, kOpenJdkLibrary, nullptr, java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
}
}
+ env->DeleteGlobalRef(java_lang_Object);
// Initialize well known classes that may invoke runtime native methods.
WellKnownClasses::LateInit(env);
@@ -2204,17 +2244,28 @@
}
void Runtime::InitThreadGroups(Thread* self) {
- JNIEnvExt* env = self->GetJniEnv();
- ScopedJniEnvLocalRefState env_state(env);
+ ScopedObjectAccess soa(self);
+ ArtField* main_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
+ ArtField* system_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ // Note: This is running before `ClassLinker::RunRootClinits()`, so we cannot rely on
+ // `ThreadGroup` and `Thread` being initialized.
+ // TODO: Clean up initialization order after all well-known methods are converted to `ArtMethod*`
+ // (and therefore the `WellKnownClasses::Init()` shall not initialize any classes).
+ StackHandleScope<2u> hs(self);
+ Handle<mirror::Class> thread_group_class =
+ hs.NewHandle(main_thread_group_field->GetDeclaringClass());
+ bool initialized = GetClassLinker()->EnsureInitialized(
+ self, thread_group_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(initialized);
+ Handle<mirror::Class> thread_class = hs.NewHandle(WellKnownClasses::java_lang_Thread.Get());
+ initialized = GetClassLinker()->EnsureInitialized(
+ self, thread_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(initialized);
main_thread_group_ =
- env->NewGlobalRef(env->GetStaticObjectField(
- WellKnownClasses::java_lang_ThreadGroup,
- WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup));
+ soa.Vm()->AddGlobalRef(self, main_thread_group_field->GetObject(thread_group_class.Get()));
CHECK_IMPLIES(main_thread_group_ == nullptr, IsAotCompiler());
system_thread_group_ =
- env->NewGlobalRef(env->GetStaticObjectField(
- WellKnownClasses::java_lang_ThreadGroup,
- WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
+ soa.Vm()->AddGlobalRef(self, system_thread_group_field->GetObject(thread_group_class.Get()));
CHECK_IMPLIES(system_thread_group_ == nullptr, IsAotCompiler());
}
@@ -2253,6 +2304,7 @@
register_java_lang_reflect_Parameter(env);
register_java_lang_reflect_Proxy(env);
register_java_lang_ref_Reference(env);
+ register_java_lang_StackStreamFactory(env);
register_java_lang_String(env);
register_java_lang_StringFactory(env);
register_java_lang_System(env);
@@ -2286,6 +2338,9 @@
}
void Runtime::DumpForSigQuit(std::ostream& os) {
+ // Print backtraces first since they are important do diagnose ANRs,
+ // and ANRs can often be trimmed to limit upload size.
+ thread_list_->DumpForSigQuit(os);
GetClassLinker()->DumpForSigQuit(os);
GetInternTable()->DumpForSigQuit(os);
GetJavaVM()->DumpForSigQuit(os);
@@ -2301,7 +2356,6 @@
GetMetrics()->DumpForSigQuit(os);
os << "\n";
- thread_list_->DumpForSigQuit(os);
BaseMutex::DumpAll(os);
// Inform anyone else who is interested in SigQuit.
@@ -2312,11 +2366,11 @@
}
void Runtime::DumpLockHolders(std::ostream& os) {
- uint64_t mutator_lock_owner = Locks::mutator_lock_->GetExclusiveOwnerTid();
+ pid_t mutator_lock_owner = Locks::mutator_lock_->GetExclusiveOwnerTid();
pid_t thread_list_lock_owner = GetThreadList()->GetLockOwner();
pid_t classes_lock_owner = GetClassLinker()->GetClassesLockOwner();
pid_t dex_lock_owner = GetClassLinker()->GetDexLockOwner();
- if ((thread_list_lock_owner | classes_lock_owner | dex_lock_owner) != 0) {
+ if ((mutator_lock_owner | thread_list_lock_owner | classes_lock_owner | dex_lock_owner) != 0) {
os << "Mutator lock exclusive owner tid: " << mutator_lock_owner << "\n"
<< "ThreadList lock owner tid: " << thread_list_lock_owner << "\n"
<< "ClassLinker classes lock owner tid: " << classes_lock_owner << "\n"
@@ -2720,10 +2774,6 @@
LOG(WARNING) << "JIT profile information will not be recorded: profile filename is empty.";
return;
}
- if (!OS::FileExists(profile_output_filename.c_str(), /*check_file_type=*/ false)) {
- LOG(WARNING) << "JIT profile information will not be recorded: profile file does not exist.";
- return;
- }
if (code_paths.empty()) {
LOG(WARNING) << "JIT profile information will not be recorded: code paths is empty.";
return;
@@ -2745,7 +2795,13 @@
// Make initialized classes visibly initialized now. If that happened during the transaction
// and then the transaction was aborted, we would roll back the status update but not the
// ClassLinker's bookkeeping structures, so these classes would never be visibly initialized.
- GetClassLinker()->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
+ {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(&root));
+ ScopedThreadSuspension sts(self, ThreadState::kNative);
+ GetClassLinker()->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
+ }
// Pass the runtime `ArenaPool` to the transaction.
arena_pool = GetArenaPool();
} else {
@@ -2973,7 +3029,9 @@
}
}
-void Runtime::CreateJitCodeCache(bool rwx_memory_allowed) {
+void Runtime::CreateJit() {
+ DCHECK(jit_code_cache_ == nullptr);
+ DCHECK(jit_ == nullptr);
if (kIsDebugBuild && GetInstrumentation()->IsForcedInterpretOnly()) {
DCHECK(!jit_options_->UseJitCompilation());
}
@@ -2982,28 +3040,19 @@
return;
}
+ if (IsSafeMode()) {
+ LOG(INFO) << "Not creating JIT because of SafeMode.";
+ return;
+ }
+
std::string error_msg;
bool profiling_only = !jit_options_->UseJitCompilation();
jit_code_cache_.reset(jit::JitCodeCache::Create(profiling_only,
- rwx_memory_allowed,
+ /*rwx_memory_allowed=*/ true,
IsZygote(),
&error_msg));
if (jit_code_cache_.get() == nullptr) {
LOG(WARNING) << "Failed to create JIT Code Cache: " << error_msg;
- }
-}
-
-void Runtime::CreateJit() {
- DCHECK(jit_ == nullptr);
- if (jit_code_cache_.get() == nullptr) {
- if (!IsSafeMode()) {
- LOG(WARNING) << "Missing code cache, cannot create JIT.";
- }
- return;
- }
- if (IsSafeMode()) {
- LOG(INFO) << "Not creating JIT because of SafeMode.";
- jit_code_cache_.reset();
return;
}
@@ -3066,12 +3115,13 @@
return verify_ == verifier::VerifyMode::kSoftFail;
}
-bool Runtime::IsAsyncDeoptimizeable(uintptr_t code) const {
+bool Runtime::IsAsyncDeoptimizeable(ArtMethod* method, uintptr_t code) const {
if (OatQuickMethodHeader::NterpMethodHeader != nullptr) {
if (OatQuickMethodHeader::NterpMethodHeader->Contains(code)) {
return true;
}
}
+
// We only support async deopt (ie the compiled code is not explicitly asking for
// deopt, but something else like the debugger) in debuggable JIT code.
// We could look at the oat file where `code` is being defined,
@@ -3079,8 +3129,14 @@
// only rely on the JIT for debuggable apps.
// The JIT-zygote is not debuggable so we need to be sure to exclude code from the non-private
// region as well.
- return IsJavaDebuggable() && GetJit() != nullptr &&
- GetJit()->GetCodeCache()->PrivateRegionContainsPc(reinterpret_cast<const void*>(code));
+ if (GetJit() != nullptr &&
+ GetJit()->GetCodeCache()->PrivateRegionContainsPc(reinterpret_cast<const void*>(code))) {
+ // If the code is JITed code then check if it was compiled as debuggable.
+ const OatQuickMethodHeader* header = method->GetOatQuickMethodHeader(code);
+ return CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr());
+ }
+
+ return false;
}
@@ -3113,6 +3169,9 @@
if (GetLinearAlloc() != nullptr) {
GetLinearAlloc()->SetupForPostZygoteFork(self);
}
+ if (GetStartupLinearAlloc() != nullptr) {
+ GetStartupLinearAlloc()->SetupForPostZygoteFork(self);
+ }
{
Locks::mutator_lock_->AssertNotHeld(self);
ReaderMutexLock mu2(self, *Locks::mutator_lock_);
@@ -3199,15 +3258,22 @@
auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (auto& m : klass->GetMethods(pointer_size)) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
+ if (!m.IsInvokable()) {
+ continue;
+ }
+ // For java debuggable runtimes we also deoptimize native methods. For other cases (boot
+ // image profiling) we don't need to deoptimize native methods. If this changes also
+ // update Instrumentation::CanUseAotCode.
+ bool deoptimize_native_methods = Runtime::Current()->IsJavaDebuggable();
if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
- !m.IsNative() &&
+ (!m.IsNative() || deoptimize_native_methods) &&
!m.IsProxyMethod()) {
instrumentation_->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
}
if (Runtime::Current()->GetJit() != nullptr &&
Runtime::Current()->GetJit()->GetCodeCache()->IsInZygoteExecSpace(code) &&
- !m.IsNative()) {
+ (!m.IsNative() || deoptimize_native_methods)) {
DCHECK(!m.IsProxyMethod());
instrumentation_->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
}
@@ -3226,23 +3292,24 @@
instrumentation::Instrumentation* const instrumentation_;
};
-void Runtime::SetJavaDebuggable(bool value) {
- is_java_debuggable_ = value;
- // Do not call DeoptimizeBootImage just yet, the runtime may still be starting up.
+void Runtime::SetRuntimeDebugState(RuntimeDebugState state) {
+ if (state != RuntimeDebugState::kJavaDebuggableAtInit) {
+ // We never change the state if we started as a debuggable runtime.
+ DCHECK(runtime_debug_state_ != RuntimeDebugState::kJavaDebuggableAtInit);
+ }
+ runtime_debug_state_ = state;
}
void Runtime::DeoptimizeBootImage() {
// If we've already started and we are setting this runtime to debuggable,
// we patch entry points of methods in boot image to interpreter bridge, as
// boot image code may be AOT compiled as not debuggable.
- if (!GetInstrumentation()->IsForcedInterpretOnly()) {
- UpdateEntryPointsClassVisitor visitor(GetInstrumentation());
- GetClassLinker()->VisitClasses(&visitor);
- jit::Jit* jit = GetJit();
- if (jit != nullptr) {
- // Code previously compiled may not be compiled debuggable.
- jit->GetCodeCache()->TransitionToDebuggable();
- }
+ UpdateEntryPointsClassVisitor visitor(GetInstrumentation());
+ GetClassLinker()->VisitClasses(&visitor);
+ jit::Jit* jit = GetJit();
+ if (jit != nullptr) {
+ // Code previously compiled may not be compiled debuggable.
+ jit->GetCodeCache()->TransitionToDebuggable();
}
}
@@ -3290,6 +3357,31 @@
startup_completed_.store(false, std::memory_order_seq_cst);
}
+class CollectStartupDexCacheVisitor : public DexCacheVisitor {
+ public:
+ explicit CollectStartupDexCacheVisitor(VariableSizedHandleScope& handles) : handles_(handles) {}
+
+ void Visit(ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_) override {
+ handles_.NewHandle(dex_cache);
+ }
+
+ private:
+ VariableSizedHandleScope& handles_;
+};
+
+class UnlinkVisitor {
+ public:
+ UnlinkVisitor() {}
+
+ void VisitRootIfNonNull(StackReference<mirror::Object>* ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!ref->IsNull()) {
+ ref->AsMirrorPtr()->AsDexCache()->UnlinkStartupCaches();
+ }
+ }
+};
+
class Runtime::NotifyStartupCompletedTask : public gc::HeapTask {
public:
NotifyStartupCompletedTask() : gc::HeapTask(/*target_run_time=*/ NanoTime()) {}
@@ -3298,10 +3390,38 @@
VLOG(startup) << "NotifyStartupCompletedTask running";
Runtime* const runtime = Runtime::Current();
{
- ScopedTrace trace("Releasing app image spaces metadata");
+ std::string compiler_filter;
+ std::string compilation_reason;
+ runtime->GetAppInfo()->GetPrimaryApkOptimizationStatus(&compiler_filter, &compilation_reason);
+ CompilerFilter::Filter filter;
+ if (CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter) &&
+ !CompilerFilter::IsAotCompilationEnabled(filter)) {
+ std::string error_msg;
+ if (!RuntimeImage::WriteImageToDisk(&error_msg)) {
+ LOG(DEBUG) << "Could not write temporary image to disk " << error_msg;
+ }
+ }
+ }
+ // Fetch the startup linear alloc before the checkpoint to play nice with
+ // 1002-notify-startup test which resets the startup state.
+ std::unique_ptr<LinearAlloc> startup_linear_alloc(runtime->ReleaseStartupLinearAlloc());
+ {
+ ScopedTrace trace("Releasing dex caches and app image spaces metadata");
ScopedObjectAccess soa(Thread::Current());
- // Request empty checkpoints to make sure no threads are accessing the image space metadata
- // section when we madvise it. Use GC exclusion to prevent deadlocks that may happen if
+
+ // Collect dex caches that were allocated with the startup linear alloc.
+ VariableSizedHandleScope handles(soa.Self());
+ {
+ CollectStartupDexCacheVisitor visitor(handles);
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ runtime->GetClassLinker()->VisitDexCaches(&visitor);
+ }
+
+ // Request empty checkpoints to make sure no threads are:
+ // - accessing the image space metadata section when we madvise it
+ // - accessing dex caches when we free them
+ //
+ // Use GC exclusion to prevent deadlocks that may happen if
// multiple threads are attempting to run empty checkpoints at the same time.
{
// Avoid using ScopedGCCriticalSection since that does not allow thread suspension. This is
@@ -3310,8 +3430,15 @@
gc::ScopedInterruptibleGCCriticalSection sigcs(self,
gc::kGcCauseRunEmptyCheckpoint,
gc::kCollectorTypeCriticalSection);
+ // Do the unlinking of dex cache arrays in the GC critical section to
+ // avoid GC not seeing these arrays. We do it before the checkpoint so
+ // we know after the checkpoint, no thread is holding onto the array.
+ UnlinkVisitor visitor;
+ handles.VisitRoots(visitor);
+
runtime->GetThreadList()->RunEmptyCheckpoint();
}
+
for (gc::space::ContinuousSpace* space : runtime->GetHeap()->GetContinuousSpaces()) {
if (space->IsImageSpace()) {
gc::space::ImageSpace* image_space = space->AsImageSpace();
@@ -3327,6 +3454,13 @@
ScopedTrace trace2("Delete thread pool");
runtime->DeleteThreadPool();
}
+
+ {
+ // We know that after the checkpoint, there is no thread that can hold
+ // the startup linear alloc, so it's safe to delete it now.
+ ScopedTrace trace2("Delete startup linear alloc");
+ startup_linear_alloc.reset();
+ }
}
};
@@ -3379,8 +3513,12 @@
WellKnownClasses::HandleJniIdTypeChange(Thread::Current()->GetJniEnv());
}
+bool Runtime::IsSystemServerProfiled() const {
+ return IsSystemServer() && jit_options_->GetSaveProfilingInfo();
+}
+
bool Runtime::GetOatFilesExecutable() const {
- return !IsAotCompiler() && !(IsSystemServer() && jit_options_->GetSaveProfilingInfo());
+ return !IsAotCompiler() && !IsSystemServerProfiled();
}
void Runtime::ProcessWeakClass(GcRoot<mirror::Class>* root_ptr,
@@ -3470,6 +3608,8 @@
}
}
+// Return whether a boot image has a profile. This means we'll need to pre-JIT
+// methods in that profile for performance.
bool Runtime::HasImageWithProfile() const {
for (gc::space::ImageSpace* space : GetHeap()->GetBootImageSpaces()) {
if (!space->GetProfileFiles().empty()) {
@@ -3479,4 +3619,70 @@
return false;
}
+void Runtime::AppendToBootClassPath(const std::string& filename, const std::string& location) {
+ DCHECK(!DexFileLoader::IsMultiDexLocation(filename.c_str()));
+ boot_class_path_.push_back(filename);
+ if (!boot_class_path_locations_.empty()) {
+ DCHECK(!DexFileLoader::IsMultiDexLocation(location.c_str()));
+ boot_class_path_locations_.push_back(location);
+ }
+}
+
+void Runtime::AppendToBootClassPath(
+ const std::string& filename,
+ const std::string& location,
+ const std::vector<std::unique_ptr<const art::DexFile>>& dex_files) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ for (const std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file.get() == dex_files.begin()->get());
+ GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get());
+ }
+}
+
+void Runtime::AppendToBootClassPath(const std::string& filename,
+ const std::string& location,
+ const std::vector<const art::DexFile*>& dex_files) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ for (const art::DexFile* dex_file : dex_files) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file == *dex_files.begin());
+ GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file);
+ }
+}
+
+void Runtime::AppendToBootClassPath(
+ const std::string& filename,
+ const std::string& location,
+ const std::vector<std::pair<const art::DexFile*, ObjPtr<mirror::DexCache>>>&
+ dex_files_and_cache) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ for (const auto& [dex_file, dex_cache] : dex_files_and_cache) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file == dex_files_and_cache.begin()->first);
+ GetClassLinker()->AppendToBootClassPath(dex_file, dex_cache);
+ }
+}
+
+void Runtime::AddExtraBootDexFiles(const std::string& filename,
+ const std::string& location,
+ std::vector<std::unique_ptr<const art::DexFile>>&& dex_files) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ if (kIsDebugBuild) {
+ for (const std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file.get() == dex_files.begin()->get());
+ }
+ }
+ GetClassLinker()->AddExtraBootDexFiles(Thread::Current(), std::move(dex_files));
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 21383f9..a0a36b9 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -68,6 +68,10 @@
class JitOptions;
} // namespace jit
+namespace jni {
+class SmallLrtAllocator;
+} // namespace jni
+
namespace mirror {
class Array;
class ClassLoader;
@@ -107,7 +111,6 @@
struct RuntimeArgumentMap;
class RuntimeCallbacks;
class SignalCatcher;
-class SmallIrtAllocator;
class StackOverflowHandler;
class SuspensionHandler;
class ThreadList;
@@ -133,6 +136,19 @@
static bool Create(const RuntimeOptions& raw_options, bool ignore_unrecognized)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
+ enum class RuntimeDebugState {
+ // This doesn't support any debug features / method tracing. This is the expected state usually.
+ kNonJavaDebuggable,
+ // This supports method tracing and a restricted set of debug features (for ex: redefinition
+ // isn't supported). We transition to this state when method tracing has started or when the
+ // debugger was attached and transition back to NonDebuggable once the tracing has stopped /
+ // the debugger agent has detached..
+ kJavaDebuggable,
+ // The runtime was started as a debuggable runtime. This allows us to support the extended set
+ // of debug features (for ex: redefinition). We never transition out of this state.
+ kJavaDebuggableAtInit
+ };
+
bool EnsurePluginLoaded(const char* plugin_name, std::string* error_msg);
bool EnsurePerfettoPlugin(std::string* error_msg);
@@ -257,6 +273,13 @@
return instance_;
}
+ // Set the current runtime to be the given instance.
+ // Note that this function is not responsible for cleaning up the old instance or taking the
+ // ownership of the new instance.
+ //
+ // For test use only.
+ static void TestOnlySetCurrent(Runtime* instance) { instance_ = instance; }
+
// Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most
// callers should prefer.
NO_RETURN static void Abort(const char* msg) REQUIRES(!Locks::abort_lock_);
@@ -298,6 +321,28 @@
return boot_class_path_locations_.empty() ? boot_class_path_ : boot_class_path_locations_;
}
+ // Dynamically adds an element to boot class path.
+ void AppendToBootClassPath(const std::string& filename,
+ const std::string& location,
+ const std::vector<std::unique_ptr<const art::DexFile>>& dex_files);
+
+ // Same as above, but takes raw pointers.
+ void AppendToBootClassPath(const std::string& filename,
+ const std::string& location,
+ const std::vector<const art::DexFile*>& dex_files);
+
+ // Same as above, but also takes a dex cache for each dex file.
+ void AppendToBootClassPath(
+ const std::string& filename,
+ const std::string& location,
+ const std::vector<std::pair<const art::DexFile*, ObjPtr<mirror::DexCache>>>&
+ dex_files_and_cache);
+
+ // Dynamically adds an element to boot class path and takes ownership of the dex files.
+ void AddExtraBootDexFiles(const std::string& filename,
+ const std::string& location,
+ std::vector<std::unique_ptr<const art::DexFile>>&& dex_files);
+
const std::vector<int>& GetBootClassPathFds() const {
return boot_class_path_fds_;
}
@@ -328,8 +373,8 @@
return class_linker_;
}
- SmallIrtAllocator* GetSmallIrtAllocator() const {
- return small_irt_allocator_;
+ jni::SmallLrtAllocator* GetSmallLrtAllocator() const {
+ return small_lrt_allocator_;
}
jni::JniIdManager* GetJniIdManager() const {
@@ -500,6 +545,10 @@
return OFFSETOF_MEMBER(Runtime, callee_save_methods_[static_cast<size_t>(type)]);
}
+ static constexpr MemberOffset GetInstrumentationOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(Runtime, instrumentation_));
+ }
+
InstructionSet GetInstructionSet() const {
return instruction_set_;
}
@@ -569,7 +618,8 @@
// Transaction support.
bool IsActiveTransaction() const;
- void EnterTransactionMode(bool strict, mirror::Class* root);
+ // EnterTransactionMode may suspend.
+ void EnterTransactionMode(bool strict, mirror::Class* root) REQUIRES_SHARED(Locks::mutator_lock_);
void ExitTransactionMode();
void RollbackAllTransactions() REQUIRES_SHARED(Locks::mutator_lock_);
// Transaction rollback and exit transaction are always done together, it's convenience to
@@ -773,12 +823,21 @@
return linear_alloc_.get();
}
+ LinearAlloc* GetStartupLinearAlloc() {
+ return startup_linear_alloc_.get();
+ }
+
jit::JitOptions* GetJITOptions() {
return jit_options_.get();
}
bool IsJavaDebuggable() const {
- return is_java_debuggable_;
+ return runtime_debug_state_ == RuntimeDebugState::kJavaDebuggable ||
+ runtime_debug_state_ == RuntimeDebugState::kJavaDebuggableAtInit;
+ }
+
+ bool IsJavaDebuggableAtInit() const {
+ return runtime_debug_state_ == RuntimeDebugState::kJavaDebuggableAtInit;
}
void SetProfileableFromShell(bool value) {
@@ -797,7 +856,7 @@
return is_profileable_;
}
- void SetJavaDebuggable(bool value);
+ void SetRuntimeDebugState(RuntimeDebugState state);
// Deoptimize the boot image, called for Java debuggable apps.
void DeoptimizeBootImage() REQUIRES(Locks::mutator_lock_);
@@ -900,7 +959,8 @@
// Returns if the code can be deoptimized asynchronously. Code may be compiled with some
// optimization that makes it impossible to deoptimize.
- bool IsAsyncDeoptimizeable(uintptr_t code) const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsAsyncDeoptimizeable(ArtMethod* method, uintptr_t code) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a saved copy of the environment (getenv/setenv values).
// Used by Fork to protect against overwriting LD_LIBRARY_PATH, etc.
@@ -1014,6 +1074,10 @@
ThreadPool* const thread_pool_;
};
+ LinearAlloc* ReleaseStartupLinearAlloc() {
+ return startup_linear_alloc_.release();
+ }
+
bool LoadAppImageStartupCache() const {
return load_app_image_startup_cache_;
}
@@ -1060,7 +1124,11 @@
uint64_t GetMonitorTimeoutNs() const {
return monitor_timeout_ns_;
}
- // Return true if we should load oat files as executable or not.
+
+ // Return whether this is system server and it is being profiled.
+ bool IsSystemServerProfiled() const;
+
+ // Return whether we should load oat files as executable or not.
bool GetOatFilesExecutable() const;
metrics::ArtMetrics* GetMetrics() { return &metrics_; }
@@ -1087,6 +1155,10 @@
return no_sig_chain_;
}
+ void AddGeneratedCodeRange(const void* start, size_t size);
+ void RemoveGeneratedCodeRange(const void* start, size_t size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Trigger a flag reload from system properties or device congfigs.
//
// Should only be called from runtime init and zygote post fork as
@@ -1098,11 +1170,33 @@
// See Flags::ReloadAllFlags as well.
static void ReloadAllFlags(const std::string& caller);
+ // Parses /apex/apex-info-list.xml to build a string containing apex versions of boot classpath
+ // jars, which is encoded into .oat files.
+ static std::string GetApexVersions(ArrayRef<const std::string> boot_class_path_locations);
+
+ bool AllowInMemoryCompilation() const { return allow_in_memory_compilation_; }
+
+ // Used by plugin code to attach a hook for OOME.
+ void SetOutOfMemoryErrorHook(void (*hook)()) {
+ out_of_memory_error_hook_ = hook;
+ }
+
+ void OutOfMemoryErrorHook() {
+ if (out_of_memory_error_hook_ != nullptr) {
+ out_of_memory_error_hook_();
+ }
+ }
+
private:
static void InitPlatformSignalHandlers();
Runtime();
+ bool HandlesSignalsInCompiledCode() const {
+ return !no_sig_chain_ &&
+ (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_);
+ }
+
void BlockSignals();
bool Init(RuntimeArgumentMap&& runtime_options)
@@ -1111,7 +1205,7 @@
void RegisterRuntimeNativeMethods(JNIEnv* env);
void InitMetrics();
- void StartDaemonThreads();
+ void StartDaemonThreads() REQUIRES_SHARED(Locks::mutator_lock_);
void StartSignalCatcher();
void MaybeSaveJitProfilingInfo();
@@ -1138,10 +1232,11 @@
ThreadPool* AcquireThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_);
void ReleaseThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_);
- // Parses /apex/apex-info-list.xml to initialize a string containing versions
- // of boot classpath jars and encoded into .oat files.
+ // Caches the apex versions produced by `GetApexVersions`.
void InitializeApexVersions();
+ void AppendToBootClassPath(const std::string& filename, const std::string& location);
+
// A pointer to the active runtime or null.
static Runtime* instance_;
@@ -1219,6 +1314,10 @@
// Shared linear alloc for now.
std::unique_ptr<LinearAlloc> linear_alloc_;
+ // Linear alloc used for allocations during startup. Will be deleted after
+ // startup.
+ std::unique_ptr<LinearAlloc> startup_linear_alloc_;
+
// The number of spins that are done before thread suspension is used to forcibly inflate.
size_t max_spins_before_thin_lock_inflation_;
MonitorList* monitor_list_;
@@ -1232,7 +1331,7 @@
SignalCatcher* signal_catcher_;
- SmallIrtAllocator* small_irt_allocator_;
+ jni::SmallLrtAllocator* small_lrt_allocator_;
std::unique_ptr<jni::JniIdManager> jni_id_manager_;
@@ -1349,7 +1448,7 @@
bool non_standard_exits_enabled_;
// Whether Java code needs to be debuggable.
- bool is_java_debuggable_;
+ RuntimeDebugState runtime_debug_state_;
bool monitor_timeout_enable_;
uint64_t monitor_timeout_ns_;
@@ -1454,6 +1553,9 @@
// True if files in /data/misc/apexdata/com.android.art are considered untrustworthy.
bool deny_art_apex_data_files_;
+ // Whether to allow compiling the boot classpath in memory when the given boot image is unusable.
+ bool allow_in_memory_compilation_ = false;
+
// Saved environment.
class EnvSnapshot {
public:
@@ -1490,6 +1592,9 @@
bool perfetto_hprof_enabled_;
bool perfetto_javaheapprof_enabled_;
+ // Called on out of memory error
+ void (*out_of_memory_error_hook_)();
+
metrics::ArtMetrics metrics_;
std::unique_ptr<metrics::MetricsReporter> metrics_reporter_;
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 753ac28..28c81a2 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -105,9 +105,9 @@
Remove(cb, &method_inspection_callbacks_);
}
-bool RuntimeCallbacks::IsMethodBeingInspected(ArtMethod* m) {
+bool RuntimeCallbacks::HaveLocalsChanged() {
for (MethodInspectionCallback* cb : COPY(method_inspection_callbacks_)) {
- if (cb->IsMethodBeingInspected(m)) {
+ if (cb->HaveLocalsChanged()) {
return true;
}
}
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index b1a7e55..98584a8 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -143,9 +143,8 @@
public:
virtual ~MethodInspectionCallback() {}
- // Returns true if the method is being inspected currently and the runtime should not modify it in
- // potentially dangerous ways (i.e. replace with compiled version, JIT it, etc).
- virtual bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ // Returns true if any locals have changed. If any locals have changed we shouldn't OSR.
+ virtual bool HaveLocalsChanged() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
// Callback to let something request to be notified when reflective objects are being visited and
@@ -225,9 +224,9 @@
void AddParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
- // Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
- // on by some code.
- bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if any locals have changed. This is used to prevent OSRing frames that have
+ // some locals changed.
+ bool HaveLocalsChanged() REQUIRES_SHARED(Locks::mutator_lock_);
void AddMethodInspectionCallback(MethodInspectionCallback* cb)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 7f64721..6f0b8a1 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -27,7 +27,7 @@
#include "jni.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/mem_map.h"
#include "base/mutex.h"
#include "class_linker.h"
@@ -35,7 +35,7 @@
#include "dex/class_reference.h"
#include "handle.h"
#include "handle_scope-inl.h"
-#include "mirror/class-inl.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/class_loader.h"
#include "monitor-inl.h"
#include "nativehelper/scoped_local_ref.h"
@@ -44,7 +44,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "thread_list.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -164,40 +164,34 @@
cb_.state = CallbackState::kBase; // Ignore main thread attach.
- {
- ScopedObjectAccess soa(self);
- MakeExecutable(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread));
- }
+ ScopedObjectAccess soa(self);
+ MakeExecutable(WellKnownClasses::java_lang_Thread.Get());
- JNIEnv* env = self->GetJniEnv();
+ StackHandleScope<3u> hs(self);
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(self, "ThreadLifecycleCallback test thread"));
+ ASSERT_TRUE(thread_name != nullptr);
- ScopedLocalRef<jobject> thread_name(env,
- env->NewStringUTF("ThreadLifecycleCallback test thread"));
- ASSERT_TRUE(thread_name.get() != nullptr);
+ Handle<mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<mirror::Object>(runtime_->GetMainThreadGroup()));
+ Handle<mirror::Object> thread =
+ WellKnownClasses::java_lang_Thread_init->NewObject<'L', 'L', 'I', 'Z'>(
+ hs, self, thread_group, thread_name, kMinThreadPriority, /*daemon=*/ false);
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_TRUE(thread != nullptr);
- ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- ASSERT_TRUE(thread.get() != nullptr);
+ ArtMethod* start_method =
+ thread->GetClass()->FindClassMethod("start", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(start_method != nullptr);
- env->CallNonvirtualVoidMethod(thread.get(),
- WellKnownClasses::java_lang_Thread,
- WellKnownClasses::java_lang_Thread_init,
- runtime_->GetMainThreadGroup(),
- thread_name.get(),
- kMinThreadPriority,
- JNI_FALSE);
- ASSERT_FALSE(env->ExceptionCheck());
+ start_method->InvokeVirtual<'V'>(self, thread.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
- jmethodID start_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "start", "()V");
- ASSERT_TRUE(start_id != nullptr);
+ ArtMethod* join_method = thread->GetClass()->FindClassMethod("join", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(join_method != nullptr);
- env->CallVoidMethod(thread.get(), start_id);
- ASSERT_FALSE(env->ExceptionCheck());
-
- jmethodID join_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "join", "()V");
- ASSERT_TRUE(join_id != nullptr);
-
- env->CallVoidMethod(thread.get(), join_id);
- ASSERT_FALSE(env->ExceptionCheck());
+ join_method->InvokeFinal<'V'>(self, thread.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
EXPECT_EQ(cb_.state, CallbackState::kDied);
}
@@ -303,6 +297,7 @@
TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) {
ScopedObjectAccess soa(Thread::Current());
jobject jclass_loader = LoadDex("XandY");
+ cb_.data.clear(); // Clear class loading records from `LoadDex()`, if any.
VariableSizedHandleScope hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader)));
@@ -514,12 +509,11 @@
ASSERT_TRUE(started);
{
ScopedObjectAccess soa(self);
- cb_.SetInterestingObject(
- soa.Decode<mirror::Class>(WellKnownClasses::java_util_Collections));
+ cb_.SetInterestingObject(WellKnownClasses::java_util_Collections.Get());
Monitor::Wait(
self,
// Just a random class
- soa.Decode<mirror::Class>(WellKnownClasses::java_util_Collections),
+ WellKnownClasses::java_util_Collections.Get(),
/*ms=*/0,
/*ns=*/0,
/*interruptShouldThrow=*/false,
diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h
index 925594e..ec08907 100644
--- a/runtime/runtime_common.h
+++ b/runtime/runtime_common.h
@@ -42,7 +42,7 @@
void Dump(std::ostream& os) const {
// This is a backtrace from a crash, do not skip any frames in case the
// crash is in the unwinder itself.
- DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_, false);
+ DumpNativeStack(os, GetTid(), "\t", nullptr, raw_context_, false);
}
private:
// Stores the context of the signal that was unexpected and will terminate the runtime. The
diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc
new file mode 100644
index 0000000..462374a
--- /dev/null
+++ b/runtime/runtime_image.cc
@@ -0,0 +1,1314 @@
+/*
+ * Copyright (C) 2022 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 "runtime_image.h"
+
+#include <lz4.h>
+#include <sstream>
+#include <unistd.h>
+
+#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+
+#include "base/arena_allocator.h"
+#include "base/arena_containers.h"
+#include "base/bit_utils.h"
+#include "base/file_utils.h"
+#include "base/length_prefixed_array.h"
+#include "base/stl_util.h"
+#include "base/unix_file/fd_file.h"
+#include "base/utils.h"
+#include "class_loader_context.h"
+#include "class_loader_utils.h"
+#include "class_root-inl.h"
+#include "gc/space/image_space.h"
+#include "image.h"
+#include "mirror/object-inl.h"
+#include "mirror/object-refvisitor-inl.h"
+#include "mirror/object_array-alloc-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object_array.h"
+#include "mirror/string-inl.h"
+#include "oat.h"
+#include "scoped_thread_state_change-inl.h"
+#include "vdex_file.h"
+
+namespace art {
+
+/**
+ * The native data structures that we store in the image.
+ */
+enum class NativeRelocationKind {
+ kArtFieldArray,
+ kArtMethodArray,
+ kArtMethod,
+ kImTable,
+};
+
+/**
+ * Helper class to generate an app image at runtime.
+ */
+class RuntimeImageHelper {
+ public:
+ explicit RuntimeImageHelper(gc::Heap* heap) :
+ sections_(ImageHeader::kSectionCount),
+ boot_image_begin_(heap->GetBootImagesStartAddress()),
+ boot_image_size_(heap->GetBootImagesSize()),
+ image_begin_(boot_image_begin_ + boot_image_size_),
+ // Note: image relocation considers the image header in the bitmap.
+ object_section_size_(sizeof(ImageHeader)),
+ intern_table_(InternStringHash(this), InternStringEquals(this)),
+ class_table_(ClassDescriptorHash(this), ClassDescriptorEquals()) {}
+
+
+ bool Generate(std::string* error_msg) {
+ if (!WriteObjects(error_msg)) {
+ return false;
+ }
+
+ // Generate the sections information stored in the header.
+ CreateImageSections();
+
+ // Now that all sections have been created and we know their offset and
+ // size, relocate native pointers inside classes and ImTables.
+ RelocateNativePointers();
+
+ // Generate the bitmap section, stored page aligned after the sections data
+ // and of size `object_section_size_` page aligned.
+ size_t sections_end = sections_[ImageHeader::kSectionMetadata].End();
+ image_bitmap_ = gc::accounting::ContinuousSpaceBitmap::Create(
+ "image bitmap",
+ reinterpret_cast<uint8_t*>(image_begin_),
+ RoundUp(object_section_size_, kPageSize));
+ for (uint32_t offset : object_offsets_) {
+ DCHECK(IsAligned<kObjectAlignment>(image_begin_ + sizeof(ImageHeader) + offset));
+ image_bitmap_.Set(
+ reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset));
+ }
+ const size_t bitmap_bytes = image_bitmap_.Size();
+ auto* bitmap_section = §ions_[ImageHeader::kSectionImageBitmap];
+ *bitmap_section = ImageSection(RoundUp(sections_end, kPageSize),
+ RoundUp(bitmap_bytes, kPageSize));
+
+ // Compute boot image checksum and boot image components, to be stored in
+ // the header.
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ uint32_t boot_image_components = 0u;
+ uint32_t boot_image_checksums = 0u;
+ const std::vector<gc::space::ImageSpace*>& image_spaces = heap->GetBootImageSpaces();
+ for (size_t i = 0u, size = image_spaces.size(); i != size; ) {
+ const ImageHeader& header = image_spaces[i]->GetImageHeader();
+ boot_image_components += header.GetComponentCount();
+ boot_image_checksums ^= header.GetImageChecksum();
+ DCHECK_LE(header.GetImageSpaceCount(), size - i);
+ i += header.GetImageSpaceCount();
+ }
+
+ header_ = ImageHeader(
+ /* image_reservation_size= */ RoundUp(sections_end, kPageSize),
+ /* component_count= */ 1,
+ image_begin_,
+ sections_end,
+ sections_.data(),
+ /* image_roots= */ image_begin_ + sizeof(ImageHeader),
+ /* oat_checksum= */ 0,
+ /* oat_file_begin= */ 0,
+ /* oat_data_begin= */ 0,
+ /* oat_data_end= */ 0,
+ /* oat_file_end= */ 0,
+ heap->GetBootImagesStartAddress(),
+ heap->GetBootImagesSize(),
+ boot_image_components,
+ boot_image_checksums,
+ static_cast<uint32_t>(kRuntimePointerSize));
+
+ // Data size includes everything except the bitmap.
+ header_.data_size_ = sections_end;
+
+ // Write image methods - needs to happen after creation of the header.
+ WriteImageMethods();
+
+ return true;
+ }
+
+ const std::vector<uint8_t>& GetObjects() const {
+ return objects_;
+ }
+
+ const std::vector<uint8_t>& GetArtMethods() const {
+ return art_methods_;
+ }
+
+ const std::vector<uint8_t>& GetArtFields() const {
+ return art_fields_;
+ }
+
+ const std::vector<uint8_t>& GetImTables() const {
+ return im_tables_;
+ }
+
+ const ImageHeader& GetHeader() const {
+ return header_;
+ }
+
+ const gc::accounting::ContinuousSpaceBitmap& GetImageBitmap() const {
+ return image_bitmap_;
+ }
+
+ const std::string& GetDexLocation() const {
+ return dex_location_;
+ }
+
+ void GenerateInternData(std::vector<uint8_t>& data) const {
+ intern_table_.WriteToMemory(data.data());
+ }
+
+ void GenerateClassTableData(std::vector<uint8_t>& data) const {
+ class_table_.WriteToMemory(data.data());
+ }
+
+ private:
+ bool IsInBootImage(const void* obj) const {
+ return reinterpret_cast<uintptr_t>(obj) - boot_image_begin_ < boot_image_size_;
+ }
+
+ // Returns a pointer that can be stored in `objects_`:
+ // - The pointer itself for boot image objects,
+ // - The offset in the image for all other objects.
+ mirror::Object* GetOrComputeImageAddress(ObjPtr<mirror::Object> object)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (object == nullptr || IsInBootImage(object.Ptr())) {
+ DCHECK(object == nullptr || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(object));
+ return object.Ptr();
+ }
+
+ if (object->IsClassLoader()) {
+ // DexCache and Class point to class loaders. For runtime-generated app
+ // images, we don't encode the class loader. It will be set when the
+ // runtime is loading the image.
+ return nullptr;
+ }
+
+ if (object->GetClass() == GetClassRoot<mirror::ClassExt>()) {
+ // No need to encode `ClassExt`. If needed, it will be reconstructed at
+ // runtime.
+ return nullptr;
+ }
+
+ uint32_t offset = 0u;
+ if (object->IsClass()) {
+ offset = CopyClass(object->AsClass());
+ } else if (object->IsDexCache()) {
+ offset = CopyDexCache(object->AsDexCache());
+ } else {
+ offset = CopyObject(object);
+ }
+ return reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset);
+ }
+
+ void CreateImageSections() {
+ sections_[ImageHeader::kSectionObjects] = ImageSection(0u, object_section_size_);
+ sections_[ImageHeader::kSectionArtFields] =
+ ImageSection(sections_[ImageHeader::kSectionObjects].End(), art_fields_.size());
+
+ // Round up to the alignment for ArtMethod.
+ static_assert(IsAligned<sizeof(void*)>(ArtMethod::Size(kRuntimePointerSize)));
+ size_t cur_pos = RoundUp(sections_[ImageHeader::kSectionArtFields].End(), sizeof(void*));
+ sections_[ImageHeader::kSectionArtMethods] = ImageSection(cur_pos, art_methods_.size());
+
+ // Round up to the alignment for ImTables.
+ cur_pos = RoundUp(sections_[ImageHeader::kSectionArtMethods].End(), sizeof(void*));
+ sections_[ImageHeader::kSectionImTables] = ImageSection(cur_pos, im_tables_.size());
+
+ // Round up to the alignment for conflict tables.
+ cur_pos = RoundUp(sections_[ImageHeader::kSectionImTables].End(), sizeof(void*));
+ sections_[ImageHeader::kSectionIMTConflictTables] = ImageSection(cur_pos, 0u);
+
+ sections_[ImageHeader::kSectionRuntimeMethods] =
+ ImageSection(sections_[ImageHeader::kSectionIMTConflictTables].End(), 0u);
+
+ // Round up to the alignment the string table expects. See HashSet::WriteToMemory.
+ cur_pos = RoundUp(sections_[ImageHeader::kSectionRuntimeMethods].End(), sizeof(uint64_t));
+
+ size_t intern_table_bytes = intern_table_.WriteToMemory(nullptr);
+ sections_[ImageHeader::kSectionInternedStrings] = ImageSection(cur_pos, intern_table_bytes);
+
+ // Obtain the new position and round it up to the appropriate alignment.
+ cur_pos = RoundUp(sections_[ImageHeader::kSectionInternedStrings].End(), sizeof(uint64_t));
+
+ size_t class_table_bytes = class_table_.WriteToMemory(nullptr);
+ sections_[ImageHeader::kSectionClassTable] = ImageSection(cur_pos, class_table_bytes);
+
+ // Round up to the alignment of the offsets we are going to store.
+ cur_pos = RoundUp(sections_[ImageHeader::kSectionClassTable].End(), sizeof(uint32_t));
+ sections_[ImageHeader::kSectionStringReferenceOffsets] = ImageSection(cur_pos, 0u);
+
+ // Round up to the alignment of the offsets we are going to store.
+ cur_pos =
+ RoundUp(sections_[ImageHeader::kSectionStringReferenceOffsets].End(), sizeof(uint32_t));
+
+ sections_[ImageHeader::kSectionMetadata] = ImageSection(cur_pos, 0u);
+ }
+
+ // Returns the copied mirror Object if in the image, or the object directly if
+ // in the boot image. For the copy, this is really its content, it should not
+ // be returned as an `ObjPtr` (as it's not a GC object), nor stored anywhere.
+ template<typename T> T* FromImageOffsetToRuntimeContent(uint32_t offset) {
+ if (offset == 0u || IsInBootImage(reinterpret_cast<const void*>(offset))) {
+ return reinterpret_cast<T*>(offset);
+ }
+ uint32_t vector_data_offset = FromImageOffsetToVectorOffset(offset);
+ return reinterpret_cast<T*>(objects_.data() + vector_data_offset);
+ }
+
+ uint32_t FromImageOffsetToVectorOffset(uint32_t offset) const {
+ DCHECK(!IsInBootImage(reinterpret_cast<const void*>(offset)));
+ return offset - sizeof(ImageHeader) - image_begin_;
+ }
+
+ class InternStringHash {
+ public:
+ explicit InternStringHash(RuntimeImageHelper* helper) : helper_(helper) {}
+
+ // NO_THREAD_SAFETY_ANALYSIS as these helpers get passed to `HashSet`.
+ size_t operator()(mirror::String* str) const NO_THREAD_SAFETY_ANALYSIS {
+ int32_t hash = str->GetStoredHashCode();
+ DCHECK_EQ(hash, str->ComputeHashCode());
+ // An additional cast to prevent undesired sign extension.
+ return static_cast<uint32_t>(hash);
+ }
+
+ size_t operator()(uint32_t entry) const NO_THREAD_SAFETY_ANALYSIS {
+ return (*this)(helper_->FromImageOffsetToRuntimeContent<mirror::String>(entry));
+ }
+
+ private:
+ RuntimeImageHelper* helper_;
+ };
+
+ class InternStringEquals {
+ public:
+ explicit InternStringEquals(RuntimeImageHelper* helper) : helper_(helper) {}
+
+ // NO_THREAD_SAFETY_ANALYSIS as these helpers get passed to `HashSet`.
+ bool operator()(uint32_t entry, mirror::String* other) const NO_THREAD_SAFETY_ANALYSIS {
+ if (kIsDebugBuild) {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ }
+ return other->Equals(helper_->FromImageOffsetToRuntimeContent<mirror::String>(entry));
+ }
+
+ bool operator()(uint32_t entry, uint32_t other) const NO_THREAD_SAFETY_ANALYSIS {
+ return (*this)(entry, helper_->FromImageOffsetToRuntimeContent<mirror::String>(other));
+ }
+
+ private:
+ RuntimeImageHelper* helper_;
+ };
+
+ using InternTableSet =
+ HashSet<uint32_t, DefaultEmptyFn<uint32_t>, InternStringHash, InternStringEquals>;
+
+ class ClassDescriptorHash {
+ public:
+ explicit ClassDescriptorHash(RuntimeImageHelper* helper) : helper_(helper) {}
+
+ uint32_t operator()(const ClassTable::TableSlot& slot) const NO_THREAD_SAFETY_ANALYSIS {
+ uint32_t ptr = slot.NonHashData();
+ if (helper_->IsInBootImage(reinterpret_cast32<const void*>(ptr))) {
+ return reinterpret_cast32<mirror::Class*>(ptr)->DescriptorHash();
+ }
+ return helper_->class_hashes_[helper_->FromImageOffsetToVectorOffset(ptr)];
+ }
+
+ private:
+ RuntimeImageHelper* helper_;
+ };
+
+ class ClassDescriptorEquals {
+ public:
+ ClassDescriptorEquals() {}
+
+ bool operator()(const ClassTable::TableSlot& a, const ClassTable::TableSlot& b)
+ const NO_THREAD_SAFETY_ANALYSIS {
+ // No need to fetch the descriptor: we know the classes we are inserting
+ // in the ClassTable are unique.
+ return a.Data() == b.Data();
+ }
+ };
+
+ using ClassTableSet = HashSet<ClassTable::TableSlot,
+ ClassTable::TableSlotEmptyFn,
+ ClassDescriptorHash,
+ ClassDescriptorEquals>;
+
+ void VisitDexCache(ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) {
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ // Currently only copy string objects into the image. Populate the intern
+ // table with these strings.
+ for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) {
+ ObjPtr<mirror::String> str = dex_cache->GetResolvedString(dex::StringIndex(i));
+ if (str != nullptr && !IsInBootImage(str.Ptr())) {
+ uint32_t hash = static_cast<uint32_t>(str->GetStoredHashCode());
+ DCHECK_EQ(hash, static_cast<uint32_t>(str->ComputeHashCode()))
+ << "Dex cache strings should be interned";
+ if (intern_table_.FindWithHash(str.Ptr(), hash) == intern_table_.end()) {
+ uint32_t offset = CopyObject(str);
+ intern_table_.InsertWithHash(image_begin_ + offset + sizeof(ImageHeader), hash);
+ }
+ }
+ }
+ }
+
+ // Helper class to collect classes that we will generate in the image.
+ class ClassTableVisitor {
+ public:
+ ClassTableVisitor(Handle<mirror::ClassLoader> loader, VariableSizedHandleScope& handles)
+ : loader_(loader), handles_(handles) {}
+
+ bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Record app classes and boot classpath classes: app classes will be
+ // generated in the image and put in the class table, boot classpath
+ // classes will be put in the class table.
+ ObjPtr<mirror::ClassLoader> class_loader = klass->GetClassLoader();
+ if (class_loader == loader_.Get() || class_loader == nullptr) {
+ handles_.NewHandle(klass);
+ }
+ return true;
+ }
+
+ private:
+ Handle<mirror::ClassLoader> loader_;
+ VariableSizedHandleScope& handles_;
+ };
+
+ // Helper class visitor to filter out classes we cannot emit.
+ class PruneVisitor {
+ public:
+ PruneVisitor(Thread* self,
+ RuntimeImageHelper* helper,
+ const ArenaSet<const DexFile*>& dex_files,
+ ArenaVector<Handle<mirror::Class>>& classes,
+ ArenaAllocator& allocator)
+ : self_(self),
+ helper_(helper),
+ dex_files_(dex_files),
+ visited_(allocator.Adapter()),
+ classes_to_write_(classes) {}
+
+ bool CanEmitHelper(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Only emit classes that are resolved and not erroneous.
+ if (!cls->IsResolved() || cls->IsErroneous()) {
+ return false;
+ }
+
+ // Classes in the boot image can be trivially encoded directly.
+ if (helper_->IsInBootImage(cls.Get())) {
+ return true;
+ }
+
+ // If the class comes from a dex file which is not part of the primary
+ // APK, don't encode it.
+ if (!ContainsElement(dex_files_, &cls->GetDexFile())) {
+ return false;
+ }
+
+ // Ensure pointers to classes in `cls` can also be emitted.
+ StackHandleScope<1> hs(self_);
+ MutableHandle<mirror::Class> other_class = hs.NewHandle(cls->GetSuperClass());
+ if (!CanEmit(other_class)) {
+ return false;
+ }
+
+ other_class.Assign(cls->GetComponentType());
+ if (!CanEmit(other_class)) {
+ return false;
+ }
+
+ for (size_t i = 0, num_interfaces = cls->NumDirectInterfaces(); i < num_interfaces; ++i) {
+ other_class.Assign(cls->GetDirectInterface(i));
+ if (!CanEmit(other_class)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool CanEmit(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (cls == nullptr) {
+ return true;
+ }
+ const dex::ClassDef* class_def = cls->GetClassDef();
+ if (class_def == nullptr) {
+ // Covers array classes and proxy classes.
+ // TODO: Handle these differently.
+ return false;
+ }
+ auto existing = visited_.find(class_def);
+ if (existing != visited_.end()) {
+ // Already processed;
+ return existing->second == VisitState::kCanEmit;
+ }
+
+ visited_.Put(class_def, VisitState::kVisiting);
+ if (CanEmitHelper(cls)) {
+ visited_.Overwrite(class_def, VisitState::kCanEmit);
+ return true;
+ } else {
+ visited_.Overwrite(class_def, VisitState::kCannotEmit);
+ return false;
+ }
+ }
+
+ void Visit(Handle<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ MutableHandle<mirror::Class> cls(obj.GetReference());
+ if (CanEmit(cls)) {
+ if (cls->IsBootStrapClassLoaded()) {
+ DCHECK(helper_->IsInBootImage(cls.Get()));
+ // Insert the bootclasspath class in the class table.
+ uint32_t hash = cls->DescriptorHash();
+ helper_->class_table_.InsertWithHash(ClassTable::TableSlot(cls.Get(), hash), hash);
+ } else {
+ classes_to_write_.push_back(cls);
+ }
+ }
+ }
+
+ private:
+ enum class VisitState {
+ kVisiting,
+ kCanEmit,
+ kCannotEmit,
+ };
+
+ Thread* const self_;
+ RuntimeImageHelper* const helper_;
+ const ArenaSet<const DexFile*>& dex_files_;
+ ArenaSafeMap<const dex::ClassDef*, VisitState> visited_;
+ ArenaVector<Handle<mirror::Class>>& classes_to_write_;
+ };
+
+ void EmitStringsAndClasses(Thread* self,
+ Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArenaAllocator allocator(Runtime::Current()->GetArenaPool());
+ ArenaSet<const DexFile*> dex_files(allocator.Adapter());
+ for (int32_t i = 0; i < dex_cache_array->GetLength(); ++i) {
+ dex_files.insert(dex_cache_array->Get(i)->AsDexCache()->GetDexFile());
+ VisitDexCache(ObjPtr<mirror::DexCache>::DownCast((dex_cache_array->Get(i))));
+ }
+
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> loader = hs.NewHandle(
+ dex_cache_array->Get(0)->AsDexCache()->GetClassLoader());
+ ClassTable* const class_table = loader->GetClassTable();
+ if (class_table == nullptr) {
+ return;
+ }
+
+ VariableSizedHandleScope handles(self);
+ {
+ ClassTableVisitor class_table_visitor(loader, handles);
+ class_table->Visit(class_table_visitor);
+ }
+
+ ArenaVector<Handle<mirror::Class>> classes_to_write(allocator.Adapter());
+ classes_to_write.reserve(class_table->Size());
+ {
+ PruneVisitor prune_visitor(self, this, dex_files, classes_to_write, allocator);
+ handles.VisitHandles(prune_visitor);
+ }
+
+ for (Handle<mirror::Class> cls : classes_to_write) {
+ ScopedAssertNoThreadSuspension sants("Writing class");
+ CopyClass(cls.Get());
+ }
+ }
+
+ // Helper visitor returning the location of a native pointer in the image.
+ class NativePointerVisitor {
+ public:
+ explicit NativePointerVisitor(RuntimeImageHelper* helper) : helper_(helper) {}
+
+ template <typename T>
+ T* operator()(T* ptr, void** dest_addr ATTRIBUTE_UNUSED) const {
+ return helper_->NativeLocationInImage(ptr);
+ }
+
+ template <typename T> T* operator()(T* ptr) const {
+ return helper_->NativeLocationInImage(ptr);
+ }
+
+ private:
+ RuntimeImageHelper* helper_;
+ };
+
+ template <typename T> T* NativeLocationInImage(T* ptr) const {
+ if (ptr == nullptr || IsInBootImage(ptr)) {
+ return ptr;
+ }
+
+ auto it = native_relocations_.find(ptr);
+ DCHECK(it != native_relocations_.end());
+ switch (it->second.first) {
+ case NativeRelocationKind::kArtMethod:
+ case NativeRelocationKind::kArtMethodArray: {
+ uint32_t offset = sections_[ImageHeader::kSectionArtMethods].Offset();
+ return reinterpret_cast<T*>(image_begin_ + offset + it->second.second);
+ }
+ case NativeRelocationKind::kArtFieldArray: {
+ uint32_t offset = sections_[ImageHeader::kSectionArtFields].Offset();
+ return reinterpret_cast<T*>(image_begin_ + offset + it->second.second);
+ }
+ case NativeRelocationKind::kImTable: {
+ uint32_t offset = sections_[ImageHeader::kSectionImTables].Offset();
+ return reinterpret_cast<T*>(image_begin_ + offset + it->second.second);
+ }
+ }
+ }
+
+ template <typename Visitor>
+ void RelocateMethodPointerArrays(mirror::Class* klass, const Visitor& visitor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // A bit of magic here: we cast contents from our buffer to mirror::Class,
+ // and do pointer comparison between 1) these classes, and 2) boot image objects.
+ // Both kinds do not move.
+
+ // See if we need to fixup the vtable field.
+ mirror::Class* super = FromImageOffsetToRuntimeContent<mirror::Class>(
+ reinterpret_cast32<uint32_t>(
+ klass->GetSuperClass<kVerifyNone, kWithoutReadBarrier>().Ptr()));
+ DCHECK(super != nullptr) << "j.l.Object should never be in an app runtime image";
+ mirror::PointerArray* vtable = FromImageOffsetToRuntimeContent<mirror::PointerArray>(
+ reinterpret_cast32<uint32_t>(klass->GetVTable<kVerifyNone, kWithoutReadBarrier>().Ptr()));
+ mirror::PointerArray* super_vtable = FromImageOffsetToRuntimeContent<mirror::PointerArray>(
+ reinterpret_cast32<uint32_t>(super->GetVTable<kVerifyNone, kWithoutReadBarrier>().Ptr()));
+ if (vtable != nullptr && vtable != super_vtable) {
+ DCHECK(!IsInBootImage(vtable));
+ vtable->Fixup(vtable, kRuntimePointerSize, visitor);
+ }
+
+ // See if we need to fixup entries in the IfTable.
+ mirror::IfTable* iftable = FromImageOffsetToRuntimeContent<mirror::IfTable>(
+ reinterpret_cast32<uint32_t>(
+ klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>().Ptr()));
+ mirror::IfTable* super_iftable = FromImageOffsetToRuntimeContent<mirror::IfTable>(
+ reinterpret_cast32<uint32_t>(
+ super->GetIfTable<kVerifyNone, kWithoutReadBarrier>().Ptr()));
+ int32_t iftable_count = iftable->Count();
+ int32_t super_iftable_count = super_iftable->Count();
+ for (int32_t i = 0; i < iftable_count; ++i) {
+ mirror::PointerArray* methods = FromImageOffsetToRuntimeContent<mirror::PointerArray>(
+ reinterpret_cast32<uint32_t>(
+ iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i).Ptr()));
+ mirror::PointerArray* super_methods = (i < super_iftable_count)
+ ? FromImageOffsetToRuntimeContent<mirror::PointerArray>(
+ reinterpret_cast32<uint32_t>(
+ super_iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i).Ptr()))
+ : nullptr;
+ if (methods != super_methods) {
+ DCHECK(!IsInBootImage(methods));
+ methods->Fixup(methods, kRuntimePointerSize, visitor);
+ }
+ }
+ }
+
+ void RelocateNativePointers() {
+ ScopedObjectAccess soa(Thread::Current());
+ NativePointerVisitor visitor(this);
+ for (auto it : classes_) {
+ mirror::Class* cls = reinterpret_cast<mirror::Class*>(&objects_[it.second]);
+ cls->FixupNativePointers(cls, kRuntimePointerSize, visitor);
+ RelocateMethodPointerArrays(cls, visitor);
+ }
+ for (auto it : native_relocations_) {
+ if (it.second.first == NativeRelocationKind::kImTable) {
+ ImTable* im_table = reinterpret_cast<ImTable*>(im_tables_.data() + it.second.second);
+ RelocateImTable(im_table, visitor);
+ }
+ }
+ }
+
+ void RelocateImTable(ImTable* im_table, const NativePointerVisitor& visitor) {
+ for (size_t i = 0; i < ImTable::kSize; ++i) {
+ ArtMethod* method = im_table->Get(i, kRuntimePointerSize);
+ ArtMethod* new_method = nullptr;
+ if (method->IsRuntimeMethod() && !IsInBootImage(method)) {
+ // New IMT conflict method: just use the boot image version.
+ // TODO: Consider copying the new IMT conflict method.
+ new_method = Runtime::Current()->GetImtConflictMethod();
+ DCHECK(IsInBootImage(new_method));
+ } else {
+ new_method = visitor(method);
+ }
+ if (method != new_method) {
+ im_table->Set(i, new_method, kRuntimePointerSize);
+ }
+ }
+ }
+
+ void CopyFieldArrays(ObjPtr<mirror::Class> cls, uint32_t class_image_address)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ LengthPrefixedArray<ArtField>* fields[] = {
+ cls->GetSFieldsPtr(), cls->GetIFieldsPtr(),
+ };
+ for (LengthPrefixedArray<ArtField>* cur_fields : fields) {
+ if (cur_fields != nullptr) {
+ // Copy the array.
+ size_t number_of_fields = cur_fields->size();
+ size_t size = LengthPrefixedArray<ArtField>::ComputeSize(number_of_fields);
+ size_t offset = art_fields_.size();
+ art_fields_.resize(offset + size);
+ auto* dest_array =
+ reinterpret_cast<LengthPrefixedArray<ArtField>*>(art_fields_.data() + offset);
+ memcpy(dest_array, cur_fields, size);
+ native_relocations_[cur_fields] =
+ std::make_pair(NativeRelocationKind::kArtFieldArray, offset);
+
+ // Update the class pointer of individual fields.
+ for (size_t i = 0; i != number_of_fields; ++i) {
+ dest_array->At(i).GetDeclaringClassAddressWithoutBarrier()->Assign(
+ reinterpret_cast<mirror::Class*>(class_image_address));
+ }
+ }
+ }
+ }
+
+ void CopyMethodArrays(ObjPtr<mirror::Class> cls, uint32_t class_image_address)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ size_t number_of_methods = cls->NumMethods();
+ if (number_of_methods == 0) {
+ return;
+ }
+
+ size_t size = LengthPrefixedArray<ArtMethod>::ComputeSize(number_of_methods);
+ size_t offset = art_methods_.size();
+ art_methods_.resize(offset + size);
+ auto* dest_array =
+ reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(art_methods_.data() + offset);
+ memcpy(dest_array, cls->GetMethodsPtr(), size);
+ native_relocations_[cls->GetMethodsPtr()] =
+ std::make_pair(NativeRelocationKind::kArtMethodArray, offset);
+
+ for (size_t i = 0; i != number_of_methods; ++i) {
+ ArtMethod* method = &cls->GetMethodsPtr()->At(i);
+ ArtMethod* copy = &dest_array->At(i);
+
+ // Update the class pointer.
+ ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
+ if (declaring_class == cls) {
+ copy->GetDeclaringClassAddressWithoutBarrier()->Assign(
+ reinterpret_cast<mirror::Class*>(class_image_address));
+ } else {
+ DCHECK(method->IsCopied());
+ if (!IsInBootImage(declaring_class.Ptr())) {
+ DCHECK(classes_.find(declaring_class->GetClassDef()) != classes_.end());
+ copy->GetDeclaringClassAddressWithoutBarrier()->Assign(
+ reinterpret_cast<mirror::Class*>(
+ image_begin_ + sizeof(ImageHeader) + classes_[declaring_class->GetClassDef()]));
+ }
+ }
+
+ // Record the native relocation of the method.
+ uintptr_t copy_offset =
+ reinterpret_cast<uintptr_t>(copy) - reinterpret_cast<uintptr_t>(art_methods_.data());
+ native_relocations_[method] = std::make_pair(NativeRelocationKind::kArtMethod, copy_offset);
+
+ // Ignore the single-implementation info for abstract method.
+ if (method->IsAbstract()) {
+ copy->SetHasSingleImplementation(false);
+ copy->SetSingleImplementation(nullptr, kRuntimePointerSize);
+ }
+
+ // Set the entrypoint and data pointer of the method.
+ StubType stub;
+ if (method->IsNative()) {
+ stub = StubType::kQuickGenericJNITrampoline;
+ } else if (cls->IsVerified() &&
+ method->SkipAccessChecks() &&
+ !method->NeedsClinitCheckBeforeCall()) {
+ stub = StubType::kNterpTrampoline;
+ } else {
+ stub = StubType::kQuickToInterpreterBridge;
+ }
+ const std::vector<gc::space::ImageSpace*>& image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ DCHECK(!image_spaces.empty());
+ const OatFile* oat_file = image_spaces[0]->GetOatFile();
+ DCHECK(oat_file != nullptr);
+ const OatHeader& header = oat_file->GetOatHeader();
+ copy->SetEntryPointFromQuickCompiledCode(header.GetOatAddress(stub));
+
+ if (method->IsNative()) {
+ StubType stub_type = method->IsCriticalNative()
+ ? StubType::kJNIDlsymLookupCriticalTrampoline
+ : StubType::kJNIDlsymLookupTrampoline;
+ copy->SetEntryPointFromJni(header.GetOatAddress(stub_type));
+ } else if (method->IsInvokable()) {
+ DCHECK(method->HasCodeItem()) << method->PrettyMethod();
+ ptrdiff_t code_item_offset = reinterpret_cast<const uint8_t*>(method->GetCodeItem()) -
+ method->GetDexFile()->DataBegin();
+ copy->SetDataPtrSize(
+ reinterpret_cast<const void*>(code_item_offset), kRuntimePointerSize);
+ }
+ }
+ }
+
+ void CopyImTable(ObjPtr<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ImTable* table = cls->GetImt(kRuntimePointerSize);
+
+ // If the table is null or shared and/or already emitted, we can skip.
+ if (table == nullptr || IsInBootImage(table) || HasNativeRelocation(table)) {
+ return;
+ }
+ const size_t size = ImTable::SizeInBytes(kRuntimePointerSize);
+ size_t offset = im_tables_.size();
+ im_tables_.resize(offset + size);
+ uint8_t* dest = im_tables_.data() + offset;
+ memcpy(dest, table, size);
+ native_relocations_[table] = std::make_pair(NativeRelocationKind::kImTable, offset);
+ }
+
+ bool HasNativeRelocation(void* ptr) const {
+ return native_relocations_.find(ptr) != native_relocations_.end();
+ }
+
+ bool WriteObjects(std::string* error_msg) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ScopedObjectAccess soa(Thread::Current());
+ VariableSizedHandleScope handles(soa.Self());
+
+ Handle<mirror::Class> object_array_class = handles.NewHandle(
+ GetClassRoot<mirror::ObjectArray<mirror::Object>>(class_linker));
+
+ Handle<mirror::ObjectArray<mirror::Object>> image_roots = handles.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(
+ soa.Self(), object_array_class.Get(), ImageHeader::kImageRootsMax));
+
+ if (image_roots == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ *error_msg = "Out of memory when trying to generate a runtime app image";
+ return false;
+ }
+
+ // Find the dex files that will be used for generating the app image.
+ dchecked_vector<Handle<mirror::DexCache>> dex_caches;
+ FindDexCaches(soa.Self(), dex_caches, handles);
+
+ if (dex_caches.size() == 0) {
+ *error_msg = "Did not find dex caches to generate an app image";
+ return false;
+ }
+ const OatDexFile* oat_dex_file = dex_caches[0]->GetDexFile()->GetOatDexFile();
+ VdexFile* vdex_file = oat_dex_file->GetOatFile()->GetVdexFile();
+ // The first entry in `dex_caches` contains the location of the primary APK.
+ dex_location_ = oat_dex_file->GetDexFileLocation();
+
+ size_t number_of_dex_files = vdex_file->GetNumberOfDexFiles();
+ if (number_of_dex_files != dex_caches.size()) {
+ // This means some dex files haven't been executed. For simplicity, just
+ // register them and recollect dex caches.
+ Handle<mirror::ClassLoader> loader = handles.NewHandle(dex_caches[0]->GetClassLoader());
+ VisitClassLoaderDexFiles(soa.Self(), loader, [&](const art::DexFile* dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ class_linker->RegisterDexFile(*dex_file, dex_caches[0]->GetClassLoader());
+ return true; // Continue with other dex files.
+ });
+ dex_caches.clear();
+ FindDexCaches(soa.Self(), dex_caches, handles);
+ if (number_of_dex_files != dex_caches.size()) {
+ *error_msg = "Number of dex caches does not match number of dex files in the primary APK";
+ return false;
+ }
+ }
+
+ // Create and populate the checksums aray.
+ Handle<mirror::IntArray> checksums_array = handles.NewHandle(
+ mirror::IntArray::Alloc(soa.Self(), number_of_dex_files));
+
+ if (checksums_array == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ *error_msg = "Out of memory when trying to generate a runtime app image";
+ return false;
+ }
+
+ const VdexFile::VdexChecksum* checksums = vdex_file->GetDexChecksumsArray();
+ static_assert(sizeof(VdexFile::VdexChecksum) == sizeof(int32_t));
+ for (uint32_t i = 0; i < number_of_dex_files; ++i) {
+ checksums_array->Set(i, checksums[i]);
+ }
+
+ // Create and populate the dex caches aray.
+ Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array = handles.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(
+ soa.Self(), object_array_class.Get(), dex_caches.size()));
+
+ if (dex_cache_array == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ *error_msg = "Out of memory when trying to generate a runtime app image";
+ return false;
+ }
+
+ for (uint32_t i = 0; i < dex_caches.size(); ++i) {
+ dex_cache_array->Set(i, dex_caches[i].Get());
+ }
+
+ // Create the special roots array.
+ Handle<mirror::ObjectArray<mirror::Object>> special_array = handles.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), object_array_class.Get(), 2));
+
+ ObjPtr<mirror::String> str = mirror::String::AllocFromModifiedUtf8(
+ soa.Self(), oat_dex_file->GetOatFile()->GetClassLoaderContext().c_str());
+ if (str == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ *error_msg = "Out of memory when trying to generate a runtime app image";
+ return false;
+ }
+ special_array->Set(0, str);
+ special_array->Set(1, checksums_array.Get());
+
+ image_roots->Set(ImageHeader::kDexCaches, dex_cache_array.Get());
+ image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots());
+ image_roots->Set(ImageHeader::kAppImageContextAndDexChecksums, special_array.Get());
+
+ {
+ // Now that we have created all objects needed for the `image_roots`, copy
+ // it into the buffer. Note that this will recursively copy all objects
+ // contained in `image_roots`. That's acceptable as we don't have cycles,
+ // nor a deep graph.
+ ScopedAssertNoThreadSuspension sants("Writing runtime app image");
+ CopyObject(image_roots.Get());
+ }
+
+ // Emit string referenced in dex caches, and classes defined in the app class loader.
+ EmitStringsAndClasses(soa.Self(), dex_cache_array);
+
+ return true;
+ }
+
+ class FixupVisitor {
+ public:
+ FixupVisitor(RuntimeImageHelper* image, size_t copy_offset)
+ : image_(image), copy_offset_(copy_offset) {}
+
+ // We do not visit native roots. These are handled with other logic.
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+ const {
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {
+ LOG(FATAL) << "UNREACHABLE";
+ }
+
+ void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // We don't copy static fields, instead classes will be marked as resolved
+ // and initialized at runtime.
+ ObjPtr<mirror::Object> ref =
+ is_static ? nullptr : obj->GetFieldObject<mirror::Object>(offset);
+ mirror::Object* address = image_->GetOrComputeImageAddress(ref.Ptr());
+ mirror::Object* copy =
+ reinterpret_cast<mirror::Object*>(image_->objects_.data() + copy_offset_);
+ copy->GetFieldObjectReferenceAddr<kVerifyNone>(offset)->Assign(address);
+ }
+
+ // java.lang.ref.Reference visitor.
+ void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false);
+ }
+
+ private:
+ RuntimeImageHelper* image_;
+ size_t copy_offset_;
+ };
+
+ uint32_t CopyDexCache(ObjPtr<mirror::DexCache> cache) REQUIRES_SHARED(Locks::mutator_lock_) {
+ auto it = dex_caches_.find(cache->GetDexFile());
+ if (it != dex_caches_.end()) {
+ return it->second;
+ }
+ uint32_t offset = CopyObject(cache);
+ dex_caches_[cache->GetDexFile()] = offset;
+ // For dex caches, clear pointers to data that will be set at runtime.
+ mirror::Object* copy = reinterpret_cast<mirror::Object*>(objects_.data() + offset);
+ reinterpret_cast<mirror::DexCache*>(copy)->ResetNativeArrays();
+ reinterpret_cast<mirror::DexCache*>(copy)->SetDexFile(nullptr);
+ return offset;
+ }
+
+ uint32_t CopyClass(ObjPtr<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) {
+ const dex::ClassDef* class_def = cls->GetClassDef();
+ auto it = classes_.find(class_def);
+ if (it != classes_.end()) {
+ return it->second;
+ }
+ uint32_t offset = CopyObject(cls);
+ classes_[class_def] = offset;
+
+ uint32_t hash = cls->DescriptorHash();
+ // Save the hash, the `HashSet` implementation requires to find it.
+ class_hashes_[offset] = hash;
+ uint32_t class_image_address = image_begin_ + sizeof(ImageHeader) + offset;
+ bool inserted =
+ class_table_.InsertWithHash(ClassTable::TableSlot(class_image_address, hash), hash).second;
+ DCHECK(inserted) << "Class " << cls->PrettyDescriptor()
+ << " (" << cls.Ptr() << ") already inserted";
+
+ // Clear internal state.
+ mirror::Class* copy = reinterpret_cast<mirror::Class*>(objects_.data() + offset);
+ copy->SetClinitThreadId(static_cast<pid_t>(0u));
+ copy->SetStatusInternal(cls->IsVerified() ? ClassStatus::kVerified : ClassStatus::kResolved);
+ copy->SetObjectSizeAllocFastPath(std::numeric_limits<uint32_t>::max());
+ copy->SetAccessFlags(copy->GetAccessFlags() & ~kAccRecursivelyInitialized);
+
+ // Clear static field values.
+ MemberOffset static_offset = cls->GetFirstReferenceStaticFieldOffset(kRuntimePointerSize);
+ memset(objects_.data() + offset + static_offset.Uint32Value(),
+ 0,
+ cls->GetClassSize() - static_offset.Uint32Value());
+
+ CopyFieldArrays(cls, class_image_address);
+ CopyMethodArrays(cls, class_image_address);
+ if (cls->ShouldHaveImt()) {
+ CopyImTable(cls);
+ }
+
+ return offset;
+ }
+
+ // Copy `obj` in `objects_` and relocate references. Returns the offset
+ // within our buffer.
+ uint32_t CopyObject(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Copy the object in `objects_`.
+ size_t object_size = obj->SizeOf();
+ size_t offset = objects_.size();
+ DCHECK(IsAligned<kObjectAlignment>(offset));
+ object_offsets_.push_back(offset);
+ objects_.resize(RoundUp(offset + object_size, kObjectAlignment));
+ memcpy(objects_.data() + offset, obj.Ptr(), object_size);
+ object_section_size_ += RoundUp(object_size, kObjectAlignment);
+
+ // Fixup reference pointers.
+ FixupVisitor visitor(this, offset);
+ obj->VisitReferences</*kVisitNativeRoots=*/ false>(visitor, visitor);
+
+ mirror::Object* copy = reinterpret_cast<mirror::Object*>(objects_.data() + offset);
+
+ // Clear any lockword data.
+ copy->SetLockWord(LockWord::Default(), /* as_volatile= */ false);
+
+ if (obj->IsString()) {
+ // Ensure a string always has a hashcode stored. This is checked at
+ // runtime because boot images don't want strings dirtied due to hashcode.
+ reinterpret_cast<mirror::String*>(copy)->GetHashCode();
+ }
+ return offset;
+ }
+
+ class CollectDexCacheVisitor : public DexCacheVisitor {
+ public:
+ explicit CollectDexCacheVisitor(VariableSizedHandleScope& handles) : handles_(handles) {}
+
+ void Visit(ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_) override {
+ dex_caches_.push_back(handles_.NewHandle(dex_cache));
+ }
+ const std::vector<Handle<mirror::DexCache>>& GetDexCaches() const {
+ return dex_caches_;
+ }
+ private:
+ VariableSizedHandleScope& handles_;
+ std::vector<Handle<mirror::DexCache>> dex_caches_;
+ };
+
+ // Find dex caches corresponding to the primary APK.
+ void FindDexCaches(Thread* self,
+ dchecked_vector<Handle<mirror::DexCache>>& dex_caches,
+ VariableSizedHandleScope& handles)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(dex_caches.empty());
+ // Collect all dex caches.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ CollectDexCacheVisitor visitor(handles);
+ {
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ class_linker->VisitDexCaches(&visitor);
+ }
+
+ // Find the primary APK.
+ AppInfo* app_info = Runtime::Current()->GetAppInfo();
+ for (Handle<mirror::DexCache> cache : visitor.GetDexCaches()) {
+ if (app_info->GetRegisteredCodeType(cache->GetDexFile()->GetLocation()) ==
+ AppInfo::CodeType::kPrimaryApk) {
+ dex_caches.push_back(handles.NewHandle(cache.Get()));
+ break;
+ }
+ }
+
+ if (dex_caches.empty()) {
+ return;
+ }
+
+ const OatDexFile* oat_dex_file = dex_caches[0]->GetDexFile()->GetOatDexFile();
+ if (oat_dex_file == nullptr) {
+ // We need a .oat file for loading an app image;
+ dex_caches.clear();
+ return;
+ }
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ for (Handle<mirror::DexCache> cache : visitor.GetDexCaches()) {
+ if (cache.Get() != dex_caches[0].Get()) {
+ const OatDexFile* other_oat_dex_file = cache->GetDexFile()->GetOatDexFile();
+ if (other_oat_dex_file != nullptr && other_oat_dex_file->GetOatFile() == oat_file) {
+ dex_caches.push_back(handles.NewHandle(cache.Get()));
+ }
+ }
+ }
+ }
+
+ static uint64_t PointerToUint64(void* ptr) {
+ return reinterpret_cast64<uint64_t>(ptr);
+ }
+
+ void WriteImageMethods() {
+ ScopedObjectAccess soa(Thread::Current());
+ // We can just use plain runtime pointers.
+ Runtime* runtime = Runtime::Current();
+ header_.image_methods_[ImageHeader::kResolutionMethod] =
+ PointerToUint64(runtime->GetResolutionMethod());
+ header_.image_methods_[ImageHeader::kImtConflictMethod] =
+ PointerToUint64(runtime->GetImtConflictMethod());
+ header_.image_methods_[ImageHeader::kImtUnimplementedMethod] =
+ PointerToUint64(runtime->GetImtUnimplementedMethod());
+ header_.image_methods_[ImageHeader::kSaveAllCalleeSavesMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves));
+ header_.image_methods_[ImageHeader::kSaveRefsOnlyMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsOnly));
+ header_.image_methods_[ImageHeader::kSaveRefsAndArgsMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
+ header_.image_methods_[ImageHeader::kSaveEverythingMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything));
+ header_.image_methods_[ImageHeader::kSaveEverythingMethodForClinit] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit));
+ header_.image_methods_[ImageHeader::kSaveEverythingMethodForSuspendCheck] =
+ PointerToUint64(
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck));
+ }
+
+ // Header for the image, created at the end once we know the size of all
+ // sections.
+ ImageHeader header_;
+
+ // Contents of the various sections.
+ std::vector<uint8_t> objects_;
+ std::vector<uint8_t> art_fields_;
+ std::vector<uint8_t> art_methods_;
+ std::vector<uint8_t> im_tables_;
+
+ // Bitmap of live objects in `objects_`. Populated from `object_offsets_`
+ // once we know `object_section_size`.
+ gc::accounting::ContinuousSpaceBitmap image_bitmap_;
+
+ // Sections stored in the header.
+ dchecked_vector<ImageSection> sections_;
+
+ // A list of offsets in `objects_` where objects begin.
+ std::vector<uint32_t> object_offsets_;
+
+ std::map<const dex::ClassDef*, uint32_t> classes_;
+ std::map<const DexFile*, uint32_t> dex_caches_;
+ std::map<uint32_t, uint32_t> class_hashes_;
+
+ std::map<void*, std::pair<NativeRelocationKind, uint32_t>> native_relocations_;
+
+ // Cached values of boot image information.
+ const uint32_t boot_image_begin_;
+ const uint32_t boot_image_size_;
+
+ // Where the image begins: just after the boot image.
+ const uint32_t image_begin_;
+
+ // Size of the `kSectionObjects` section.
+ size_t object_section_size_;
+
+ // The location of the primary APK / dex file.
+ std::string dex_location_;
+
+ // The intern table for strings that we will write to disk.
+ InternTableSet intern_table_;
+
+ // The class table holding classes that we will write to disk.
+ ClassTableSet class_table_;
+
+ friend class ClassDescriptorHash;
+ friend class PruneVisitor;
+ friend class NativePointerVisitor;
+};
+
+std::string RuntimeImage::GetRuntimeImagePath(const std::string& dex_location) {
+ const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory();
+
+ std::string new_location = ReplaceFileExtension(
+ dex_location, (kRuntimePointerSize == PointerSize::k32 ? "art32" : "art64"));
+
+ if (data_dir.empty()) {
+ // The data ditectory is empty for tests.
+ return new_location;
+ } else {
+ std::replace(new_location.begin(), new_location.end(), '/', '@');
+ return data_dir + "/" + new_location;
+ }
+}
+
+bool RuntimeImage::WriteImageToDisk(std::string* error_msg) {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ if (!heap->HasBootImageSpace()) {
+ *error_msg = "Cannot generate an app image without a boot image";
+ return false;
+ }
+ RuntimeImageHelper image(heap);
+ if (!image.Generate(error_msg)) {
+ return false;
+ }
+
+ const std::string path = GetRuntimeImagePath(image.GetDexLocation());
+ // We first generate the app image in a temporary file, which we will then
+ // move to `path`.
+ const std::string temp_path = path + std::to_string(getpid());
+ std::unique_ptr<File> out(OS::CreateEmptyFileWriteOnly(temp_path.c_str()));
+ if (out == nullptr) {
+ *error_msg = "Could not open " + temp_path + " for writing";
+ return false;
+ }
+
+ // Write objects. The header is written at the end in case we get killed.
+ if (out->Write(reinterpret_cast<const char*>(image.GetObjects().data()),
+ image.GetObjects().size(),
+ sizeof(ImageHeader)) != static_cast<int64_t>(image.GetObjects().size())) {
+ *error_msg = "Could not write image data to " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+
+ {
+ // Write fields.
+ auto fields_section = image.GetHeader().GetImageSection(ImageHeader::kSectionArtFields);
+ if (out->Write(reinterpret_cast<const char*>(image.GetArtFields().data()),
+ fields_section.Size(),
+ fields_section.Offset()) != fields_section.Size()) {
+ *error_msg = "Could not write fields section " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+ }
+
+ {
+ // Write methods.
+ auto methods_section = image.GetHeader().GetImageSection(ImageHeader::kSectionArtMethods);
+ if (out->Write(reinterpret_cast<const char*>(image.GetArtMethods().data()),
+ methods_section.Size(),
+ methods_section.Offset()) != methods_section.Size()) {
+ *error_msg = "Could not write methods section " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+ }
+
+ {
+ // Write im tables.
+ auto im_tables_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImTables);
+ if (out->Write(reinterpret_cast<const char*>(image.GetImTables().data()),
+ im_tables_section.Size(),
+ im_tables_section.Offset()) != im_tables_section.Size()) {
+ *error_msg = "Could not write ImTable section " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+ }
+
+ {
+ // Write intern string set.
+ auto intern_section = image.GetHeader().GetImageSection(ImageHeader::kSectionInternedStrings);
+ std::vector<uint8_t> intern_data(intern_section.Size());
+ image.GenerateInternData(intern_data);
+ if (out->Write(reinterpret_cast<const char*>(intern_data.data()),
+ intern_section.Size(),
+ intern_section.Offset()) != intern_section.Size()) {
+ *error_msg = "Could not write intern section " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+ }
+
+ {
+ // Write class table.
+ auto class_table_section = image.GetHeader().GetImageSection(ImageHeader::kSectionClassTable);
+ std::vector<uint8_t> class_table_data(class_table_section.Size());
+ image.GenerateClassTableData(class_table_data);
+ if (out->Write(reinterpret_cast<const char*>(class_table_data.data()),
+ class_table_section.Size(),
+ class_table_section.Offset()) != class_table_section.Size()) {
+ *error_msg = "Could not write class table section " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+ }
+
+ // Write bitmap.
+ auto bitmap_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImageBitmap);
+ if (out->Write(reinterpret_cast<const char*>(image.GetImageBitmap().Begin()),
+ bitmap_section.Size(),
+ bitmap_section.Offset()) != bitmap_section.Size()) {
+ *error_msg = "Could not write image bitmap " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+
+ // Now write header.
+ if (out->Write(reinterpret_cast<const char*>(&image.GetHeader()), sizeof(ImageHeader), 0u) !=
+ sizeof(ImageHeader)) {
+ *error_msg = "Could not write image header to " + temp_path;
+ out->Erase(/*unlink=*/true);
+ return false;
+ }
+
+ if (out->FlushClose() != 0) {
+ *error_msg = "Could not flush and close " + temp_path;
+ // Unlink directly: we cannot use `out` as we may have closed it.
+ unlink(temp_path.c_str());
+ return false;
+ }
+
+ if (rename(temp_path.c_str(), path.c_str()) != 0) {
+ *error_msg =
+ "Failed to move runtime app image to " + path + ": " + std::string(strerror(errno));
+ // Unlink directly: we cannot use `out` as we have closed it.
+ unlink(temp_path.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace art
diff --git a/runtime/runtime_image.h b/runtime/runtime_image.h
new file mode 100644
index 0000000..d494e1c
--- /dev/null
+++ b/runtime/runtime_image.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_RUNTIME_IMAGE_H_
+#define ART_RUNTIME_RUNTIME_IMAGE_H_
+
+#include <string>
+
+namespace art {
+
+class RuntimeImage {
+ public:
+ // Writes an app image for the currently running process.
+ static bool WriteImageToDisk(std::string* error_msg);
+
+ // Gets the path where a runtime-generated app image is stored.
+ static std::string GetRuntimeImagePath(const std::string& dex_location);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_RUNTIME_IMAGE_H_
diff --git a/runtime/runtime_intrinsics.cc b/runtime/runtime_intrinsics.cc
index 1672c49..fb60cfe 100644
--- a/runtime/runtime_intrinsics.cc
+++ b/runtime/runtime_intrinsics.cc
@@ -90,10 +90,10 @@
}
}
-bool AreAllIntrinsicsInitialized() {
- ScopedObjectAccess soa(Thread::Current());
+bool AreAllIntrinsicsInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
#define IS_INTRINSIC_INITIALIZED(Name, InvokeType, _, __, ___, ClassName, MethodName, Signature) \
- IsIntrinsicInitialized(soa.Self(), \
+ IsIntrinsicInitialized(self, \
Intrinsics::k##Name, \
InvokeType, \
ClassName, \
@@ -107,11 +107,11 @@
} // namespace
void InitializeIntrinsics() {
- ScopedObjectAccess soa(Thread::Current());
+ Thread* self = Thread::Current();
// Initialization here uses the short-circuit operator || to stop
// initializing if there's an already initialized intrinsic.
#define INITIALIZE_INTRINSIC(Name, InvokeType, _, __, ___, ClassName, MethodName, Signature) \
- InitializeIntrinsic(soa.Self(), \
+ InitializeIntrinsic(self, \
Intrinsics::k##Name, \
InvokeType, \
ClassName, \
diff --git a/runtime/runtime_intrinsics.h b/runtime/runtime_intrinsics.h
index 98dc9bc..bf2cb2b 100644
--- a/runtime/runtime_intrinsics.h
+++ b/runtime/runtime_intrinsics.h
@@ -17,9 +17,11 @@
#ifndef ART_RUNTIME_RUNTIME_INTRINSICS_H_
#define ART_RUNTIME_RUNTIME_INTRINSICS_H_
+#include "base/locks.h"
+
namespace art {
-void InitializeIntrinsics();
+void InitializeIntrinsics() REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace art
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 6721834..b2fcf7d 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -47,6 +47,7 @@
RUNTIME_OPTIONS_KEY (std::string, ClassPath)
RUNTIME_OPTIONS_KEY (ParseStringList<':'>,Image)
RUNTIME_OPTIONS_KEY (Unit, ForceJitZygote)
+RUNTIME_OPTIONS_KEY (Unit, AllowInMemoryCompilation)
RUNTIME_OPTIONS_KEY (Unit, CheckJni)
RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy)
RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "suspend=n,server=y")
@@ -125,6 +126,7 @@
RUNTIME_OPTIONS_KEY (std::string, MethodTraceFile, "/data/misc/trace/method-trace-file.bin")
RUNTIME_OPTIONS_KEY (unsigned int, MethodTraceFileSize, 10 * MB)
RUNTIME_OPTIONS_KEY (Unit, MethodTraceStreaming)
+RUNTIME_OPTIONS_KEY (TraceClockSource, MethodTraceClock, kDefaultTraceClockSource)
RUNTIME_OPTIONS_KEY (TraceClockSource, ProfileClock, kDefaultTraceClockSource) // -Xprofile:
RUNTIME_OPTIONS_KEY (ProfileSaverOptions, ProfileSaverOpts) // -Xjitsaveprofilinginfo, -Xps-*
RUNTIME_OPTIONS_KEY (std::string, Compiler)
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
index d601952..674d791 100644
--- a/runtime/scoped_thread_state_change-inl.h
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -94,7 +94,7 @@
}
inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
- : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {}
+ : self_(Thread::ForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {}
inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self)
: self_(self),
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 50a96d0..d7d5851 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -129,7 +129,7 @@
GetCurrentQuickFrame(), cur_quick_frame_pc_, abort_on_failure);
} else if (cur_oat_quick_method_header_->IsOptimized()) {
StackMap* stack_map = GetCurrentStackMap();
- DCHECK(stack_map->IsValid());
+ CHECK(stack_map->IsValid()) << "StackMap not found for " << std::hex << cur_quick_frame_pc_;
return stack_map->GetDexPc();
} else {
DCHECK(cur_oat_quick_method_header_->IsNterpMethodHeader());
@@ -140,6 +140,29 @@
}
}
+std::vector<uint32_t> StackVisitor::ComputeDexPcList(uint32_t handler_dex_pc) const {
+ std::vector<uint32_t> result;
+ if (cur_shadow_frame_ == nullptr && cur_quick_frame_ != nullptr && IsInInlinedFrame()) {
+ const BitTableRange<InlineInfo>& infos = current_inline_frames_;
+ DCHECK_NE(infos.size(), 0u);
+
+ // Outermost dex_pc.
+ result.push_back(GetCurrentStackMap()->GetDexPc());
+
+ // The mid dex_pcs. Note that we skip the last one since we want to change that for
+ // `handler_dex_pc`.
+ for (size_t index = 0; index < infos.size() - 1; ++index) {
+ result.push_back(infos[index].GetDexPc());
+ }
+ }
+
+ // The innermost dex_pc has to be the handler dex_pc. In the case of no inline frames, it will be
+ // just the one dex_pc. In the case of inlining we will be replacing the innermost InlineInfo's
+ // dex_pc with this one.
+ result.push_back(handler_dex_pc);
+ return result;
+}
+
extern "C" mirror::Object* artQuickGetProxyThisObject(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -213,7 +236,8 @@
uint16_t vreg,
VRegKind kind,
uint32_t* val,
- std::optional<DexRegisterLocation> location) const {
+ std::optional<DexRegisterLocation> location,
+ bool need_full_register_list) const {
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
@@ -235,10 +259,10 @@
// which does not decode the stack maps.
result = GetVRegFromOptimizedCode(location.value(), val);
// Compare to the slower overload.
- DCHECK_EQ(result, GetVRegFromOptimizedCode(m, vreg, kind, &val2));
+ DCHECK_EQ(result, GetVRegFromOptimizedCode(m, vreg, kind, &val2, need_full_register_list));
DCHECK_EQ(*val, val2);
} else {
- result = GetVRegFromOptimizedCode(m, vreg, kind, val);
+ result = GetVRegFromOptimizedCode(m, vreg, kind, val, need_full_register_list);
}
}
if (kind == kReferenceVReg) {
@@ -261,16 +285,20 @@
}
}
+size_t StackVisitor::GetNumberOfRegisters(CodeInfo* code_info, int depth) const {
+ return depth == 0
+ ? code_info->GetNumberOfDexRegisters()
+ : current_inline_frames_[depth - 1].GetNumberOfDexRegisters();
+}
+
bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m,
uint16_t vreg,
VRegKind kind,
- uint32_t* val) const {
+ uint32_t* val,
+ bool need_full_register_list) const {
DCHECK_EQ(m, GetMethod());
// Can't be null or how would we compile its instructions?
DCHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod();
- CodeItemDataAccessor accessor(m->DexInstructionData());
- uint16_t number_of_dex_registers = accessor.RegistersSize();
- DCHECK_LT(vreg, number_of_dex_registers);
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
CodeInfo code_info(method_header);
@@ -278,13 +306,18 @@
StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
DCHECK(stack_map.IsValid());
- DexRegisterMap dex_register_map = IsInInlinedFrame()
- ? code_info.GetInlineDexRegisterMapOf(stack_map, current_inline_frames_.back())
- : code_info.GetDexRegisterMapOf(stack_map);
+ DexRegisterMap dex_register_map = (IsInInlinedFrame() && !need_full_register_list)
+ ? code_info.GetInlineDexRegisterMapOf(stack_map, current_inline_frames_.back())
+ : code_info.GetDexRegisterMapOf(stack_map,
+ /* first= */ 0,
+ GetNumberOfRegisters(&code_info, InlineDepth()));
+
if (dex_register_map.empty()) {
return false;
}
- DCHECK_EQ(dex_register_map.size(), number_of_dex_registers);
+
+ const size_t number_of_dex_registers = dex_register_map.size();
+ DCHECK_LT(vreg, number_of_dex_registers);
DexRegisterLocation::Kind location_kind = dex_register_map[vreg].GetKind();
switch (location_kind) {
case DexRegisterLocation::Kind::kInStack: {
@@ -745,15 +778,6 @@
// (resolution, instrumentation) trampoline; or
// - fake a Generic JNI frame in art_jni_dlsym_lookup_critical_stub.
DCHECK(method->IsNative());
- if (kIsDebugBuild && !method->IsCriticalNative()) {
- ClassLinker* class_linker = runtime->GetClassLinker();
- const void* entry_point = runtime->GetInstrumentation()->GetCodeForInvoke(method);
- CHECK(class_linker->IsQuickGenericJniStub(entry_point) ||
- // The current entrypoint (after filtering out trampolines) may have changed
- // from GenericJNI to JIT-compiled stub since we have entered this frame.
- (runtime->GetJit() != nullptr &&
- runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod();
- }
// Generic JNI frame is just like the SaveRefsAndArgs frame.
// Note that HandleScope, if any, is below the frame.
return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
@@ -780,7 +804,6 @@
DCHECK(thread_ == Thread::Current() || thread_->IsSuspended());
}
CHECK_EQ(cur_depth_, 0U);
- size_t inlined_frames_count = 0;
for (const ManagedStack* current_fragment = thread_->GetManagedStack();
current_fragment != nullptr; current_fragment = current_fragment->GetLink()) {
@@ -788,6 +811,12 @@
cur_quick_frame_ = current_fragment->GetTopQuickFrame();
cur_quick_frame_pc_ = 0;
DCHECK(cur_oat_quick_method_header_ == nullptr);
+
+ if (kDebugStackWalk) {
+ LOG(INFO) << "Tid=" << thread_-> GetThreadId()
+ << ", ManagedStack fragement: " << current_fragment;
+ }
+
if (cur_quick_frame_ != nullptr) { // Handle quick stack frames.
// Can't be both a shadow and a quick fragment.
DCHECK(current_fragment->GetTopShadowFrame() == nullptr);
@@ -800,18 +829,26 @@
// between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have
// changed since the frame was entered. The top quick frame tag indicates
// GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub.
- if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) {
+ if (UNLIKELY(current_fragment->GetTopQuickFrameGenericJniTag())) {
// The generic JNI does not have any method header.
cur_oat_quick_method_header_ = nullptr;
+ } else if (UNLIKELY(current_fragment->GetTopQuickFrameJitJniTag())) {
+ // Should be JITed code.
+ Runtime* runtime = Runtime::Current();
+ const void* code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
+ CHECK(code != nullptr) << method->PrettyMethod();
+ cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code);
} else {
+ // We are sure we are not running GenericJni here. Though the entry point could still be
+ // GenericJnistub. The entry point is usually JITed or AOT code. It could be lso a
+ // resolution stub if the class isn't visibly initialized yet.
const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode();
CHECK(existing_entry_point != nullptr);
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
// Check whether we can quickly get the header from the current entrypoint.
if (!class_linker->IsQuickGenericJniStub(existing_entry_point) &&
- !class_linker->IsQuickResolutionStub(existing_entry_point) &&
- existing_entry_point != GetQuickInstrumentationEntryPoint()) {
+ !class_linker->IsQuickResolutionStub(existing_entry_point)) {
cur_oat_quick_method_header_ =
OatQuickMethodHeader::FromEntryPoint(existing_entry_point);
} else {
@@ -819,7 +856,11 @@
if (code != nullptr) {
cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code);
} else {
- // This must be a JITted JNI stub frame.
+ // This must be a JITted JNI stub frame. For non-debuggable runtimes we only generate
+ // JIT stubs if there are no AOT stubs for native methods. Since we checked for AOT
+ // code earlier, we must be running JITed code. For debuggable runtimes we might have
+ // JIT code even when AOT code is present but we tag SP in JITed JNI stubs
+ // in debuggable runtimes. This case is handled earlier.
CHECK(runtime->GetJit() != nullptr);
code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
CHECK(code != nullptr) << method->PrettyMethod();
@@ -834,8 +875,12 @@
cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_);
}
header_retrieved = false; // Force header retrieval in next iteration.
- ValidateFrame();
+ if (kDebugStackWalk) {
+ LOG(INFO) << "Early print: Tid=" << thread_-> GetThreadId() << ", method: "
+ << ArtMethod::PrettyMethod(method) << "@" << method;
+ }
+ ValidateFrame();
if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames)
&& (cur_oat_quick_method_header_ != nullptr)
&& cur_oat_quick_method_header_->IsOptimized()
@@ -854,7 +899,6 @@
return;
}
cur_depth_++;
- inlined_frames_count++;
}
}
}
@@ -871,44 +915,14 @@
// Compute PC for next stack frame from return PC.
size_t frame_size = frame_info.FrameSizeInBytes();
uintptr_t return_pc_addr = GetReturnPcAddr();
- uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
- if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == return_pc)) {
- // While profiling, the return pc is restored from the side stack, except when walking
- // the stack for an exception where the side stack will be unwound in VisitFrame.
- const std::map<uintptr_t, instrumentation::InstrumentationStackFrame>&
- instrumentation_stack = *thread_->GetInstrumentationStack();
- auto it = instrumentation_stack.find(return_pc_addr);
- CHECK(it != instrumentation_stack.end());
- const instrumentation::InstrumentationStackFrame& instrumentation_frame = it->second;
- if (GetMethod() ==
- Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves)) {
- // Skip runtime save all callee frames which are used to deliver exceptions.
- } else if (instrumentation_frame.interpreter_entry_) {
- ArtMethod* callee =
- Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs);
- CHECK_EQ(GetMethod(), callee) << "Expected: " << ArtMethod::PrettyMethod(callee)
- << " Found: " << ArtMethod::PrettyMethod(GetMethod());
- } else if (!instrumentation_frame.method_->IsRuntimeMethod()) {
- // Trampolines get replaced with their actual method in the stack,
- // so don't do the check below for runtime methods.
- // Instrumentation generally doesn't distinguish between a method's obsolete and
- // non-obsolete version.
- CHECK_EQ(instrumentation_frame.method_->GetNonObsoleteMethod(),
- GetMethod()->GetNonObsoleteMethod())
- << "Expected: "
- << ArtMethod::PrettyMethod(instrumentation_frame.method_->GetNonObsoleteMethod())
- << " Found: " << ArtMethod::PrettyMethod(GetMethod()->GetNonObsoleteMethod());
- }
- return_pc = instrumentation_frame.return_pc_;
- }
-
- cur_quick_frame_pc_ = return_pc;
+ cur_quick_frame_pc_ = *reinterpret_cast<uintptr_t*>(return_pc_addr);
uint8_t* next_frame = reinterpret_cast<uint8_t*>(cur_quick_frame_) + frame_size;
cur_quick_frame_ = reinterpret_cast<ArtMethod**>(next_frame);
if (kDebugStackWalk) {
- LOG(INFO) << ArtMethod::PrettyMethod(method) << "@" << method << " size=" << frame_size
+ LOG(INFO) << "Tid=" << thread_-> GetThreadId() << ", method: "
+ << ArtMethod::PrettyMethod(method) << "@" << method << " size=" << frame_size
<< std::boolalpha
<< " optimized=" << (cur_oat_quick_method_header_ != nullptr &&
cur_oat_quick_method_header_->IsOptimized())
@@ -928,6 +942,12 @@
cur_oat_quick_method_header_ = nullptr;
} else if (cur_shadow_frame_ != nullptr) {
do {
+ if (kDebugStackWalk) {
+ ArtMethod* method = cur_shadow_frame_->GetMethod();
+ LOG(INFO) << "Tid=" << thread_-> GetThreadId() << ", method: "
+ << ArtMethod::PrettyMethod(method) << "@" << method
+ << ", ShadowFrame";
+ }
ValidateFrame();
bool should_continue = VisitFrame();
if (UNLIKELY(!should_continue)) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 1b00b54..a4bcf17 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -58,11 +58,6 @@
};
std::ostream& operator<<(std::ostream& os, VRegKind rhs);
-// Size in bytes of the should_deoptimize flag on stack.
-// We just need 4 bytes for our purpose regardless of the architecture. Frame size
-// calculation will automatically do alignment for the final frame size.
-static constexpr size_t kShouldDeoptimizeFlagSize = 4;
-
/*
* Our current stack layout.
* The Dalvik registers come first, followed by the
@@ -197,6 +192,12 @@
uint32_t GetDexPc(bool abort_on_failure = true) const REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns a vector of the inlined dex pcs, in order from outermost to innermost but it replaces
+ // the innermost one with `handler_dex_pc`. In essence, (outermost dex pc, mid dex pc #1, ..., mid
+ // dex pc #n-1, `handler_dex_pc`).
+ std::vector<uint32_t> ComputeDexPcList(uint32_t handler_dex_pc) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ObjPtr<mirror::Object> GetThisObject() const REQUIRES_SHARED(Locks::mutator_lock_);
size_t GetNativePcOffset() const REQUIRES_SHARED(Locks::mutator_lock_);
@@ -230,9 +231,8 @@
uint16_t vreg,
VRegKind kind,
uint32_t* val,
- std::optional<DexRegisterLocation> location =
- std::optional<DexRegisterLocation>()) const
- REQUIRES_SHARED(Locks::mutator_lock_);
+ std::optional<DexRegisterLocation> location = std::optional<DexRegisterLocation>(),
+ bool need_full_register_list = false) const REQUIRES_SHARED(Locks::mutator_lock_);
bool GetVRegPair(ArtMethod* m, uint16_t vreg, VRegKind kind_lo, VRegKind kind_hi,
uint64_t* val) const
@@ -268,10 +268,16 @@
return !current_inline_frames_.empty();
}
+ size_t InlineDepth() const { return current_inline_frames_.size(); }
+
InlineInfo GetCurrentInlinedFrame() const {
return current_inline_frames_.back();
}
+ const BitTableRange<InlineInfo>& GetCurrentInlinedFrames() const {
+ return current_inline_frames_;
+ }
+
uintptr_t GetCurrentQuickFramePc() const {
return cur_quick_frame_pc_;
}
@@ -302,10 +308,26 @@
*should_deoptimize_addr = *should_deoptimize_addr | static_cast<uint8_t>(value);
};
+ void UnsetShouldDeoptimizeFlag(DeoptimizeFlagValue value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t* should_deoptimize_addr = GetShouldDeoptimizeFlagAddr();
+ *should_deoptimize_addr = *should_deoptimize_addr & ~static_cast<uint8_t>(value);
+ };
+
uint8_t GetShouldDeoptimizeFlag() const REQUIRES_SHARED(Locks::mutator_lock_) {
return *GetShouldDeoptimizeFlagAddr();
}
+ bool ShouldForceDeoptForRedefinition() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t should_deopt_flag = GetShouldDeoptimizeFlag();
+ return (should_deopt_flag &
+ static_cast<uint8_t>(DeoptimizeFlagValue::kForceDeoptForRedefinition)) != 0;
+ }
+
+ // Return the number of dex register in the map from the outermost frame to the number of inlined
+ // frames indicated by `depth`. If `depth` is 0, grab just the registers from the outermost level.
+ // If it is greater than 0, grab as many inline frames as `depth` indicates.
+ size_t GetNumberOfRegisters(CodeInfo* code_info, int depth) const;
+
private:
// Private constructor known in the case that num_frames_ has already been computed.
StackVisitor(Thread* thread,
@@ -334,7 +356,8 @@
bool GetVRegFromOptimizedCode(ArtMethod* m,
uint16_t vreg,
VRegKind kind,
- uint32_t* val) const
+ uint32_t* val,
+ bool need_full_register_list = false) const
REQUIRES_SHARED(Locks::mutator_lock_);
bool GetVRegPairFromDebuggerShadowFrame(uint16_t vreg,
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7a13dbd..514d30e 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -20,9 +20,12 @@
#include <limits>
#include "arch/instruction_set.h"
+#include "base/array_ref.h"
#include "base/bit_memory_region.h"
#include "base/bit_table.h"
#include "base/bit_utils.h"
+#include "base/globals.h"
+#include "base/logging.h"
#include "base/memory_region.h"
#include "dex/dex_file_types.h"
#include "dex_register_location.h"
@@ -360,15 +363,12 @@
return GetMethodInfoOf(inline_info).GetMethodIndex();
}
+ // Returns the dex registers for `stack_map`, ignoring any inlined dex registers.
ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map) const {
- if (stack_map.HasDexRegisterMap()) {
- DexRegisterMap map(number_of_dex_registers_, DexRegisterLocation::Invalid());
- DecodeDexRegisterMap(stack_map.Row(), /* first_dex_register= */ 0, &map);
- return map;
- }
- return DexRegisterMap(0, DexRegisterLocation::None());
+ return GetDexRegisterMapOf(stack_map, /* first= */ 0, number_of_dex_registers_);
}
+ // Returns the dex register map of `inline_info`, and just those registers.
ALWAYS_INLINE DexRegisterMap GetInlineDexRegisterMapOf(StackMap stack_map,
InlineInfo inline_info) const {
if (stack_map.HasDexRegisterMap()) {
@@ -381,6 +381,17 @@
? number_of_dex_registers_
: inline_infos_.GetRow(inline_info.Row() - 1).GetNumberOfDexRegisters();
uint32_t last = inline_info.GetNumberOfDexRegisters();
+ return GetDexRegisterMapOf(stack_map, first, last);
+ }
+ return DexRegisterMap(0, DexRegisterLocation::None());
+ }
+
+ // Returns the dex register map of `stack_map` in the range the range [first, last).
+ ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
+ uint32_t first,
+ uint32_t last) const {
+ if (stack_map.HasDexRegisterMap()) {
+ DCHECK_LE(first, last);
DexRegisterMap map(last - first, DexRegisterLocation::Invalid());
DecodeDexRegisterMap(stack_map.Row(), first, &map);
return map;
@@ -409,12 +420,39 @@
return stack_maps_.GetInvalidRow();
}
- // Searches the stack map list backwards because catch stack maps are stored at the end.
- StackMap GetCatchStackMapForDexPc(uint32_t dex_pc) const {
+ StackMap GetCatchStackMapForDexPc(ArrayRef<const uint32_t> dex_pcs) const {
+ // Searches the stack map list backwards because catch stack maps are stored at the end.
for (size_t i = GetNumberOfStackMaps(); i > 0; --i) {
StackMap stack_map = GetStackMapAt(i - 1);
- if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::Catch) {
- return stack_map;
+ if (UNLIKELY(stack_map.GetKind() != StackMap::Kind::Catch)) {
+ // Early break since we should have catch stack maps only at the end.
+ if (kIsDebugBuild) {
+ for (size_t j = i - 1; j > 0; --j) {
+ DCHECK(GetStackMapAt(j - 1).GetKind() != StackMap::Kind::Catch);
+ }
+ }
+ break;
+ }
+
+ // Both the handler dex_pc and all of the inline dex_pcs have to match i.e. we want dex_pcs to
+ // be [stack_map_dex_pc, inline_dex_pc_1, ..., inline_dex_pc_n].
+ if (stack_map.GetDexPc() != dex_pcs.front()) {
+ continue;
+ }
+
+ const BitTableRange<InlineInfo>& inline_infos = GetInlineInfosOf(stack_map);
+ if (inline_infos.size() == dex_pcs.size() - 1) {
+ bool matching_dex_pcs = true;
+ for (size_t inline_info_index = 0; inline_info_index < inline_infos.size();
+ ++inline_info_index) {
+ if (inline_infos[inline_info_index].GetDexPc() != dex_pcs[inline_info_index + 1]) {
+ matching_dex_pcs = false;
+ break;
+ }
+ }
+ if (matching_dex_pcs) {
+ return stack_map;
+ }
}
}
return stack_maps_.GetInvalidRow();
@@ -449,6 +487,14 @@
return (*code_info_data & kIsBaseline) != 0;
}
+ ALWAYS_INLINE static bool IsDebuggable(const uint8_t* code_info_data) {
+ return (*code_info_data & kIsDebuggable) != 0;
+ }
+
+ uint32_t GetNumberOfDexRegisters() {
+ return number_of_dex_registers_;
+ }
+
private:
// Scan backward to determine dex register locations at given stack map.
void DecodeDexRegisterMap(uint32_t stack_map_index,
@@ -495,11 +541,18 @@
enum Flags {
kHasInlineInfo = 1 << 0,
kIsBaseline = 1 << 1,
+ kIsDebuggable = 1 << 2,
};
// The CodeInfo starts with sequence of variable-length bit-encoded integers.
+ // (Please see kVarintMax for more details about encoding).
static constexpr size_t kNumHeaders = 7;
- uint32_t flags_ = 0; // Note that the space is limited to three bits.
+ // Note that the space for flags is limited to three bits. We use a custom encoding where we
+ // encode the value inline if it is less than kVarintMax. We want to access flags without
+ // decoding the entire CodeInfo header so the value of flags cannot be more than kVarintMax.
+ // See IsDebuggable / IsBaseline / HasInlineInfo on how we access flags_ without decoding the
+ // header.
+ uint32_t flags_ = 0;
uint32_t code_size_ = 0; // The size of native PC range in bytes.
uint32_t packed_frame_size_ = 0; // Frame size in kStackAlignment units.
uint32_t core_spill_mask_ = 0;
diff --git a/runtime/string_builder_append.cc b/runtime/string_builder_append.cc
index 85b70eb..0083b91 100644
--- a/runtime/string_builder_append.cc
+++ b/runtime/string_builder_append.cc
@@ -20,9 +20,11 @@
#include "base/logging.h"
#include "common_throws.h"
#include "gc/heap.h"
+#include "mirror/array-inl.h"
#include "mirror/string-alloc-inl.h"
#include "obj_ptr-inl.h"
#include "runtime.h"
+#include "well_known_classes.h"
namespace art {
@@ -60,6 +62,11 @@
return new_string->GetLength() - (data - new_string->GetValue());
}
+ template <typename CharType>
+ CharType* AppendFpArg(ObjPtr<mirror::String> new_string,
+ CharType* data,
+ size_t fp_arg_index) const REQUIRES_SHARED(Locks::mutator_lock_);
+
template <typename CharType, size_t size>
static CharType* AppendLiteral(ObjPtr<mirror::String> new_string,
CharType* data,
@@ -75,6 +82,8 @@
CharType* data,
int64_t value) REQUIRES_SHARED(Locks::mutator_lock_);
+ int32_t ConvertFpArgs() REQUIRES_SHARED(Locks::mutator_lock_);
+
template <typename CharType>
void StoreData(ObjPtr<mirror::String> new_string, CharType* data) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -93,6 +102,15 @@
// References are moved to the handle scope during CalculateLengthWithFlag().
StackHandleScope<kMaxArgs> hs_;
+ // We convert float/double values using jdk.internal.math.FloatingDecimal which uses
+ // a thread-local converter under the hood. As we may have more than one
+ // float/double argument, we need to copy the data out of the converter.
+ // Maximum number of characters is 26. See BinaryToASCIIBuffer.buffer in FloatingDecimal.java .
+ // (This is more than enough for the `ExceptionalBinaryToASCIIBuffer` cases.)
+ static constexpr size_t kBinaryToASCIIBufferSize = 26;
+ uint8_t converted_fp_args_[kMaxArgs][kBinaryToASCIIBufferSize];
+ int32_t converted_fp_arg_lengths_[kMaxArgs];
+
// The length and flag to store when the AppendBuilder is used as a pre-fence visitor.
int32_t length_with_flag_ = 0u;
};
@@ -142,6 +160,18 @@
return log10_value_estimate + adjustment;
}
+template <typename CharType>
+inline CharType* StringBuilderAppend::Builder::AppendFpArg(ObjPtr<mirror::String> new_string,
+ CharType* data,
+ size_t fp_arg_index) const {
+ DCHECK_LE(fp_arg_index, std::size(converted_fp_args_));
+ const uint8_t* src = converted_fp_args_[fp_arg_index];
+ size_t length = converted_fp_arg_lengths_[fp_arg_index];
+ DCHECK_LE(length, kBinaryToASCIIBufferSize);
+ DCHECK_LE(length, RemainingSpace(new_string, data));
+ return std::copy_n(src, length, data);
+}
+
template <typename CharType, size_t size>
inline CharType* StringBuilderAppend::Builder::AppendLiteral(ObjPtr<mirror::String> new_string,
CharType* data,
@@ -204,10 +234,111 @@
return data + length;
}
+int32_t StringBuilderAppend::Builder::ConvertFpArgs() {
+ int32_t fp_args_length = 0u;
+ const uint32_t* current_arg = args_;
+ size_t fp_arg_index = 0u;
+ for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
+ DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
+ bool fp_arg = false;
+ ObjPtr<mirror::Object> converter;
+ switch (static_cast<Argument>(f & kArgMask)) {
+ case Argument::kString:
+ case Argument::kBoolean:
+ case Argument::kChar:
+ case Argument::kInt:
+ break;
+ case Argument::kLong: {
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ break;
+ }
+ case Argument::kFloat: {
+ fp_arg = true;
+ float arg = bit_cast<float>(*current_arg);
+ converter = WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F
+ ->InvokeStatic<'L', 'F'>(hs_.Self(), arg);
+ break;
+ }
+ case Argument::kDouble: {
+ fp_arg = true;
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ double arg = bit_cast<double>(
+ static_cast<uint64_t>(current_arg[0]) + (static_cast<uint64_t>(current_arg[1]) << 32));
+ converter = WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D
+ ->InvokeStatic<'L', 'D'>(hs_.Self(), arg);
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ break;
+ }
+ case Argument::kStringBuilder:
+ case Argument::kCharArray:
+ case Argument::kObject:
+ LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
+ << (f & kArgMask) << " full format: 0x" << std::hex << format_;
+ UNREACHABLE();
+ default:
+ LOG(FATAL) << "Unexpected arg format: 0x" << std::hex
+ << (f & kArgMask) << " full format: 0x" << std::hex << format_;
+ UNREACHABLE();
+ }
+ if (fp_arg) {
+ // If we see an exception (presumably OOME or SOE), keep it as is, even
+ // though it may be confusing to see the stack trace for FP argument
+ // conversion continue at the StringBuilder.toString() invoke location.
+ DCHECK_EQ(converter == nullptr, hs_.Self()->IsExceptionPending());
+ if (UNLIKELY(converter == nullptr)) {
+ return -1;
+ }
+ ArtField* btab_buffer_field =
+ WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
+ int32_t length;
+ if (converter->GetClass() == btab_buffer_field->GetDeclaringClass()) {
+ // Call `converter.getChars(converter.buffer)`.
+ StackHandleScope<1u> hs2(hs_.Self());
+ Handle<mirror::CharArray> buffer =
+ hs2.NewHandle(btab_buffer_field->GetObj<mirror::CharArray>(converter));
+ DCHECK(buffer != nullptr);
+ length = WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars
+ ->InvokeInstance<'I', 'L'>(hs_.Self(), converter, buffer.Get());
+ if (UNLIKELY(hs_.Self()->IsExceptionPending())) {
+ return -1;
+ }
+ // The converted string is now at the front of the buffer.
+ DCHECK_GT(length, 0);
+ DCHECK_LE(length, buffer->GetLength());
+ DCHECK_LE(static_cast<size_t>(length), std::size(converted_fp_args_[0]));
+ DCHECK(mirror::String::AllASCII(buffer->GetData(), length));
+ std::copy_n(buffer->GetData(), length, converted_fp_args_[fp_arg_index]);
+ } else {
+ ArtField* ebtab_image_field = WellKnownClasses::
+ jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
+ DCHECK(converter->GetClass() == ebtab_image_field->GetDeclaringClass());
+ ObjPtr<mirror::String> converted = ebtab_image_field->GetObj<mirror::String>(converter);
+ DCHECK(converted != nullptr);
+ length = converted->GetLength();
+ if (mirror::kUseStringCompression) {
+ DCHECK(converted->IsCompressed());
+ memcpy(converted_fp_args_[fp_arg_index], converted->GetValueCompressed(), length);
+ } else {
+ DCHECK(mirror::String::AllASCII(converted->GetValue(), length));
+ std::copy_n(converted->GetValue(), length, converted_fp_args_[fp_arg_index]);
+ }
+ }
+ converted_fp_arg_lengths_[fp_arg_index] = length;
+ fp_args_length += length;
+ ++fp_arg_index;
+ }
+ ++current_arg;
+ DCHECK_LE(fp_arg_index, kMaxArgs);
+ }
+ return fp_args_length;
+}
+
inline int32_t StringBuilderAppend::Builder::CalculateLengthWithFlag() {
static_assert(static_cast<size_t>(Argument::kEnd) == 0u, "kEnd must be 0.");
bool compressible = mirror::kUseStringCompression;
uint64_t length = 0u;
+ bool has_fp_args = false;
const uint32_t* current_arg = args_;
for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
@@ -243,12 +374,19 @@
++current_arg; // Skip the low word, let the common code skip the high word.
break;
}
+ case Argument::kDouble:
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ FALLTHROUGH_INTENDED;
+ case Argument::kFloat:
+ // Conversion shall be performed in a separate pass because it calls back to
+ // managed code and we need to convert reference arguments to `Handle<>`s first.
+ has_fp_args = true;
+ break;
case Argument::kStringBuilder:
case Argument::kCharArray:
case Argument::kObject:
- case Argument::kFloat:
- case Argument::kDouble:
LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
<< (f & kArgMask) << " full format: 0x" << std::hex << format_;
UNREACHABLE();
@@ -261,6 +399,16 @@
DCHECK_LE(hs_.NumberOfReferences(), kMaxArgs);
}
+ if (UNLIKELY(has_fp_args)) {
+ // Call Java helpers to convert FP args.
+ int32_t fp_args_length = ConvertFpArgs();
+ if (fp_args_length == -1) {
+ return -1;
+ }
+ DCHECK_GT(fp_args_length, 0);
+ length += fp_args_length;
+ }
+
if (length > std::numeric_limits<int32_t>::max()) {
// We cannot allocate memory for the entire result.
hs_.Self()->ThrowNewException("Ljava/lang/OutOfMemoryError;",
@@ -276,6 +424,7 @@
inline void StringBuilderAppend::Builder::StoreData(ObjPtr<mirror::String> new_string,
CharType* data) const {
size_t handle_index = 0u;
+ size_t fp_arg_index = 0u;
const uint32_t* current_arg = args_;
for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
@@ -315,11 +464,18 @@
++current_arg; // Skip the low word, let the common code skip the high word.
break;
}
+ case Argument::kDouble:
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ FALLTHROUGH_INTENDED;
+ case Argument::kFloat: {
+ data = AppendFpArg(new_string, data, fp_arg_index);
+ ++fp_arg_index;
+ break;
+ }
case Argument::kStringBuilder:
case Argument::kCharArray:
- case Argument::kFloat:
- case Argument::kDouble:
LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
<< (f & kArgMask) << " full format: 0x" << std::hex << format_;
UNREACHABLE();
@@ -330,6 +486,7 @@
}
++current_arg;
DCHECK_LE(handle_index, hs_.NumberOfReferences());
+ DCHECK_LE(fp_arg_index, std::size(converted_fp_args_));
}
DCHECK_EQ(RemainingSpace(new_string, data), 0u) << std::hex << format_;
}
diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h
index ca1feb5..90b9b57 100644
--- a/runtime/subtype_check.h
+++ b/runtime/subtype_check.h
@@ -382,12 +382,12 @@
if (UNLIKELY(!klass->HasSuperClass())) {
// Object root always goes directly from Uninitialized -> Assigned.
- const SubtypeCheckInfo root_sci = GetSubtypeCheckInfo(klass);
+ SubtypeCheckInfo root_sci = GetSubtypeCheckInfo(klass);
if (root_sci.GetState() != SubtypeCheckInfo::kUninitialized) {
return root_sci; // No change needed.
}
- const SubtypeCheckInfo new_root_sci = root_sci.CreateRoot();
+ SubtypeCheckInfo new_root_sci = root_sci.CreateRoot();
SetSubtypeCheckInfo(klass, new_root_sci);
// The object root is always in the Uninitialized|Assigned state.
@@ -571,9 +571,7 @@
DCHECK_EQ(depth, klass->Depth());
SubtypeCheckBitsAndStatus current_bits_and_status = ReadField(klass);
- const SubtypeCheckInfo current =
- SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth);
- return current;
+ return SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth);
}
static void SetSubtypeCheckInfo(ClassPtr klass, const SubtypeCheckInfo& new_sci)
diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h
index d734557..eef68d7 100644
--- a/runtime/subtype_check_info.h
+++ b/runtime/subtype_check_info.h
@@ -153,7 +153,7 @@
// Create from the depth and the bitstring+of state.
// This is done for convenience to avoid passing in "depth" everywhere,
// since our current state is almost always a function of depth.
- static SubtypeCheckInfo Create(SubtypeCheckBits compressed_value, size_t depth) {
+ static SubtypeCheckInfo Create(const SubtypeCheckBits& compressed_value, size_t depth) {
SubtypeCheckInfo io;
io.depth_ = depth;
io.bitstring_and_of_ = compressed_value;
@@ -168,9 +168,8 @@
//
// Normally, return kSubtypeOf or kNotSubtypeOf.
Result IsSubtypeOf(const SubtypeCheckInfo& target) {
- if (target.GetState() != SubtypeCheckInfo::kAssigned) {
- return Result::kUnknownSubtypeOf;
- } else if (GetState() == SubtypeCheckInfo::kUninitialized) {
+ if (target.GetState() != SubtypeCheckInfo::kAssigned ||
+ GetState() == SubtypeCheckInfo::kUninitialized) {
return Result::kUnknownSubtypeOf;
}
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 4110ed2..a99977f 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -34,7 +34,7 @@
namespace art {
// Quickly access the current thread from a JNIEnv.
-static inline Thread* ThreadForEnv(JNIEnv* env) {
+inline Thread* Thread::ForEnv(JNIEnv* env) {
JNIEnvExt* full_env(down_cast<JNIEnvExt*>(env));
return full_env->GetSelf();
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 920fb7a..ab4069c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -41,6 +41,8 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "unwindstack/AndroidUnwinder.h"
+
#include "arch/context-inl.h"
#include "arch/context.h"
#include "art_field-inl.h"
@@ -84,6 +86,7 @@
#include "mirror/class_loader.h"
#include "mirror/object_array-alloc-inl.h"
#include "mirror/object_array-inl.h"
+#include "mirror/stack_frame_info.h"
#include "mirror/stack_trace_element.h"
#include "monitor.h"
#include "monitor_objects_stack_visitor.h"
@@ -110,9 +113,10 @@
#include "stack_map.h"
#include "thread-inl.h"
#include "thread_list.h"
+#include "trace.h"
#include "verifier/method_verifier.h"
#include "verify_object.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
#if ART_USE_FUTEXES
#include "linux/futex.h"
@@ -130,7 +134,7 @@
using android::base::StringAppendV;
using android::base::StringPrintf;
-extern "C" NO_RETURN void artDeoptimize(Thread* self);
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks);
bool Thread::is_started_ = false;
pthread_key_t Thread::pthread_key_self_;
@@ -272,6 +276,7 @@
ObjPtr<mirror::Throwable> exception,
bool from_code,
DeoptimizationMethodType method_type) {
+ DCHECK(exception != Thread::GetDeoptimizationException());
DeoptimizationContextRecord* record = new DeoptimizationContextRecord(
return_value,
is_reference,
@@ -433,15 +438,18 @@
tlsPtr_.stacked_shadow_frame_record = record;
}
-ShadowFrame* Thread::PopStackedShadowFrame(StackedShadowFrameType type, bool must_be_present) {
+ShadowFrame* Thread::MaybePopDeoptimizedStackedShadowFrame() {
StackedShadowFrameRecord* record = tlsPtr_.stacked_shadow_frame_record;
- if (must_be_present) {
- DCHECK(record != nullptr);
- } else {
- if (record == nullptr || record->GetType() != type) {
- return nullptr;
- }
+ if (record == nullptr ||
+ record->GetType() != StackedShadowFrameType::kDeoptimizationShadowFrame) {
+ return nullptr;
}
+ return PopStackedShadowFrame();
+}
+
+ShadowFrame* Thread::PopStackedShadowFrame() {
+ StackedShadowFrameRecord* record = tlsPtr_.stacked_shadow_frame_record;
+ DCHECK_NE(record, nullptr);
tlsPtr_.stacked_shadow_frame_record = record->GetLink();
ShadowFrame* shadow_frame = record->GetShadowFrame();
delete record;
@@ -531,7 +539,7 @@
return shadow_frame;
}
VLOG(deopt) << "Create pre-deopted ShadowFrame for " << ArtMethod::PrettyMethod(method);
- shadow_frame = ShadowFrame::CreateDeoptimizedFrame(num_vregs, nullptr, method, dex_pc);
+ shadow_frame = ShadowFrame::CreateDeoptimizedFrame(num_vregs, method, dex_pc);
FrameIdToShadowFrame* record = FrameIdToShadowFrame::Create(frame_id,
shadow_frame,
tlsPtr_.frame_id_to_shadow_frame,
@@ -638,7 +646,7 @@
self->DeleteJPeer(self->GetJniEnv());
self->SetThreadName(self->GetThreadName()->ToModifiedUtf8().c_str());
- ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority);
+ ArtField* priorityField = WellKnownClasses::java_lang_Thread_priority;
self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));
runtime->GetRuntimeCallbacks()->ThreadStart(self);
@@ -646,8 +654,7 @@
// Unpark ourselves if the java peer was unparked before it started (see
// b/28845097#comment49 for more information)
- ArtField* unparkedField = jni::DecodeArtField(
- WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ ArtField* unparkedField = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
bool should_unpark = false;
{
// Hold the lock here, so that if another thread calls unpark before the thread starts
@@ -661,9 +668,7 @@
}
// Invoke the 'run' method of our java.lang.Thread.
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
- jmethodID mid = WellKnownClasses::java_lang_Thread_run;
- ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
- InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
+ WellKnownClasses::java_lang_Thread_run->InvokeVirtual<'V'>(self, receiver);
}
// Detach and delete self.
Runtime::Current()->GetThreadList()->Unregister(self, /* should_run_callbacks= */ true);
@@ -673,7 +678,7 @@
Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa,
ObjPtr<mirror::Object> thread_peer) {
- ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer);
+ ArtField* f = WellKnownClasses::java_lang_Thread_nativePeer;
Thread* result = reinterpret_cast64<Thread*>(f->GetLong(thread_peer));
// Check that if we have a result it is either suspended or we hold the thread_list_lock_
// to stop it from going away.
@@ -790,7 +795,7 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wframe-larger-than="
NO_INLINE
- static void Touch(uintptr_t target) {
+ __attribute__((no_sanitize("memtag"))) static void Touch(uintptr_t target) {
volatile size_t zero = 0;
// Use a large local volatile array to ensure a large frame size. Do not use anything close
// to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but
@@ -831,6 +836,22 @@
madvise(pregion, unwanted_size, MADV_DONTNEED);
}
+template <bool kSupportTransaction>
+static void SetNativePeer(ObjPtr<mirror::Object> java_peer, Thread* thread)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* field = WellKnownClasses::java_lang_Thread_nativePeer;
+ if (kSupportTransaction && Runtime::Current()->IsActiveTransaction()) {
+ field->SetLong</*kTransactionActive=*/ true>(java_peer, reinterpret_cast<jlong>(thread));
+ } else {
+ field->SetLong</*kTransactionActive=*/ false>(java_peer, reinterpret_cast<jlong>(thread));
+ }
+}
+
+static void SetNativePeer(JNIEnv* env, jobject java_peer, Thread* thread) {
+ ScopedObjectAccess soa(env);
+ SetNativePeer</*kSupportTransaction=*/ false>(soa.Decode<mirror::Object>(java_peer), thread);
+}
+
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();
@@ -838,7 +859,7 @@
if (VLOG_IS_ON(threads)) {
ScopedObjectAccess soa(env);
- ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name);
+ ArtField* f = WellKnownClasses::java_lang_Thread_name;
ObjPtr<mirror::String> java_name =
f->GetObject(soa.Decode<mirror::Object>(java_peer))->AsString();
std::string thread_name;
@@ -877,8 +898,7 @@
// Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing
// to assign it.
- env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
- reinterpret_cast<jlong>(child_thread));
+ SetNativePeer(env, java_peer, child_thread);
// Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and
// do not have a good way to report this on the child's side.
@@ -923,7 +943,7 @@
delete child_thread;
child_thread = nullptr;
// TODO: remove from thread group?
- env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
+ SetNativePeer(env, java_peer, nullptr);
{
std::string msg(child_jni_env_ext.get() == nullptr ?
StringPrintf("Could not allocate JNI Env: %s", error_msg.c_str()) :
@@ -1091,14 +1111,11 @@
Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_peer) {
auto set_peer_action = [&](Thread* self) {
// Install the given peer.
- {
- DCHECK(self == Thread::Current());
- ScopedObjectAccess soa(self);
- self->tlsPtr_.opeer = soa.Decode<mirror::Object>(thread_peer).Ptr();
- }
- self->GetJniEnv()->SetLongField(thread_peer,
- WellKnownClasses::java_lang_Thread_nativePeer,
- reinterpret_cast64<jlong>(self));
+ DCHECK(self == Thread::Current());
+ ScopedObjectAccess soa(self);
+ ObjPtr<mirror::Object> peer = soa.Decode<mirror::Object>(thread_peer);
+ self->tlsPtr_.opeer = peer.Ptr();
+ SetNativePeer</*kSupportTransaction=*/ false>(peer, self);
return true;
};
return Attach(thread_name, as_daemon, set_peer_action, /* should_run_callbacks= */ true);
@@ -1107,45 +1124,39 @@
void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsStarted());
- JNIEnv* env = tlsPtr_.jni_env;
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
- if (thread_group == nullptr) {
- thread_group = runtime->GetMainThreadGroup();
- }
- ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+ ScopedObjectAccess soa(self);
+ StackHandleScope<4u> hs(self);
+ DCHECK(WellKnownClasses::java_lang_ThreadGroup->IsInitialized());
+ Handle<mirror::Object> thr_group = hs.NewHandle(soa.Decode<mirror::Object>(
+ thread_group != nullptr ? thread_group : runtime->GetMainThreadGroup()));
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ name != nullptr ? mirror::String::AllocFromModifiedUtf8(self, name) : nullptr);
// Add missing null check in case of OOM b/18297817
- if (name != nullptr && thread_name.get() == nullptr) {
- CHECK(IsExceptionPending());
+ if (name != nullptr && UNLIKELY(thread_name == nullptr)) {
+ CHECK(self->IsExceptionPending());
return;
}
jint thread_priority = GetNativePriority();
- jboolean thread_is_daemon = as_daemon;
- ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- if (peer.get() == nullptr) {
+ DCHECK(WellKnownClasses::java_lang_Thread->IsInitialized());
+ Handle<mirror::Object> peer =
+ hs.NewHandle(WellKnownClasses::java_lang_Thread->AllocObject(self));
+ if (UNLIKELY(peer == nullptr)) {
CHECK(IsExceptionPending());
return;
}
- {
- ScopedObjectAccess soa(this);
- tlsPtr_.opeer = soa.Decode<mirror::Object>(peer.get()).Ptr();
- }
- env->CallNonvirtualVoidMethod(peer.get(),
- WellKnownClasses::java_lang_Thread,
- WellKnownClasses::java_lang_Thread_init,
- thread_group, thread_name.get(), thread_priority, thread_is_daemon);
- if (IsExceptionPending()) {
+ tlsPtr_.opeer = peer.Get();
+ WellKnownClasses::java_lang_Thread_init->InvokeInstance<'V', 'L', 'L', 'I', 'Z'>(
+ self, peer.Get(), thr_group.Get(), thread_name.Get(), thread_priority, as_daemon);
+ if (self->IsExceptionPending()) {
return;
}
- Thread* self = this;
- DCHECK_EQ(self, Thread::Current());
- env->SetLongField(peer.get(),
- WellKnownClasses::java_lang_Thread_nativePeer,
- reinterpret_cast64<jlong>(self));
+ SetNativePeer</*kSupportTransaction=*/ false>(peer.Get(), self);
- ScopedObjectAccess soa(self);
- StackHandleScope<1> hs(self);
MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName()));
if (peer_thread_name == nullptr) {
// The Thread constructor should have set the Thread.name to a
@@ -1153,18 +1164,16 @@
// available (in the compiler, in tests), we manually assign the
// fields the constructor should have set.
if (runtime->IsActiveTransaction()) {
- InitPeer<true>(soa,
- tlsPtr_.opeer,
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<true>(tlsPtr_.opeer,
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
} else {
- InitPeer<false>(soa,
- tlsPtr_.opeer,
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<false>(tlsPtr_.opeer,
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
}
peer_thread_name.Assign(GetThreadName());
@@ -1175,27 +1184,32 @@
}
}
-jobject Thread::CreateCompileTimePeer(JNIEnv* env,
- const char* name,
- bool as_daemon,
- jobject thread_group) {
+ObjPtr<mirror::Object> Thread::CreateCompileTimePeer(const char* name,
+ bool as_daemon,
+ jobject thread_group) {
Runtime* runtime = Runtime::Current();
CHECK(!runtime->IsStarted());
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
- if (thread_group == nullptr) {
- thread_group = runtime->GetMainThreadGroup();
- }
- ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+ ScopedObjectAccessUnchecked soa(self);
+ StackHandleScope<3u> hs(self);
+ DCHECK(WellKnownClasses::java_lang_ThreadGroup->IsInitialized());
+ Handle<mirror::Object> thr_group = hs.NewHandle(soa.Decode<mirror::Object>(
+ thread_group != nullptr ? thread_group : runtime->GetMainThreadGroup()));
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ name != nullptr ? mirror::String::AllocFromModifiedUtf8(self, name) : nullptr);
// Add missing null check in case of OOM b/18297817
- if (name != nullptr && thread_name.get() == nullptr) {
- CHECK(Thread::Current()->IsExceptionPending());
+ if (name != nullptr && UNLIKELY(thread_name == nullptr)) {
+ CHECK(self->IsExceptionPending());
return nullptr;
}
jint thread_priority = kNormThreadPriority; // Always normalize to NORM priority.
- jboolean thread_is_daemon = as_daemon;
- ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- if (peer.get() == nullptr) {
+ DCHECK(WellKnownClasses::java_lang_Thread->IsInitialized());
+ Handle<mirror::Object> peer = hs.NewHandle(
+ WellKnownClasses::java_lang_Thread->AllocObject(self));
+ if (peer == nullptr) {
CHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
@@ -1206,41 +1220,34 @@
// non-null value. However, because we can run without code
// available (in the compiler, in tests), we manually assign the
// fields the constructor should have set.
- ScopedObjectAccessUnchecked soa(Thread::Current());
if (runtime->IsActiveTransaction()) {
- InitPeer<true>(soa,
- soa.Decode<mirror::Object>(peer.get()),
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<true>(peer.Get(),
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
} else {
- InitPeer<false>(soa,
- soa.Decode<mirror::Object>(peer.get()),
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<false>(peer.Get(),
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
}
- return peer.release();
+ return peer.Get();
}
template<bool kTransactionActive>
-void Thread::InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::Object> peer,
- jboolean thread_is_daemon,
- jobject thread_group,
- jobject thread_name,
+void Thread::InitPeer(ObjPtr<mirror::Object> peer,
+ bool as_daemon,
+ ObjPtr<mirror::Object> thread_group,
+ ObjPtr<mirror::String> thread_name,
jint thread_priority) {
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_daemon)->
- SetBoolean<kTransactionActive>(peer, thread_is_daemon);
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)->
- SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_group));
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name)->
- SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_name));
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority)->
- SetInt<kTransactionActive>(peer, thread_priority);
+ WellKnownClasses::java_lang_Thread_daemon->SetBoolean<kTransactionActive>(peer,
+ static_cast<uint8_t>(as_daemon ? 1u : 0u));
+ WellKnownClasses::java_lang_Thread_group->SetObject<kTransactionActive>(peer, thread_group);
+ WellKnownClasses::java_lang_Thread_name->SetObject<kTransactionActive>(peer, thread_name);
+ WellKnownClasses::java_lang_Thread_priority->SetInt<kTransactionActive>(peer, thread_priority);
}
void Thread::SetCachedThreadName(const char* name) {
@@ -1399,18 +1406,26 @@
tls32_.num_name_readers.fetch_sub(1 /* at least memory_order_release */);
}
-void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map,
- bool force_dump_stack) const {
+Thread::DumpOrder Thread::Dump(std::ostream& os,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
DumpState(os);
- DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack);
+ return DumpStack(os, dump_native_stack, force_dump_stack);
+}
+
+Thread::DumpOrder Thread::Dump(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
+ DumpState(os);
+ return DumpStack(os, unwinder, dump_native_stack, force_dump_stack);
}
ObjPtr<mirror::String> Thread::GetThreadName() const {
- ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name);
if (tlsPtr_.opeer == nullptr) {
return nullptr;
}
- ObjPtr<mirror::Object> name = f->GetObject(tlsPtr_.opeer);
+ ObjPtr<mirror::Object> name = WellKnownClasses::java_lang_Thread_name->GetObject(tlsPtr_.opeer);
return name == nullptr ? nullptr : name->AsString();
}
@@ -1948,20 +1963,15 @@
// cause ScopedObjectAccessUnchecked to deadlock.
if (gAborting == 0 && self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) {
ScopedObjectAccessUnchecked soa(self);
- priority = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority)
- ->GetInt(thread->tlsPtr_.opeer);
- is_daemon = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_daemon)
- ->GetBoolean(thread->tlsPtr_.opeer);
+ priority = WellKnownClasses::java_lang_Thread_priority->GetInt(thread->tlsPtr_.opeer);
+ is_daemon = WellKnownClasses::java_lang_Thread_daemon->GetBoolean(thread->tlsPtr_.opeer);
ObjPtr<mirror::Object> thread_group =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)
- ->GetObject(thread->tlsPtr_.opeer);
+ WellKnownClasses::java_lang_Thread_group->GetObject(thread->tlsPtr_.opeer);
if (thread_group != nullptr) {
- ArtField* group_name_field =
- jni::DecodeArtField(WellKnownClasses::java_lang_ThreadGroup_name);
ObjPtr<mirror::String> group_name_string =
- group_name_field->GetObject(thread_group)->AsString();
+ WellKnownClasses::java_lang_ThreadGroup_name->GetObject(thread_group)->AsString();
group_name = (group_name_string != nullptr) ? group_name_string->ToModifiedUtf8() : "<null>";
}
} else if (thread != nullptr) {
@@ -1989,6 +1999,9 @@
if (thread->IsStillStarting()) {
os << " (still starting up)";
}
+ if (thread->tls32_.disable_thread_flip_count != 0) {
+ os << " DisableFlipCount = " << thread->tls32_.disable_thread_flip_count;
+ }
os << "\n";
} else {
os << '"' << ::art::GetThreadName(tid) << '"'
@@ -2201,11 +2214,13 @@
UNREACHABLE();
}
PrintObject(obj, msg, owner_tid);
+ num_blocked++;
}
void VisitLockedObject(ObjPtr<mirror::Object> obj)
override
REQUIRES_SHARED(Locks::mutator_lock_) {
PrintObject(obj, " - locked ", ThreadList::kInvalidThreadId);
+ num_locked++;
}
void PrintObject(ObjPtr<mirror::Object> obj,
@@ -2239,6 +2254,8 @@
ArtMethod* last_method;
int last_line_number;
size_t repetition_count;
+ size_t num_blocked = 0;
+ size_t num_locked = 0;
};
static bool ShouldShowNativeStack(const Thread* thread)
@@ -2270,7 +2287,9 @@
return current_method != nullptr && current_method->IsNative();
}
-void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_locks) const {
+Thread::DumpOrder Thread::DumpJavaStack(std::ostream& os,
+ bool check_suspended,
+ bool dump_locks) const {
// Dumping the Java stack involves the verifier for locks. The verifier operates under the
// assumption that there is no exception pending on entry. Thus, stash any pending exception.
// Thread::Current() instead of this in case a thread is dumping the stack of another suspended
@@ -2281,12 +2300,28 @@
StackDumpVisitor dumper(os, const_cast<Thread*>(this), context.get(),
!tls32_.throwing_OutOfMemoryError, check_suspended, dump_locks);
dumper.WalkStack();
+ if (IsJitSensitiveThread()) {
+ return DumpOrder::kMain;
+ } else if (dumper.num_blocked > 0) {
+ return DumpOrder::kBlocked;
+ } else if (dumper.num_locked > 0) {
+ return DumpOrder::kLocked;
+ } else {
+ return DumpOrder::kDefault;
+ }
}
-void Thread::DumpStack(std::ostream& os,
- bool dump_native_stack,
- BacktraceMap* backtrace_map,
- bool force_dump_stack) const {
+Thread::DumpOrder Thread::DumpStack(std::ostream& os,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ return DumpStack(os, unwinder, dump_native_stack, force_dump_stack);
+}
+
+Thread::DumpOrder Thread::DumpStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
// TODO: we call this code when dying but may not have suspended the thread ourself. The
// IsSuspended check is therefore racy with the use for dumping (normally we inhibit
// the race with the thread_suspend_count_lock_).
@@ -2297,6 +2332,7 @@
// thread's stack in debug builds where we'll hit the not suspended check in the stack walk.
safe_to_dump = (safe_to_dump || dump_for_abort);
}
+ DumpOrder dump_order = DumpOrder::kDefault;
if (safe_to_dump || force_dump_stack) {
// If we're currently in native code, dump that stack before dumping the managed stack.
if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) {
@@ -2304,14 +2340,15 @@
GetCurrentMethod(nullptr,
/*check_suspended=*/ !force_dump_stack,
/*abort_on_error=*/ !(dump_for_abort || force_dump_stack));
- DumpNativeStack(os, GetTid(), backtrace_map, " native: ", method);
+ DumpNativeStack(os, unwinder, GetTid(), " native: ", method);
}
- DumpJavaStack(os,
- /*check_suspended=*/ !force_dump_stack,
- /*dump_locks=*/ !force_dump_stack);
+ dump_order = DumpJavaStack(os,
+ /*check_suspended=*/ !force_dump_stack,
+ /*dump_locks=*/ !force_dump_stack);
} else {
os << "Not able to dump stack of thread that isn't suspended";
}
+ return dump_order;
}
void Thread::ThreadExitCallback(void* arg) {
@@ -2389,25 +2426,17 @@
}
void Thread::NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group) {
- ScopedLocalRef<jobject> thread_jobject(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
- ScopedLocalRef<jobject> thread_group_jobject_scoped(
- soa.Env(), nullptr);
- jobject thread_group_jobject = thread_group;
+ ObjPtr<mirror::Object> thread_object = soa.Self()->GetPeer();
+ ObjPtr<mirror::Object> thread_group_object = soa.Decode<mirror::Object>(thread_group);
if (thread_group == nullptr || kIsDebugBuild) {
// There is always a group set. Retrieve it.
- thread_group_jobject_scoped.reset(
- soa.Env()->GetObjectField(thread_jobject.get(),
- WellKnownClasses::java_lang_Thread_group));
- thread_group_jobject = thread_group_jobject_scoped.get();
+ thread_group_object = WellKnownClasses::java_lang_Thread_group->GetObject(thread_object);
if (kIsDebugBuild && thread_group != nullptr) {
- CHECK(soa.Env()->IsSameObject(thread_group, thread_group_jobject));
+ CHECK(thread_group_object == soa.Decode<mirror::Object>(thread_group));
}
}
- soa.Env()->CallNonvirtualVoidMethod(thread_group_jobject,
- WellKnownClasses::java_lang_ThreadGroup,
- WellKnownClasses::java_lang_ThreadGroup_add,
- thread_jobject.get());
+ WellKnownClasses::java_lang_ThreadGroup_add->InvokeVirtual<'V', 'L'>(
+ soa.Self(), thread_group_object, thread_object);
}
Thread::Thread(bool daemon)
@@ -2418,8 +2447,6 @@
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
tlsPtr_.mutator_lock = Locks::mutator_lock_;
DCHECK(tlsPtr_.mutator_lock != nullptr);
- tlsPtr_.instrumentation_stack =
- new std::map<uintptr_t, instrumentation::InstrumentationStackFrame>;
tlsPtr_.name.store(kThreadNameDuringStartup, std::memory_order_relaxed);
static_assert((sizeof(Thread) % 4) == 0U,
@@ -2467,8 +2494,7 @@
void Thread::AssertPendingOOMException() const {
AssertPendingException();
auto* e = GetException();
- CHECK_EQ(e->GetClass(), DecodeJObject(WellKnownClasses::java_lang_OutOfMemoryError)->AsClass())
- << e->Dump();
+ CHECK_EQ(e->GetClass(), WellKnownClasses::java_lang_OutOfMemoryError.Get()) << e->Dump();
}
void Thread::AssertNoPendingException() const {
@@ -2531,27 +2557,25 @@
if (tlsPtr_.opeer != nullptr) {
ScopedObjectAccess soa(self);
+ if (UNLIKELY(self->GetMethodTraceBuffer() != nullptr)) {
+ Trace::FlushThreadBuffer(self);
+ self->ResetMethodTraceBuffer();
+ }
// We may need to call user-supplied managed code, do this before final clean-up.
- HandleUncaughtExceptions(soa);
- RemoveFromThreadGroup(soa);
+ HandleUncaughtExceptions();
+ RemoveFromThreadGroup();
Runtime* runtime = Runtime::Current();
if (runtime != nullptr && should_run_callbacks) {
runtime->GetRuntimeCallbacks()->ThreadDeath(self);
}
// this.nativePeer = 0;
- if (Runtime::Current()->IsActiveTransaction()) {
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
- ->SetLong<true>(tlsPtr_.opeer, 0);
- } else {
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
- ->SetLong<false>(tlsPtr_.opeer, 0);
- }
+ SetNativePeer</*kSupportTransaction=*/ true>(tlsPtr_.opeer, nullptr);
// Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
// who is waiting.
ObjPtr<mirror::Object> lock =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_lock)->GetObject(tlsPtr_.opeer);
+ WellKnownClasses::java_lang_Thread_lock->GetObject(tlsPtr_.opeer);
// (This conditional is only needed for tests, where Thread.lock won't have been set.)
if (lock != nullptr) {
StackHandleScope<1> hs(self);
@@ -2609,47 +2633,47 @@
CleanupCpu();
}
- delete tlsPtr_.instrumentation_stack;
SetCachedThreadName(nullptr); // Deallocate name.
delete tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
+ if (tlsPtr_.method_trace_buffer != nullptr) {
+ delete[] tlsPtr_.method_trace_buffer;
+ }
+
Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
TearDownAlternateSignalStack();
}
-void Thread::HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa) {
- if (!IsExceptionPending()) {
+void Thread::HandleUncaughtExceptions() {
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
+ if (!self->IsExceptionPending()) {
return;
}
- ScopedLocalRef<jobject> peer(tlsPtr_.jni_env, soa.AddLocalReference<jobject>(tlsPtr_.opeer));
- ScopedThreadStateChange tsc(this, ThreadState::kNative);
// Get and clear the exception.
- ScopedLocalRef<jthrowable> exception(tlsPtr_.jni_env, tlsPtr_.jni_env->ExceptionOccurred());
- tlsPtr_.jni_env->ExceptionClear();
+ ObjPtr<mirror::Object> exception = self->GetException();
+ self->ClearException();
// Call the Thread instance's dispatchUncaughtException(Throwable)
- tlsPtr_.jni_env->CallVoidMethod(peer.get(),
- WellKnownClasses::java_lang_Thread_dispatchUncaughtException,
- exception.get());
+ WellKnownClasses::java_lang_Thread_dispatchUncaughtException->InvokeFinal<'V', 'L'>(
+ self, tlsPtr_.opeer, exception);
// If the dispatchUncaughtException threw, clear that exception too.
- tlsPtr_.jni_env->ExceptionClear();
+ self->ClearException();
}
-void Thread::RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa) {
- // this.group.removeThread(this);
+void Thread::RemoveFromThreadGroup() {
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
+ // this.group.threadTerminated(this);
// group can be null if we're in the compiler or a test.
- ObjPtr<mirror::Object> ogroup = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)
- ->GetObject(tlsPtr_.opeer);
- if (ogroup != nullptr) {
- ScopedLocalRef<jobject> group(soa.Env(), soa.AddLocalReference<jobject>(ogroup));
- ScopedLocalRef<jobject> peer(soa.Env(), soa.AddLocalReference<jobject>(tlsPtr_.opeer));
- ScopedThreadStateChange tsc(soa.Self(), ThreadState::kNative);
- tlsPtr_.jni_env->CallVoidMethod(group.get(),
- WellKnownClasses::java_lang_ThreadGroup_removeThread,
- peer.get());
+ ObjPtr<mirror::Object> group =
+ WellKnownClasses::java_lang_Thread_group->GetObject(tlsPtr_.opeer);
+ if (group != nullptr) {
+ WellKnownClasses::java_lang_ThreadGroup_threadTerminated->InvokeVirtual<'V', 'L'>(
+ self, group, tlsPtr_.opeer);
}
}
@@ -2750,10 +2774,10 @@
bool expect_null = false;
// The "kinds" below are sorted by the frequency we expect to encounter them.
if (kind == kLocal) {
- IndirectReferenceTable& locals = tlsPtr_.jni_env->locals_;
+ jni::LocalReferenceTable& locals = tlsPtr_.jni_env->locals_;
// Local references do not need a read barrier.
- result = locals.Get<kWithoutReadBarrier>(ref);
- } else if (kind == kJniTransitionOrInvalid) {
+ result = locals.Get(ref);
+ } else if (kind == kJniTransition) {
// The `jclass` for a static method points to the CompressedReference<> in the
// `ArtMethod::declaring_class_`. Other `jobject` arguments point to spilled stack
// references but a StackReference<> is just a subclass of CompressedReference<>.
@@ -2956,9 +2980,19 @@
methods_and_pcs->SetElementPtrSize</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
static_cast<uint32_t>(methods_and_pcs->GetLength()) / 2 + count_, dex_pc, pointer_size_);
// Save the declaring class of the method to ensure that the declaring classes of the methods
- // do not get unloaded while the stack trace is live.
+ // do not get unloaded while the stack trace is live. However, this does not work for copied
+ // methods because the declaring class of a copied method points to an interface class which
+ // may be in a different class loader. Instead, retrieve the class loader associated with the
+ // allocator that holds the copied method. This is much cheaper than finding the actual class.
+ ObjPtr<mirror::Object> keep_alive;
+ if (UNLIKELY(method->IsCopied())) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ keep_alive = class_linker->GetHoldingClassLoaderOfCopiedMethod(self_, method);
+ } else {
+ keep_alive = method->GetDeclaringClass();
+ }
trace_->Set</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
- static_cast<int32_t>(count_) + 1, method->GetDeclaringClass());
+ static_cast<int32_t>(count_) + 1, keep_alive);
++count_;
}
@@ -2976,11 +3010,14 @@
uint32_t skip_depth_;
// Current position down stack trace.
uint32_t count_ = 0;
- // An object array where the first element is a pointer array that contains the ArtMethod
- // pointers on the stack and dex PCs. The rest of the elements are the declaring class of
- // the ArtMethod pointers. trace_[i+1] contains the declaring class of the ArtMethod of the
- // i'th frame. We're initializing a newly allocated trace, so we do not need to record that
- // under a transaction. If the transaction is aborted, the whole trace shall be unreachable.
+ // An object array where the first element is a pointer array that contains the `ArtMethod`
+ // pointers on the stack and dex PCs. The rest of the elements are referencing objects
+ // that shall keep the methods alive, namely the declaring class of the `ArtMethod` for
+ // declared methods and the class loader for copied methods (because it's faster to find
+ // the class loader than the actual class that holds the copied method). The `trace_[i+1]`
+ // contains the declaring class or class loader of the `ArtMethod` of the i'th frame.
+ // We're initializing a newly allocated trace, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole trace shall be unreachable.
mirror::ObjectArray<mirror::Object>* trace_ = nullptr;
// For cross compilation.
const PointerSize pointer_size_;
@@ -3147,6 +3184,148 @@
return result;
}
+[[nodiscard]] static ObjPtr<mirror::StackFrameInfo> InitStackFrameInfo(
+ const ScopedObjectAccessAlreadyRunnable& soa,
+ ClassLinker* class_linker,
+ Handle<mirror::StackFrameInfo> stackFrameInfo,
+ ArtMethod* method,
+ uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<4> hs(soa.Self());
+ int32_t line_number;
+ auto source_name_object(hs.NewHandle<mirror::String>(nullptr));
+ if (method->IsProxyMethod()) {
+ line_number = -1;
+ // source_name_object intentionally left null for proxy methods
+ } else {
+ line_number = method->GetLineNumFromDexPC(dex_pc);
+ if (line_number == -1) {
+ // Make the line_number field of StackFrameInfo hold the dex pc.
+ // source_name_object is intentionally left null if we failed to map the dex pc to
+ // a line number (most probably because there is no debug info). See b/30183883.
+ line_number = static_cast<int32_t>(dex_pc);
+ } else {
+ const char* source_file = method->GetDeclaringClassSourceFile();
+ if (source_file != nullptr) {
+ source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file));
+ if (source_name_object == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ Handle<mirror::Class> declaring_class_object(
+ hs.NewHandle<mirror::Class>(method->GetDeclaringClass()));
+
+ ArtMethod* interface_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ const char* method_name = interface_method->GetName();
+ CHECK(method_name != nullptr);
+ Handle<mirror::String> method_name_object(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name)));
+ if (method_name_object == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
+
+ dex::ProtoIndex proto_idx =
+ method->GetDexFile()->GetIndexForProtoId(interface_method->GetPrototype());
+ Handle<mirror::MethodType> method_type_object(hs.NewHandle<mirror::MethodType>(
+ class_linker->ResolveMethodType(soa.Self(), proto_idx, interface_method)));
+ if (method_type_object == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
+
+ stackFrameInfo->AssignFields(declaring_class_object,
+ method_type_object,
+ method_name_object,
+ source_name_object,
+ line_number,
+ static_cast<int32_t>(dex_pc));
+ return stackFrameInfo.Get();
+}
+
+constexpr jlong FILL_CLASS_REFS_ONLY = 0x2; // StackStreamFactory.FILL_CLASS_REFS_ONLY
+
+jint Thread::InternalStackTraceToStackFrameInfoArray(
+ const ScopedObjectAccessAlreadyRunnable& soa,
+ jlong mode, // See java.lang.StackStreamFactory for the mode flags
+ jobject internal,
+ jint startLevel,
+ jint batchSize,
+ jint startBufferIndex,
+ jobjectArray output_array) {
+ // Decode the internal stack trace into the depth, method trace and PC trace.
+ // Subtract one for the methods and PC trace.
+ int32_t depth = soa.Decode<mirror::Array>(internal)->GetLength() - 1;
+ DCHECK_GE(depth, 0);
+
+ StackHandleScope<6> hs(soa.Self());
+ Handle<mirror::ObjectArray<mirror::Object>> framesOrClasses =
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(output_array));
+
+ jint endBufferIndex = startBufferIndex;
+
+ if (startLevel < 0 || startLevel >= depth) {
+ return endBufferIndex;
+ }
+
+ int32_t bufferSize = framesOrClasses->GetLength();
+ if (startBufferIndex < 0 || startBufferIndex >= bufferSize) {
+ return endBufferIndex;
+ }
+
+ // The FILL_CLASS_REFS_ONLY flag is defined in AbstractStackWalker.fetchStackFrames() javadoc.
+ bool isClassArray = (mode & FILL_CLASS_REFS_ONLY) != 0;
+
+ Handle<mirror::ObjectArray<mirror::Object>> decoded_traces =
+ hs.NewHandle(soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>());
+ // Methods and dex PC trace is element 0.
+ DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray());
+ Handle<mirror::PointerArray> method_trace =
+ hs.NewHandle(ObjPtr<mirror::PointerArray>::DownCast(decoded_traces->Get(0)));
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> sfi_class =
+ hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/StackFrameInfo;"));
+ DCHECK(sfi_class != nullptr);
+
+ MutableHandle<mirror::StackFrameInfo> frame = hs.NewHandle<mirror::StackFrameInfo>(nullptr);
+ MutableHandle<mirror::Class> clazz = hs.NewHandle<mirror::Class>(nullptr);
+ for (uint32_t i = static_cast<uint32_t>(startLevel); i < static_cast<uint32_t>(depth); ++i) {
+ if (endBufferIndex >= startBufferIndex + batchSize || endBufferIndex >= bufferSize) {
+ break;
+ }
+
+ ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize);
+ if (isClassArray) {
+ clazz.Assign(method->GetDeclaringClass());
+ framesOrClasses->Set(endBufferIndex, clazz.Get());
+ } else {
+ // Prepare parameters for fields in StackFrameInfo
+ uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>(
+ i + static_cast<uint32_t>(method_trace->GetLength()) / 2, kRuntimePointerSize);
+
+ ObjPtr<mirror::Object> frameObject = framesOrClasses->Get(endBufferIndex);
+ // If libcore didn't allocate the object, we just stop here, but it's unlikely.
+ if (frameObject == nullptr || !frameObject->InstanceOf(sfi_class.Get())) {
+ break;
+ }
+ frame.Assign(ObjPtr<mirror::StackFrameInfo>::DownCast(frameObject));
+ frame.Assign(InitStackFrameInfo(soa, class_linker, frame, method, dex_pc));
+ // Break if InitStackFrameInfo fails to allocate objects or assign the fields.
+ if (frame == nullptr) {
+ break;
+ }
+ }
+
+ ++endBufferIndex;
+ }
+
+ return endBufferIndex;
+}
+
jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const {
// This code allocates. Do not allow it to operate with a pending exception.
if (IsExceptionPending()) {
@@ -3583,6 +3762,7 @@
QUICK_ENTRY_POINT_INFO(pAputObject)
QUICK_ENTRY_POINT_INFO(pJniMethodStart)
QUICK_ENTRY_POINT_INFO(pJniMethodEnd)
+ QUICK_ENTRY_POINT_INFO(pJniMethodEntryHook)
QUICK_ENTRY_POINT_INFO(pJniDecodeReferenceResult)
QUICK_ENTRY_POINT_INFO(pJniLockObject)
QUICK_ENTRY_POINT_INFO(pJniUnlockObject)
@@ -3648,6 +3828,7 @@
QUICK_ENTRY_POINT_INFO(pA64Store)
QUICK_ENTRY_POINT_INFO(pNewEmptyString)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_B)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BB)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BI)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BII)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BIII)
@@ -3662,6 +3843,7 @@
QUICK_ENTRY_POINT_INFO(pNewStringFromString)
QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuffer)
QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuilder)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromUtf16Bytes_BII)
QUICK_ENTRY_POINT_INFO(pJniReadBarrier)
QUICK_ENTRY_POINT_INFO(pReadBarrierMarkReg00)
QUICK_ENTRY_POINT_INFO(pReadBarrierMarkReg01)
@@ -3700,12 +3882,15 @@
os << offset;
}
-void Thread::QuickDeliverException() {
+void Thread::QuickDeliverException(bool skip_method_exit_callbacks) {
// Get exception from thread.
ObjPtr<mirror::Throwable> exception = GetException();
CHECK(exception != nullptr);
if (exception == GetDeoptimizationException()) {
- artDeoptimize(this);
+ // This wasn't a real exception, so just clear it here. If there was an actual exception it
+ // will be recorded in the DeoptimizationContext and it will be restored later.
+ ClearException();
+ artDeoptimize(this, skip_method_exit_callbacks);
UNREACHABLE();
}
@@ -3726,63 +3911,37 @@
// Note: we do this *after* reporting the exception to instrumentation in case it now requires
// deoptimization. It may happen if a debugger is attached and requests new events (single-step,
// breakpoint, ...) when the exception is reported.
- //
- // Note we need to check for both force_frame_pop and force_retry_instruction. The first is
- // expected to happen fairly regularly but the second can only happen if we are using
- // instrumentation trampolines (for example with DDMS tracing). That forces us to do deopt later
- // and see every frame being popped. We don't need to handle it any differently.
- ShadowFrame* cf;
- bool force_deopt = false;
- if (Runtime::Current()->AreNonStandardExitsEnabled() || kIsDebugBuild) {
+ // Frame pop can be requested on a method unwind callback which requires a deopt. We could
+ // potentially check after each unwind callback to see if a frame pop was requested and deopt if
+ // needed. Since this is a debug only feature and this path is only taken when an exception is
+ // thrown, it is not performance critical and we keep it simple by just deopting if method exit
+ // listeners are installed and frame pop feature is supported.
+ bool needs_deopt =
+ instrumentation->HasMethodExitListeners() && Runtime::Current()->AreNonStandardExitsEnabled();
+ if (Dbg::IsForcedInterpreterNeededForException(this) || IsForceInterpreter() || needs_deopt) {
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack();
- cf = visitor.GetCurrentShadowFrame();
- if (cf == nullptr) {
- cf = FindDebuggerShadowFrame(visitor.GetFrameId());
- }
- bool force_frame_pop = cf != nullptr && cf->GetForcePopFrame();
- bool force_retry_instr = cf != nullptr && cf->GetForceRetryInstruction();
- if (kIsDebugBuild && force_frame_pop) {
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- NthCallerVisitor penultimate_visitor(this, 1, false);
- penultimate_visitor.WalkStack();
- ShadowFrame* penultimate_frame = penultimate_visitor.GetCurrentShadowFrame();
- if (penultimate_frame == nullptr) {
- penultimate_frame = FindDebuggerShadowFrame(penultimate_visitor.GetFrameId());
+ if (visitor.GetCurrentQuickFrame() != nullptr) {
+ if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.GetOuterMethod(), visitor.caller_pc)) {
+ // method_type shouldn't matter due to exception handling.
+ const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
+ // Save the exception into the deoptimization context so it can be restored
+ // before entering the interpreter.
+ PushDeoptimizationContext(
+ JValue(),
+ /* is_reference= */ false,
+ exception,
+ /* from_code= */ false,
+ method_type);
+ artDeoptimize(this, skip_method_exit_callbacks);
+ UNREACHABLE();
+ } else {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << visitor.caller->PrettyMethod();
}
- }
- if (force_retry_instr) {
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- }
- force_deopt = force_frame_pop || force_retry_instr;
- }
- if (Dbg::IsForcedInterpreterNeededForException(this) || force_deopt || IsForceInterpreter()) {
- NthCallerVisitor visitor(this, 0, false);
- visitor.WalkStack();
- if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
- // method_type shouldn't matter due to exception handling.
- const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
- // Save the exception into the deoptimization context so it can be restored
- // before entering the interpreter.
- if (force_deopt) {
- VLOG(deopt) << "Deopting " << cf->GetMethod()->PrettyMethod() << " for frame-pop";
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- // Get rid of the exception since we are doing a framepop instead.
- LOG(WARNING) << "Suppressing pending exception for retry-instruction/frame-pop: "
- << exception->Dump();
- ClearException();
- }
- PushDeoptimizationContext(
- JValue(),
- /* is_reference= */ false,
- (force_deopt ? nullptr : exception),
- /* from_code= */ false,
- method_type);
- artDeoptimize(this);
- UNREACHABLE();
- } else if (visitor.caller != nullptr) {
- LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
- << visitor.caller->PrettyMethod();
+ } else {
+ // This is either top of call stack, or shadow frame.
+ DCHECK(visitor.caller == nullptr || visitor.IsShadowFrame());
}
}
@@ -3790,7 +3949,7 @@
// resolution.
ClearException();
QuickExceptionHandler exception_handler(this, false);
- exception_handler.FindCatch(exception);
+ exception_handler.FindCatch(exception, skip_method_exit_callbacks);
if (exception_handler.GetClearException()) {
// Exception was cleared as part of delivery.
DCHECK(!IsExceptionPending());
@@ -3994,7 +4153,7 @@
// (PC shall be known thanks to the runtime frame for throwing SIOOBE).
// Note that JIT does not emit that intrinic implementation.
const void* pc = reinterpret_cast<const void*>(GetCurrentQuickFramePc());
- if (pc != 0u && Runtime::Current()->GetHeap()->IsInBootImageOatFile(pc)) {
+ if (pc != nullptr && Runtime::Current()->GetHeap()->IsInBootImageOatFile(pc)) {
return;
}
}
@@ -4291,9 +4450,6 @@
RootCallbackVisitor visitor_to_callback(visitor, thread_id);
ReferenceMapVisitor<RootCallbackVisitor, kPrecise> mapper(this, &context, visitor_to_callback);
mapper.template WalkStack<StackVisitor::CountTransitions::kNo>(false);
- for (auto& entry : *GetInstrumentationStack()) {
- visitor->VisitRootIfNonNull(&entry.second.this_object_, RootInfo(kRootVMInternal, thread_id));
- }
}
#pragma GCC diagnostic pop
@@ -4460,9 +4616,11 @@
VLOG(threads) << "Protecting stack at " << pregion;
if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) {
if (fatal_on_error) {
- LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. "
+ // b/249586057, LOG(FATAL) times out
+ LOG(ERROR) << "Unable to create protected region in stack for implicit overflow check. "
"Reason: "
<< strerror(errno) << " size: " << kStackOverflowProtectedSize;
+ exit(1);
}
return false;
}
@@ -4496,25 +4654,29 @@
void Thread::DeoptimizeWithDeoptimizationException(JValue* result) {
DCHECK_EQ(GetException(), Thread::GetDeoptimizationException());
ClearException();
- ShadowFrame* shadow_frame =
- PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
DeoptimizationMethodType method_type;
PopDeoptimizationContext(result, &pending_exception, &from_code, &method_type);
SetTopOfStack(nullptr);
- SetTopOfShadowStack(shadow_frame);
// Restore the exception that was pending before deoptimization then interpret the
// deoptimized frames.
if (pending_exception != nullptr) {
SetException(pending_exception);
}
- interpreter::EnterInterpreterFromDeoptimize(this,
- shadow_frame,
- result,
- from_code,
- method_type);
+
+ ShadowFrame* shadow_frame = MaybePopDeoptimizedStackedShadowFrame();
+ // We may not have a shadow frame if we deoptimized at the return of the
+ // quick_to_interpreter_bridge which got directly called by art_quick_invoke_stub.
+ if (shadow_frame != nullptr) {
+ SetTopOfShadowStack(shadow_frame);
+ interpreter::EnterInterpreterFromDeoptimize(this,
+ shadow_frame,
+ result,
+ from_code,
+ method_type);
+ }
}
void Thread::SetAsyncException(ObjPtr<mirror::Throwable> new_exception) {
@@ -4612,8 +4774,7 @@
if (GetPeer() == nullptr) {
return false;
}
- return jni::DecodeArtField(
- WellKnownClasses::java_lang_Thread_systemDaemon)->GetBoolean(GetPeer());
+ return WellKnownClasses::java_lang_Thread_systemDaemon->GetBoolean(GetPeer());
}
std::string Thread::StateAndFlagsAsHexString() const {
@@ -4631,7 +4792,9 @@
CHECK(self_->IsExceptionPending()) << *self_;
ObjPtr<mirror::Throwable> old_suppressed(excp_.Get());
excp_.Assign(self_->GetException());
- LOG(WARNING) << message << "Suppressing old exception: " << old_suppressed->Dump();
+ if (old_suppressed != nullptr) {
+ LOG(WARNING) << message << "Suppressing old exception: " << old_suppressed->Dump();
+ }
self_->ClearException();
}
diff --git a/runtime/thread.h b/runtime/thread.h
index f9303d8..95d06cf 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -38,6 +38,7 @@
#include "handle.h"
#include "handle_scope.h"
#include "interpreter/interpreter_cache.h"
+#include "interpreter/shadow_frame.h"
#include "javaheapprof/javaheapsampler.h"
#include "jvalue.h"
#include "managed_stack.h"
@@ -48,7 +49,9 @@
#include "runtime_stats.h"
#include "thread_state.h"
-class BacktraceMap;
+namespace unwindstack {
+class AndroidLocalUnwinder;
+} // namespace unwindstack
namespace art {
@@ -188,7 +191,7 @@
// This should match RosAlloc::kNumThreadLocalSizeBrackets.
static constexpr size_t kNumRosAllocThreadLocalSizeBracketsInThread = 16;
-static constexpr size_t kSharedMethodHotnessThreshold = 0xffff;
+static constexpr size_t kSharedMethodHotnessThreshold = 0x1fff;
// Thread's stack layout for implicit stack overflow checks:
//
@@ -245,6 +248,9 @@
// TODO: mark as PURE so the compiler may coalesce and remove?
static Thread* Current();
+ // Get the thread from the JNI environment.
+ static Thread* ForEnv(JNIEnv* env);
+
// On a runnable thread, check for pending thread suspension request and handle if pending.
void AllowThreadSuspension() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -270,16 +276,28 @@
// Dumps a one-line summary of thread state (used for operator<<).
void ShortDump(std::ostream& os) const;
+ // Order of threads for ANRs (ANRs can be trimmed, so we print important ones first).
+ enum class DumpOrder : uint8_t {
+ kMain, // Always print the main thread first (there might not be one).
+ kBlocked, // Then print all threads that are blocked due to waiting on lock.
+ kLocked, // Then print all threads that are holding some lock already.
+ kDefault, // Print all other threads which might not be interesting for ANR.
+ };
+
// Dumps the detailed thread state and the thread stack (used for SIGQUIT).
- void Dump(std::ostream& os,
- bool dump_native_stack = true,
- BacktraceMap* backtrace_map = nullptr,
- bool force_dump_stack = false) const
+ DumpOrder Dump(std::ostream& os,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ DumpOrder Dump(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void DumpJavaStack(std::ostream& os,
- bool check_suspended = true,
- bool dump_locks = true) const
+ DumpOrder DumpJavaStack(std::ostream& os,
+ bool check_suspended = true,
+ bool dump_locks = true) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Dumps the SIGQUIT per-thread header. 'thread' can be null for a non-attached thread, in which
@@ -549,8 +567,12 @@
// that needs to be dealt with, false otherwise.
bool ObserveAsyncException() REQUIRES_SHARED(Locks::mutator_lock_);
- // Find catch block and perform long jump to appropriate exception handle
- NO_RETURN void QuickDeliverException() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find catch block and perform long jump to appropriate exception handle. When
+ // is_method_exit_exception is true, the exception was thrown by the method exit callback and we
+ // should not send method unwind for the method on top of the stack since method exit callback was
+ // already called.
+ NO_RETURN void QuickDeliverException(bool is_method_exit_exception = false)
+ REQUIRES_SHARED(Locks::mutator_lock_);
Context* GetLongJumpContext();
void ReleaseLongJumpContext(Context* context) {
@@ -576,8 +598,8 @@
tlsPtr_.managed_stack.SetTopQuickFrame(top_method);
}
- void SetTopOfStackTagged(ArtMethod** top_method) {
- tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method);
+ void SetTopOfStackGenericJniTagged(ArtMethod** top_method) {
+ tlsPtr_.managed_stack.SetTopQuickFrameGenericJniTagged(top_method);
}
void SetTopOfShadowStack(ShadowFrame* top) {
@@ -711,6 +733,16 @@
jobjectArray output_array = nullptr, int* stack_depth = nullptr)
REQUIRES_SHARED(Locks::mutator_lock_);
+ static jint InternalStackTraceToStackFrameInfoArray(
+ const ScopedObjectAccessAlreadyRunnable& soa,
+ jlong mode, // See java.lang.StackStreamFactory for the mode flags
+ jobject internal,
+ jint startLevel,
+ jint batchSize,
+ jint startIndex,
+ jobjectArray output_array) // java.lang.StackFrameInfo[]
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
jobjectArray CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -745,6 +777,13 @@
}
template<PointerSize pointer_size>
+ static constexpr ThreadOffset<pointer_size> TidOffset() {
+ return ThreadOffset<pointer_size>(
+ OFFSETOF_MEMBER(Thread, tls32_) +
+ OFFSETOF_MEMBER(tls_32bit_sized_values, tid));
+ }
+
+ template<PointerSize pointer_size>
static constexpr ThreadOffset<pointer_size> InterruptedOffset() {
return ThreadOffset<pointer_size>(
OFFSETOF_MEMBER(Thread, tls32_) +
@@ -772,6 +811,13 @@
OFFSETOF_MEMBER(tls_32bit_sized_values, is_gc_marking));
}
+ template <PointerSize pointer_size>
+ static constexpr ThreadOffset<pointer_size> DeoptCheckRequiredOffset() {
+ return ThreadOffset<pointer_size>(
+ OFFSETOF_MEMBER(Thread, tls32_) +
+ OFFSETOF_MEMBER(tls_32bit_sized_values, is_deopt_check_required));
+ }
+
static constexpr size_t IsGcMarkingSize() {
return sizeof(tls32_.is_gc_marking);
}
@@ -1023,6 +1069,10 @@
void SetIsGcMarkingAndUpdateEntrypoints(bool is_marking);
+ bool IsDeoptCheckRequired() const { return tls32_.is_deopt_check_required; }
+
+ void SetDeoptCheckRequired(bool flag) { tls32_.is_deopt_check_required = flag; }
+
bool GetWeakRefAccessEnabled() const; // Only safe for current thread.
void SetWeakRefAccessEnabled(bool enabled) {
@@ -1094,7 +1144,8 @@
void AssertHasDeoptimizationContext()
REQUIRES_SHARED(Locks::mutator_lock_);
void PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type);
- ShadowFrame* PopStackedShadowFrame(StackedShadowFrameType type, bool must_be_present = true);
+ ShadowFrame* PopStackedShadowFrame();
+ ShadowFrame* MaybePopDeoptimizedStackedShadowFrame();
// For debugger, find the shadow frame that corresponds to a frame id.
// Or return null if there is none.
@@ -1115,22 +1166,6 @@
void RemoveDebuggerShadowFrameMapping(size_t frame_id)
REQUIRES_SHARED(Locks::mutator_lock_);
- // While getting this map requires shared the mutator lock, manipulating it
- // should actually follow these rules:
- // (1) The owner of this map (the thread) can change it with its mutator lock.
- // (2) Other threads can read this map when the owner is suspended and they
- // hold the mutator lock.
- // (3) Other threads can change this map when owning the mutator lock exclusively.
- //
- // The reason why (3) needs the mutator lock exclusively (and not just having
- // the owner suspended) is that we don't want other threads to concurrently read the map.
- //
- // TODO: Add a class abstraction to express these rules.
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* GetInstrumentationStack()
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return tlsPtr_.instrumentation_stack;
- }
-
std::vector<ArtMethod*>* GetStackTraceSample() const {
DCHECK(!IsAotCompiler());
return tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
@@ -1155,6 +1190,22 @@
tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps;
}
+ uintptr_t* GetMethodTraceBuffer() { return tlsPtr_.method_trace_buffer; }
+
+ size_t* GetMethodTraceIndexPtr() { return &tlsPtr_.method_trace_buffer_index; }
+
+ uintptr_t* SetMethodTraceBuffer(uintptr_t* buffer) {
+ return tlsPtr_.method_trace_buffer = buffer;
+ }
+
+ void ResetMethodTraceBuffer() {
+ if (tlsPtr_.method_trace_buffer != nullptr) {
+ delete[] tlsPtr_.method_trace_buffer;
+ }
+ tlsPtr_.method_trace_buffer = nullptr;
+ tlsPtr_.method_trace_buffer_index = 0;
+ }
+
uint64_t GetTraceClockBase() const {
return tls64_.trace_clock_base;
}
@@ -1305,11 +1356,12 @@
bool IncrementMakeVisiblyInitializedCounter() {
tls32_.make_visibly_initialized_counter += 1u;
- return tls32_.make_visibly_initialized_counter == kMakeVisiblyInitializedCounterTriggerCount;
- }
-
- void ClearMakeVisiblyInitializedCounter() {
- tls32_.make_visibly_initialized_counter = 0u;
+ DCHECK_LE(tls32_.make_visibly_initialized_counter, kMakeVisiblyInitializedCounterTriggerCount);
+ if (tls32_.make_visibly_initialized_counter == kMakeVisiblyInitializedCounterTriggerCount) {
+ tls32_.make_visibly_initialized_counter = 0u;
+ return true;
+ }
+ return false;
}
void PushVerifier(verifier::MethodVerifier* verifier);
@@ -1354,10 +1406,9 @@
// Set to the read barrier marking entrypoints to be non-null.
void SetReadBarrierEntrypoints();
- static jobject CreateCompileTimePeer(JNIEnv* env,
- const char* name,
- bool as_daemon,
- jobject thread_group)
+ ObjPtr<mirror::Object> CreateCompileTimePeer(const char* name,
+ bool as_daemon,
+ jobject thread_group)
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE InterpreterCache* GetInterpreterCache() {
@@ -1437,11 +1488,10 @@
void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
template<bool kTransactionActive>
- static void InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::Object> peer,
- jboolean thread_is_daemon,
- jobject thread_group,
- jobject thread_name,
+ static void InitPeer(ObjPtr<mirror::Object> peer,
+ bool as_daemon,
+ ObjPtr<mirror::Object> thread_group,
+ ObjPtr<mirror::String> thread_name,
jint thread_priority)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1487,10 +1537,14 @@
void VerifyStackImpl() REQUIRES_SHARED(Locks::mutator_lock_);
void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_);
- void DumpStack(std::ostream& os,
- bool dump_native_stack = true,
- BacktraceMap* backtrace_map = nullptr,
- bool force_dump_stack = false) const
+ DumpOrder DumpStack(std::ostream& os,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ DumpOrder DumpStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Out-of-line conveniences for debugging in gdb.
@@ -1503,10 +1557,8 @@
static NO_INLINE void* CreateCallbackWithUffdGc(void* arg);
static void* CreateCallback(void* arg);
- void HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(Locks::mutator_lock_);
- void RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ void HandleUncaughtExceptions() REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveFromThreadGroup() REQUIRES_SHARED(Locks::mutator_lock_);
// Initialize a thread.
//
@@ -1720,6 +1772,7 @@
thread_exit_check_count(0),
is_transitioning_to_runnable(false),
is_gc_marking(false),
+ is_deopt_check_required(false),
weak_ref_access_enabled(WeakRefAccessState::kVisiblyEnabled),
disable_thread_flip_count(0),
user_code_suspend_count(0),
@@ -1774,6 +1827,12 @@
// GC roots.
bool32_t is_gc_marking;
+ // True if we need to check for deoptimization when returning from the runtime functions. This
+ // is required only when a class is redefined to prevent executing code that has field offsets
+ // embedded. For non-debuggable apps redefinition is not allowed and this flag should always be
+ // set to false.
+ bool32_t is_deopt_check_required;
+
// Thread "interrupted" status; stays raised until queried or thrown.
Atomic<bool32_t> interrupted;
@@ -1861,19 +1920,18 @@
top_handle_scope(nullptr),
class_loader_override(nullptr),
long_jump_context(nullptr),
- instrumentation_stack(nullptr),
stacked_shadow_frame_record(nullptr),
deoptimization_context_stack(nullptr),
frame_id_to_shadow_frame(nullptr),
name(nullptr),
pthread_self(0),
last_no_thread_suspension_cause(nullptr),
- checkpoint_function(nullptr),
thread_local_start(nullptr),
thread_local_pos(nullptr),
thread_local_end(nullptr),
thread_local_limit(nullptr),
thread_local_objects(0),
+ checkpoint_function(nullptr),
thread_local_alloc_stack_top(nullptr),
thread_local_alloc_stack_end(nullptr),
mutator_lock(nullptr),
@@ -1881,7 +1939,9 @@
method_verifier(nullptr),
thread_local_mark_stack(nullptr),
async_exception(nullptr),
- top_reflective_handle_scope(nullptr) {
+ top_reflective_handle_scope(nullptr),
+ method_trace_buffer(nullptr),
+ method_trace_buffer_index(0) {
std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr);
}
@@ -1954,14 +2014,6 @@
// Thread local, lazily allocated, long jump context. Used to deliver exceptions.
Context* long_jump_context;
- // Additional stack used by method instrumentation to store method and return pc values.
- // Stored as a pointer since std::map is not PACKED.
- // !DO NOT CHANGE! to std::unordered_map: the users of this map require an
- // ordered iteration on the keys (which are stack addresses).
- // Also see Thread::GetInstrumentationStack for the requirements on
- // manipulating and reading this map.
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* instrumentation_stack;
-
// For gc purpose, a shadow frame record stack that keeps track of:
// 1) shadow frames under construction.
// 2) deoptimization shadow frames.
@@ -1987,10 +2039,6 @@
// If no_thread_suspension_ is > 0, what is causing that assertion.
const char* last_no_thread_suspension_cause;
- // Pending checkpoint function or null if non-pending. If this checkpoint is set and someone\
- // requests another checkpoint, it goes to the checkpoint overflow list.
- Closure* checkpoint_function GUARDED_BY(Locks::thread_suspend_count_lock_);
-
// Pending barriers that require passing or NULL if non-pending. Installation guarding by
// Locks::thread_suspend_count_lock_.
// They work effectively as art::Barrier, but implemented directly using AtomicInteger and futex
@@ -2011,6 +2059,10 @@
size_t thread_local_objects;
+ // Pending checkpoint function or null if non-pending. If this checkpoint is set and someone\
+ // requests another checkpoint, it goes to the checkpoint overflow list.
+ Closure* checkpoint_function GUARDED_BY(Locks::thread_suspend_count_lock_);
+
// Entrypoint function pointers.
// TODO: move this to more of a global offset table model to avoid per-thread duplication.
JniEntryPoints jni_entrypoints;
@@ -2044,6 +2096,12 @@
// Top of the linked-list for reflective-handle scopes or null if none.
BaseReflectiveHandleScope* top_reflective_handle_scope;
+
+ // Pointer to a thread-local buffer for method tracing.
+ uintptr_t* method_trace_buffer;
+
+ // The index of the next free entry in method_trace_buffer.
+ size_t method_trace_buffer_index;
} tlsPtr_;
// Small thread-local cache to be used from the interpreter.
@@ -2076,8 +2134,10 @@
SafeMap<std::string, std::unique_ptr<TLSData>, std::less<>> custom_tls_
GUARDED_BY(Locks::custom_tls_lock_);
-#ifndef __BIONIC__
- __attribute__((tls_model("initial-exec")))
+#if !defined(__BIONIC__)
+#if !defined(ANDROID_HOST_MUSL)
+ __attribute__((tls_model("initial-exec")))
+#endif
static thread_local Thread* self_tls_;
#endif
@@ -2160,17 +2220,18 @@
class ScopedStackedShadowFramePusher {
public:
- ScopedStackedShadowFramePusher(Thread* self, ShadowFrame* sf, StackedShadowFrameType type)
- : self_(self), type_(type) {
- self_->PushStackedShadowFrame(sf, type);
+ ScopedStackedShadowFramePusher(Thread* self, ShadowFrame* sf) : self_(self), sf_(sf) {
+ DCHECK_EQ(sf->GetLink(), nullptr);
+ self_->PushStackedShadowFrame(sf, StackedShadowFrameType::kShadowFrameUnderConstruction);
}
~ScopedStackedShadowFramePusher() {
- self_->PopStackedShadowFrame(type_);
+ ShadowFrame* sf = self_->PopStackedShadowFrame();
+ DCHECK_EQ(sf, sf_);
}
private:
Thread* const self_;
- const StackedShadowFrameType type_;
+ ShadowFrame* const sf_;
DISALLOW_COPY_AND_ASSIGN(ScopedStackedShadowFramePusher);
};
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index e8d01cc..0f41e2f 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -42,8 +42,10 @@
IsAligned<kPageSize>(old_ss.ss_sp) &&
IsAligned<kPageSize>(old_ss.ss_size)) {
CHECK_EQ(old_ss.ss_flags & SS_ONSTACK, 0);
- result = madvise(old_ss.ss_sp, old_ss.ss_size, MADV_DONTNEED);
- CHECK_EQ(result, 0);
+ // Note: We're testing and benchmarking ART on devices with old kernels
+ // which may not support `MADV_FREE`, so we do not check the result.
+ // It should succeed on devices with Android 12+.
+ madvise(old_ss.ss_sp, old_ss.ss_size, MADV_FREE);
}
}
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index c522be3..8fa7bde 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -20,14 +20,17 @@
#include <sys/types.h>
#include <unistd.h>
+#include <map>
#include <sstream>
+#include <tuple>
#include <vector>
#include "android-base/stringprintf.h"
-#include "backtrace/BacktraceMap.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
+#include "unwindstack/AndroidUnwinder.h"
+#include "art_field-inl.h"
#include "base/aborting.h"
#include "base/histogram-inl.h"
#include "base/mutex-inl.h"
@@ -42,8 +45,10 @@
#include "gc_root.h"
#include "jni/jni_internal.h"
#include "lock_word.h"
+#include "mirror/string.h"
#include "monitor.h"
#include "native_stack_dump.h"
+#include "obj_ptr-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "trace.h"
@@ -123,10 +128,10 @@
void ThreadList::DumpNativeStacks(std::ostream& os) {
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
+ unwindstack::AndroidLocalUnwinder unwinder;
for (const auto& thread : list_) {
os << "DUMPING THREAD " << thread->GetTid() << "\n";
- DumpNativeStack(os, thread->GetTid(), map.get(), "\t");
+ DumpNativeStack(os, unwinder, thread->GetTid(), "\t");
os << "\n";
}
}
@@ -152,7 +157,7 @@
// refactor DumpState to avoid skipping analysis.
Thread::DumpState(os, nullptr, tid);
if (dump_native_stack) {
- DumpNativeStack(os, tid, nullptr, " native: ");
+ DumpNativeStack(os, tid, " native: ");
}
os << std::endl;
}
@@ -189,16 +194,14 @@
// A closure used by Thread::Dump.
class DumpCheckpoint final : public Closure {
public:
- DumpCheckpoint(std::ostream* os, bool dump_native_stack)
- : os_(os),
+ DumpCheckpoint(bool dump_native_stack)
+ : lock_("Dump checkpoint lock", kGenericBottomLock),
+ os_(),
// Avoid verifying count in case a thread doesn't end up passing through the barrier.
// This avoids a SIGABRT that would otherwise happen in the destructor.
barrier_(0, /*verify_count_on_shutdown=*/false),
- backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr),
+ unwinder_(std::vector<std::string>{}, std::vector<std::string> {"oat", "odex"}),
dump_native_stack_(dump_native_stack) {
- if (backtrace_map_ != nullptr) {
- backtrace_map_->SetSuffixesToIgnore(std::vector<std::string> { "oat", "odex" });
- }
}
void Run(Thread* thread) override {
@@ -207,18 +210,28 @@
Thread* self = Thread::Current();
CHECK(self != nullptr);
std::ostringstream local_os;
+ Thread::DumpOrder dump_order;
{
ScopedObjectAccess soa(self);
- thread->Dump(local_os, dump_native_stack_, backtrace_map_.get());
+ dump_order = thread->Dump(local_os, unwinder_, dump_native_stack_);
}
{
- // Use the logging lock to ensure serialization when writing to the common ostream.
- MutexLock mu(self, *Locks::logging_lock_);
- *os_ << local_os.str() << std::endl;
+ MutexLock mu(self, lock_);
+ // Sort, so that the most interesting threads for ANR are printed first (ANRs can be trimmed).
+ std::pair<Thread::DumpOrder, uint32_t> sort_key(dump_order, thread->GetThreadId());
+ os_.emplace(sort_key, std::move(local_os));
}
barrier_.Pass(self);
}
+ // Called at the end to print all the dumps in sequential prioritized order.
+ void Dump(Thread* self, std::ostream& os) {
+ MutexLock mu(self, lock_);
+ for (const auto& it : os_) {
+ os << it.second.str() << std::endl;
+ }
+ }
+
void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
Thread* self = Thread::Current();
ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun);
@@ -231,12 +244,14 @@
}
private:
- // The common stream that will accumulate all the dumps.
- std::ostream* const os_;
+ // Storage for the per-thread dumps (guarded by lock since they are generated in parallel).
+ // Map is used to obtain sorted order. The key is unique, but use multimap just in case.
+ Mutex lock_;
+ std::multimap<std::pair<Thread::DumpOrder, uint32_t>, std::ostringstream> os_ GUARDED_BY(lock_);
// The barrier to be passed through and for the requestor to wait upon.
Barrier barrier_;
// A backtrace map, so that all threads use a shared info and don't reacquire/parse separately.
- std::unique_ptr<BacktraceMap> backtrace_map_;
+ unwindstack::AndroidLocalUnwinder unwinder_;
// Whether we should dump the native stack.
const bool dump_native_stack_;
};
@@ -248,7 +263,7 @@
os << "DALVIK THREADS (" << list_.size() << "):\n";
}
if (self != nullptr) {
- DumpCheckpoint checkpoint(&os, dump_native_stack);
+ DumpCheckpoint checkpoint(dump_native_stack);
size_t threads_running_checkpoint;
{
// Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time.
@@ -258,6 +273,7 @@
if (threads_running_checkpoint != 0) {
checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
}
+ checkpoint.Dump(self, os);
} else {
DumpUnattachedThreads(os, dump_native_stack);
}
@@ -485,7 +501,6 @@
// Assume it's stuck and safe to dump its stack.
thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT),
/*dump_native_stack=*/ true,
- /*backtrace_map=*/ nullptr,
/*force_dump_stack=*/ true);
}
}
@@ -850,20 +865,16 @@
return true;
}
-static void ThreadSuspendByPeerWarning(Thread* self,
+static void ThreadSuspendByPeerWarning(ScopedObjectAccess& soa,
LogSeverity severity,
const char* message,
- jobject peer) {
- JNIEnvExt* env = self->GetJniEnv();
- ScopedLocalRef<jstring>
- scoped_name_string(env, static_cast<jstring>(env->GetObjectField(
- peer, WellKnownClasses::java_lang_Thread_name)));
- ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
- if (scoped_name_chars.c_str() == nullptr) {
- LOG(severity) << message << ": " << peer;
- env->ExceptionClear();
+ jobject peer) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> name =
+ WellKnownClasses::java_lang_Thread_name->GetObject(soa.Decode<mirror::Object>(peer));
+ if (name == nullptr) {
+ LOG(severity) << message << ": " << peer;
} else {
- LOG(severity) << message << ": " << peer << ":" << scoped_name_chars.c_str();
+ LOG(severity) << message << ": " << peer << ":" << name->AsString()->ToModifiedUtf8();
}
}
@@ -901,7 +912,7 @@
reason);
DCHECK(updated);
}
- ThreadSuspendByPeerWarning(self,
+ ThreadSuspendByPeerWarning(soa,
::android::base::WARNING,
"No such thread for suspend",
peer);
@@ -954,7 +965,7 @@
const uint64_t total_delay = NanoTime() - start_time;
if (total_delay >= thread_suspend_timeout_ns_) {
if (suspended_thread == nullptr) {
- ThreadSuspendByPeerWarning(self,
+ ThreadSuspendByPeerWarning(soa,
::android::base::FATAL,
"Failed to issue suspend request",
peer);
@@ -966,7 +977,7 @@
// Explicitly release thread_suspend_count_lock_; we haven't held it for long, so
// seeing threads blocked on it is not informative.
Locks::thread_suspend_count_lock_->Unlock(self);
- ThreadSuspendByPeerWarning(self,
+ ThreadSuspendByPeerWarning(soa,
::android::base::FATAL,
"Thread suspension timed out",
peer);
@@ -1290,6 +1301,10 @@
DCHECK_EQ(self, Thread::Current());
CHECK_NE(self->GetState(), ThreadState::kRunnable);
Locks::mutator_lock_->AssertNotHeld(self);
+ if (self->tls32_.disable_thread_flip_count != 0) {
+ LOG(FATAL) << "Incomplete PrimitiveArrayCritical section at exit: " << *self << "count = "
+ << self->tls32_.disable_thread_flip_count;
+ }
VLOG(threads) << "ThreadList::Unregister() " << *self;
@@ -1319,7 +1334,7 @@
std::string thread_name;
self->GetThreadName(thread_name);
std::ostringstream os;
- DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), " native: ", nullptr);
LOG(ERROR) << "Request to unregister unattached thread " << thread_name << "\n" << os.str();
break;
} else {
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index dc99044..92ac845 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -253,6 +253,11 @@
started_ = false;
}
+bool ThreadPool::HasStarted(Thread* self) {
+ MutexLock mu(self, task_queue_lock_);
+ return started_;
+}
+
Task* ThreadPool::GetTask(Thread* self) {
MutexLock mu(self, task_queue_lock_);
while (!IsShuttingDown()) {
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index b9e5a97..5c75733 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -123,6 +123,9 @@
// Do not allow workers to grab any new tasks.
void StopWorkers(Thread* self) REQUIRES(!task_queue_lock_);
+ // Returns if the thread pool has started.
+ bool HasStarted(Thread* self) REQUIRES(!task_queue_lock_);
+
// Add a new task, the first available started worker will process it. Does not delete the task
// after running it, it is the caller's responsibility.
void AddTask(Thread* self, Task* task) REQUIRES(!task_queue_lock_);
diff --git a/runtime/trace.cc b/runtime/trace.cc
index ec61726..4e39eb3 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -393,17 +393,12 @@
// Enable count of allocs if specified in the flags.
bool enable_stats = false;
- if (runtime->GetJit() != nullptr) {
- // TODO b/110263880 It would be better if we didn't need to do this.
- // Since we need to hold the method entrypoint across a suspend to ensure instrumentation
- // hooks are called correctly we have to disable jit-gc to ensure that the entrypoint doesn't
- // go away. Furthermore we need to leave this off permanently since one could get the same
- // effect by causing this to be toggled on and off.
- runtime->GetJit()->GetCodeCache()->SetGarbageCollectCode(false);
- }
-
// Create Trace object.
{
+ // Suspend JIT here since we are switching runtime to debuggable. Debuggable runtimes cannot use
+ // JITed code from before so we need to invalidated all JITed code here. Enter suspend JIT scope
+ // to prevent any races with ongoing JIT compilations.
+ jit::ScopedJitSuspend suspend_jit;
// Required since EnableMethodTracing calls ConfigureStubs which visits class linker classes.
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseInstrumentation,
@@ -421,6 +416,17 @@
"Sampling profiler thread");
the_trace_->interval_us_ = interval_us;
} else {
+ if (!runtime->IsJavaDebuggable()) {
+ art::jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr) {
+ jit->GetCodeCache()->InvalidateAllCompiledCode();
+ jit->GetCodeCache()->TransitionToDebuggable();
+ jit->GetJitCompiler()->SetDebuggableCompilerOption(true);
+ }
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggable);
+ runtime->GetInstrumentation()->UpdateEntrypointsForDebuggable();
+ runtime->DeoptimizeBootImage();
+ }
runtime->GetInstrumentation()->AddListener(
the_trace_,
instrumentation::Instrumentation::kMethodEntered |
@@ -431,8 +437,8 @@
// we know that inlining and other problematic optimizations are disabled. We might just
// want to use the trampolines anyway since it is faster. It makes the story with disabling
// jit-gc more complex though.
- runtime->GetInstrumentation()->EnableMethodTracing(
- kTracerInstrumentationKey, /*needs_interpreter=*/!runtime->IsJavaDebuggable());
+ runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey,
+ /*needs_interpreter=*/false);
}
}
}
@@ -474,6 +480,7 @@
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseInstrumentation,
gc::kCollectorTypeInstrumentation);
+ jit::ScopedJitSuspend suspend_jit;
ScopedSuspendAll ssa(__FUNCTION__);
if (the_trace->trace_mode_ == TraceMode::kSampling) {
@@ -486,6 +493,7 @@
instrumentation::Instrumentation::kMethodExited |
instrumentation::Instrumentation::kMethodUnwind);
runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
+ runtime->GetInstrumentation()->MaybeSwitchRuntimeDebugState(self);
}
}
// At this point, code may read buf_ as it's writers are shutdown
@@ -515,6 +523,11 @@
}
}
+void Trace::FlushThreadBuffer(Thread* self) {
+ MutexLock mu(self, *Locks::trace_lock_);
+ the_trace_->FlushStreamingBuffer(self);
+}
+
void Trace::Abort() {
// Do not write anything anymore.
StopTracing(false, false);
@@ -548,6 +561,28 @@
}
static constexpr size_t kMinBufSize = 18U; // Trace header is up to 18B.
+// Size of per-thread buffer size. The value is chosen arbitrarily. This value
+// should be greater than kMinBufSize.
+static constexpr size_t kPerThreadBufSize = 512 * 1024;
+static_assert(kPerThreadBufSize > kMinBufSize);
+
+namespace {
+
+TraceClockSource GetClockSourceFromFlags(int flags) {
+ bool need_wall = flags & Trace::TraceFlag::kTraceClockSourceWallClock;
+ bool need_thread_cpu = flags & Trace::TraceFlag::kTraceClockSourceThreadCpu;
+ if (need_wall && need_thread_cpu) {
+ return TraceClockSource::kDual;
+ } else if (need_wall) {
+ return TraceClockSource::kWall;
+ } else if (need_thread_cpu) {
+ return TraceClockSource::kThreadCpu;
+ } else {
+ return kDefaultTraceClockSource;
+ }
+}
+
+} // namespace
Trace::Trace(File* trace_file,
size_t buffer_size,
@@ -556,11 +591,16 @@
TraceMode trace_mode)
: trace_file_(trace_file),
buf_(new uint8_t[std::max(kMinBufSize, buffer_size)]()),
- flags_(flags), trace_output_mode_(output_mode), trace_mode_(trace_mode),
- clock_source_(default_clock_source_),
+ flags_(flags),
+ trace_output_mode_(output_mode),
+ trace_mode_(trace_mode),
+ clock_source_(GetClockSourceFromFlags(flags)),
buffer_size_(std::max(kMinBufSize, buffer_size)),
- start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()),
- overflow_(false), interval_us_(0), streaming_lock_(nullptr),
+ start_time_(MicroTime()),
+ clock_overhead_ns_(GetClockOverheadNanoSeconds()),
+ overflow_(false),
+ interval_us_(0),
+ streaming_lock_(nullptr),
unique_methods_lock_(new Mutex("unique methods lock", kTracingUniqueMethodsLock)) {
CHECK_IMPLIES(trace_file == nullptr, output_mode == TraceOutputMode::kDDMS);
@@ -584,7 +624,12 @@
if (output_mode == TraceOutputMode::kStreaming) {
streaming_lock_ = new Mutex("tracing lock", LockLevel::kTracingStreamingLock);
- seen_threads_.reset(new ThreadIDBitSet());
+ // Flush the header information to the file. We use a per thread buffer, so
+ // it is easier to just write the header information directly to file.
+ if (!trace_file_->WriteFully(buf_.get(), kTraceHeaderLength)) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
+ cur_offset_.store(0, std::memory_order_relaxed);
}
}
@@ -664,20 +709,30 @@
std::string header(os.str());
if (trace_output_mode_ == TraceOutputMode::kStreaming) {
- // Protect access to buf_ and satisfy sanitizer for calls to WriteBuf / FlushBuf.
- MutexLock mu(Thread::Current(), *streaming_lock_);
+ // Flush thread specific buffer from all threads.
+ {
+ MutexLock tl_lock(Thread::Current(), *Locks::thread_list_lock_);
+ for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
+ if (thread->GetMethodTraceBuffer() != nullptr) {
+ FlushStreamingBuffer(thread);
+ thread->ResetMethodTraceBuffer();
+ }
+ }
+ }
+ // It is expected that this method is called when all other threads are suspended, so there
+ // cannot be any writes to trace_file_ after finish tracing.
// Write a special token to mark the end of trace records and the start of
// trace summary.
uint8_t buf[7];
Append2LE(buf, 0);
buf[2] = kOpTraceSummary;
Append4LE(buf + 3, static_cast<uint32_t>(header.length()));
- WriteToBuf(buf, sizeof(buf));
// Write the trace summary. The summary is identical to the file header when
// the output mode is not streaming (except for methods).
- WriteToBuf(reinterpret_cast<const uint8_t*>(header.c_str()), header.length());
- // Flush the buffer, which may include some trace records before the summary.
- FlushBuf();
+ if (!trace_file_->WriteFully(buf, sizeof(buf)) ||
+ !trace_file_->WriteFully(header.c_str(), header.length())) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
} else {
if (trace_file_.get() == nullptr) {
std::vector<uint8_t> data;
@@ -757,7 +812,6 @@
}
void Trace::MethodUnwind(Thread* thread,
- Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method,
uint32_t dex_pc ATTRIBUTE_UNUSED) {
uint32_t thread_clock_diff = 0;
@@ -819,18 +873,6 @@
return false;
}
-bool Trace::RegisterThread(Thread* thread) {
- pid_t tid = thread->GetTid();
- CHECK_LT(0U, static_cast<uint32_t>(tid));
- CHECK_LT(static_cast<uint32_t>(tid), kMaxThreadIdNumber);
-
- if (!(*seen_threads_)[tid]) {
- seen_threads_->set(tid);
- return true;
- }
- return false;
-}
-
std::string Trace::GetMethodLine(ArtMethod* method) {
method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
return StringPrintf("%#x\t%s\t%s\t%s\t%s\n", (EncodeTraceMethod(method) << TraceActionBits),
@@ -838,54 +880,155 @@
method->GetSignature().ToString().c_str(), method->GetDeclaringClassSourceFile());
}
-void Trace::WriteToBuf(const uint8_t* src, size_t src_size) {
- // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode.
- int32_t old_offset = cur_offset_.load(std::memory_order_relaxed);
- int32_t new_offset = old_offset + static_cast<int32_t>(src_size);
- if (dchecked_integral_cast<size_t>(new_offset) > buffer_size_) {
- // Flush buffer.
- if (!trace_file_->WriteFully(buf_.get(), old_offset)) {
- PLOG(WARNING) << "Failed streaming a tracing event.";
- }
+void Trace::RecordStreamingMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
+ uintptr_t* method_trace_buffer = thread->GetMethodTraceBuffer();
+ size_t* current_offset = thread->GetMethodTraceIndexPtr();
+ // Initialize the buffer lazily. It's just simpler to keep the creation at one place.
+ if (method_trace_buffer == nullptr) {
+ method_trace_buffer = new uintptr_t[std::max(kMinBufSize, kPerThreadBufSize)]();
+ thread->SetMethodTraceBuffer(method_trace_buffer);
+ *current_offset = 0;
- // Check whether the data is too large for the buffer, then write immediately.
- if (src_size >= buffer_size_) {
- if (!trace_file_->WriteFully(src, src_size)) {
+ // This is the first event from this thread, so first record information about the thread.
+ std::string thread_name;
+ thread->GetThreadName(thread_name);
+ static constexpr size_t kThreadNameHeaderSize = 7;
+ uint8_t header[kThreadNameHeaderSize];
+ Append2LE(header, 0);
+ header[2] = kOpNewThread;
+ // We use only 16 bits to encode thread id. On Android, we don't expect to use more than
+ // 16-bits for a Tid. For 32-bit platforms it is always ensured we use less than 16 bits.
+ // See __check_max_thread_id in bionic for more details. Even on 64-bit the max threads
+ // is currently less than 65536.
+ // TODO(mythria): On host, we know thread ids can be greater than 16 bits. Consider adding
+ // a map similar to method ids.
+ DCHECK(!kIsTargetBuild || thread->GetTid() < (1 << 16));
+ Append2LE(header + 3, static_cast<uint16_t>(thread->GetTid()));
+ Append2LE(header + 5, static_cast<uint16_t>(thread_name.length()));
+
+ {
+ MutexLock mu(Thread::Current(), *streaming_lock_);
+ if (!trace_file_->WriteFully(header, kThreadNameHeaderSize) ||
+ !trace_file_->WriteFully(reinterpret_cast<const uint8_t*>(thread_name.c_str()),
+ thread_name.length())) {
PLOG(WARNING) << "Failed streaming a tracing event.";
}
- cur_offset_.store(0, std::memory_order_relaxed); // Buffer is empty now.
- return;
+ }
+ }
+
+ size_t required_entries = (clock_source_ == TraceClockSource::kDual) ? 4 : 3;
+ if (*current_offset + required_entries >= kPerThreadBufSize) {
+ // We don't have space for further entries. Flush the contents of the buffer and reuse the
+ // buffer to store contents. Reset the index to the start of the buffer.
+ FlushStreamingBuffer(thread);
+ *current_offset = 0;
+ }
+
+ // Record entry in per-thread trace buffer.
+ method_trace_buffer[*current_offset] = reinterpret_cast<uintptr_t>(method);
+ *current_offset += 1;
+ // TODO(mythria): We only need two bits to record the action. Consider merging
+ // it with the method entry to save space.
+ method_trace_buffer[*current_offset] = action;
+ *current_offset += 1;
+ if (UseThreadCpuClock()) {
+ method_trace_buffer[*current_offset] = thread_clock_diff;
+ *current_offset += 1;
+ }
+ if (UseWallClock()) {
+ method_trace_buffer[*current_offset] = wall_clock_diff;
+ *current_offset += 1;
+ }
+}
+
+void Trace::WriteToBuf(uint8_t* header,
+ size_t header_size,
+ const std::string& data,
+ size_t* current_index,
+ uint8_t* buffer,
+ size_t buffer_size) {
+ EnsureSpace(buffer, current_index, buffer_size, header_size);
+ memcpy(buffer + *current_index, header, header_size);
+ *current_index += header_size;
+
+ EnsureSpace(buffer, current_index, buffer_size, data.length());
+ if (data.length() < buffer_size) {
+ memcpy(buffer + *current_index, reinterpret_cast<const uint8_t*>(data.c_str()), data.length());
+ *current_index += data.length();
+ } else {
+ // The data is larger than buffer, so write directly to the file. EnsureSpace should have
+ // flushed any data in the buffer.
+ DCHECK_EQ(*current_index, 0U);
+ if (!trace_file_->WriteFully(reinterpret_cast<const uint8_t*>(data.c_str()), data.length())) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
+ }
+}
+
+void Trace::FlushStreamingBuffer(Thread* thread) {
+ // Take a streaming_lock_ to serialize writes across threads. We also need to allocate a unique
+ // method id for each method. We do that by maintaining a map from id to method for each newly
+ // seen method (see RegisterMethod). streaming_lock_ also is required to serialize these.
+ MutexLock mu(Thread::Current(), *streaming_lock_);
+ uintptr_t* method_trace_buffer = thread->GetMethodTraceBuffer();
+ // Create a temporary buffer to encode the trace events from the specified thread.
+ size_t buffer_size = kPerThreadBufSize;
+ size_t current_index = 0;
+ std::unique_ptr<uint8_t[]> buffer(new uint8_t[std::max(kMinBufSize, buffer_size)]);
+
+ size_t num_entries = *(thread->GetMethodTraceIndexPtr());
+ for (size_t entry_index = 0; entry_index < num_entries;) {
+ ArtMethod* method = reinterpret_cast<ArtMethod*>(method_trace_buffer[entry_index++]);
+ TraceAction action = DecodeTraceAction(method_trace_buffer[entry_index++]);
+ uint32_t thread_time = 0;
+ uint32_t wall_time = 0;
+ if (UseThreadCpuClock()) {
+ thread_time = method_trace_buffer[entry_index++];
+ }
+ if (UseWallClock()) {
+ wall_time = method_trace_buffer[entry_index++];
}
- old_offset = 0;
- new_offset = static_cast<int32_t>(src_size);
+ // If we haven't seen this method before record information about the method.
+ if (RegisterMethod(method)) {
+ // Write a special block with the name.
+ std::string method_line(GetMethodLine(method));
+ static constexpr size_t kMethodNameHeaderSize = 5;
+ uint8_t method_header[kMethodNameHeaderSize];
+ DCHECK_LT(kMethodNameHeaderSize, kPerThreadBufSize);
+ Append2LE(method_header, 0);
+ method_header[2] = kOpNewMethod;
+ Append2LE(method_header + 3, static_cast<uint16_t>(method_line.length()));
+ WriteToBuf(method_header,
+ kMethodNameHeaderSize,
+ method_line,
+ ¤t_index,
+ buffer.get(),
+ buffer_size);
+ }
+
+ const size_t record_size = GetRecordSize(clock_source_);
+ DCHECK_LT(record_size, kPerThreadBufSize);
+ EnsureSpace(buffer.get(), ¤t_index, buffer_size, record_size);
+ EncodeEventEntry(buffer.get() + current_index, thread, method, action, thread_time, wall_time);
+ current_index += record_size;
}
- cur_offset_.store(new_offset, std::memory_order_relaxed);
- // Fill in data.
- memcpy(buf_.get() + old_offset, src, src_size);
+
+ // Flush the contents of buffer to file.
+ if (!trace_file_->WriteFully(buffer.get(), current_index)) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
}
-void Trace::FlushBuf() {
- // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode.
- int32_t offset = cur_offset_.load(std::memory_order_relaxed);
- if (!trace_file_->WriteFully(buf_.get(), offset)) {
- PLOG(WARNING) << "Failed flush the remaining data in streaming.";
- }
- cur_offset_.store(0, std::memory_order_relaxed);
-}
-
-void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method,
- instrumentation::Instrumentation::InstrumentationEvent event,
- uint32_t thread_clock_diff, uint32_t wall_clock_diff) {
- // This method is called in both tracing modes (method and
- // sampling). In sampling mode, this method is only called by the
- // sampling thread. In method tracing mode, it can be called
- // concurrently.
-
- // Ensure we always use the non-obsolete version of the method so that entry/exit events have the
- // same pointer value.
- method = method->GetNonObsoleteMethod();
-
+void Trace::RecordMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
// Advance cur_offset_ atomically.
int32_t new_offset;
int32_t old_offset = 0;
@@ -893,20 +1036,40 @@
// In the non-streaming case, we do a busy loop here trying to get
// an offset to write our record and advance cur_offset_ for the
// next use.
- if (trace_output_mode_ != TraceOutputMode::kStreaming) {
- // Although multiple threads can call this method concurrently,
- // the compare_exchange_weak here is still atomic (by definition).
- // A succeeding update is visible to other cores when they pass
- // through this point.
- old_offset = cur_offset_.load(std::memory_order_relaxed); // Speculative read
- do {
- new_offset = old_offset + GetRecordSize(clock_source_);
- if (static_cast<size_t>(new_offset) > buffer_size_) {
- overflow_ = true;
- return;
- }
- } while (!cur_offset_.compare_exchange_weak(old_offset, new_offset, std::memory_order_relaxed));
- }
+ // Although multiple threads can call this method concurrently,
+ // the compare_exchange_weak here is still atomic (by definition).
+ // A succeeding update is visible to other cores when they pass
+ // through this point.
+ old_offset = cur_offset_.load(std::memory_order_relaxed); // Speculative read
+ do {
+ new_offset = old_offset + GetRecordSize(clock_source_);
+ if (static_cast<size_t>(new_offset) > buffer_size_) {
+ overflow_ = true;
+ return;
+ }
+ } while (!cur_offset_.compare_exchange_weak(old_offset, new_offset, std::memory_order_relaxed));
+
+ // Write data into the tracing buffer (if not streaming) or into a
+ // small buffer on the stack (if streaming) which we'll put into the
+ // tracing buffer below.
+ //
+ // These writes to the tracing buffer are synchronised with the
+ // future reads that (only) occur under FinishTracing(). The callers
+ // of FinishTracing() acquire locks and (implicitly) synchronise
+ // the buffer memory.
+ uint8_t* ptr;
+ ptr = buf_.get() + old_offset;
+ EncodeEventEntry(ptr, thread, method, action, thread_clock_diff, wall_clock_diff);
+}
+
+void Trace::LogMethodTraceEvent(Thread* thread,
+ ArtMethod* method,
+ instrumentation::Instrumentation::InstrumentationEvent event,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
+ // This method is called in both tracing modes (method and sampling). In sampling mode, this
+ // method is only called by the sampling thread. In method tracing mode, it can be called
+ // concurrently.
TraceAction action = kTraceMethodEnter;
switch (event) {
@@ -923,25 +1086,25 @@
UNIMPLEMENTED(FATAL) << "Unexpected event: " << event;
}
- uint32_t method_value = EncodeTraceMethodAndAction(method, action);
+ // Ensure we always use the non-obsolete version of the method so that entry/exit events have the
+ // same pointer value.
+ method = method->GetNonObsoleteMethod();
- // Write data into the tracing buffer (if not streaming) or into a
- // small buffer on the stack (if streaming) which we'll put into the
- // tracing buffer below.
- //
- // These writes to the tracing buffer are synchronised with the
- // future reads that (only) occur under FinishTracing(). The callers
- // of FinishTracing() acquire locks and (implicitly) synchronise
- // the buffer memory.
- uint8_t* ptr;
- static constexpr size_t kPacketSize = 14U; // The maximum size of data in a packet.
- uint8_t stack_buf[kPacketSize]; // Space to store a packet when in streaming mode.
if (trace_output_mode_ == TraceOutputMode::kStreaming) {
- ptr = stack_buf;
+ RecordStreamingMethodEvent(thread, method, action, thread_clock_diff, wall_clock_diff);
} else {
- ptr = buf_.get() + old_offset;
+ RecordMethodEvent(thread, method, action, thread_clock_diff, wall_clock_diff);
}
+}
+void Trace::EncodeEventEntry(uint8_t* ptr,
+ Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
+ static constexpr size_t kPacketSize = 14U; // The maximum size of data in a packet.
+ uint32_t method_value = EncodeTraceMethodAndAction(method, action);
Append2LE(ptr, thread->GetTid());
Append4LE(ptr + 2, method_value);
ptr += 6;
@@ -954,33 +1117,20 @@
Append4LE(ptr, wall_clock_diff);
}
static_assert(kPacketSize == 2 + 4 + 4 + 4, "Packet size incorrect.");
+}
- if (trace_output_mode_ == TraceOutputMode::kStreaming) {
- MutexLock mu(Thread::Current(), *streaming_lock_); // To serialize writing.
- if (RegisterMethod(method)) {
- // Write a special block with the name.
- std::string method_line(GetMethodLine(method));
- uint8_t buf2[5];
- Append2LE(buf2, 0);
- buf2[2] = kOpNewMethod;
- Append2LE(buf2 + 3, static_cast<uint16_t>(method_line.length()));
- WriteToBuf(buf2, sizeof(buf2));
- WriteToBuf(reinterpret_cast<const uint8_t*>(method_line.c_str()), method_line.length());
- }
- if (RegisterThread(thread)) {
- // It might be better to postpone this. Threads might not have received names...
- std::string thread_name;
- thread->GetThreadName(thread_name);
- uint8_t buf2[7];
- Append2LE(buf2, 0);
- buf2[2] = kOpNewThread;
- Append2LE(buf2 + 3, static_cast<uint16_t>(thread->GetTid()));
- Append2LE(buf2 + 5, static_cast<uint16_t>(thread_name.length()));
- WriteToBuf(buf2, sizeof(buf2));
- WriteToBuf(reinterpret_cast<const uint8_t*>(thread_name.c_str()), thread_name.length());
- }
- WriteToBuf(stack_buf, sizeof(stack_buf));
+void Trace::EnsureSpace(uint8_t* buffer,
+ size_t* current_index,
+ size_t buffer_size,
+ size_t required_size) {
+ if (*current_index + required_size < buffer_size) {
+ return;
}
+
+ if (!trace_file_->WriteFully(buffer, *current_index)) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
+ *current_index = 0;
}
void Trace::GetVisitedMethods(size_t buf_size,
@@ -1042,9 +1192,21 @@
return the_trace_->trace_mode_;
}
+int Trace::GetFlags() {
+ MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+ CHECK(the_trace_ != nullptr) << "Trace flags requested, but no trace currently running";
+ return the_trace_->flags_;
+}
+
+int Trace::GetIntervalInMillis() {
+ MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+ CHECK(the_trace_ != nullptr) << "Trace interval requested, but no trace currently running";
+ return the_trace_->interval_us_;
+}
+
size_t Trace::GetBufferSize() {
MutexLock mu(Thread::Current(), *Locks::trace_lock_);
- CHECK(the_trace_ != nullptr) << "Trace mode requested, but no trace currently running";
+ CHECK(the_trace_ != nullptr) << "Trace buffer size requested, but no trace currently running";
return the_trace_->buffer_size_;
}
diff --git a/runtime/trace.h b/runtime/trace.h
index c6f36e4..344c783 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -49,9 +49,6 @@
using DexIndexBitSet = std::bitset<65536>;
-constexpr size_t kMaxThreadIdNumber = kIsTargetBuild ? 65536U : 1048576U;
-using ThreadIDBitSet = std::bitset<kMaxThreadIdNumber>;
-
enum TracingMode {
kTracingInactive,
kMethodTracingActive, // Trace activity synchronous with method progress.
@@ -107,7 +104,9 @@
class Trace final : public instrumentation::InstrumentationListener {
public:
enum TraceFlag {
- kTraceCountAllocs = 1,
+ kTraceCountAllocs = 0x001,
+ kTraceClockSourceWallClock = 0x010,
+ kTraceClockSourceThreadCpu = 0x100,
};
enum class TraceOutputMode {
@@ -166,6 +165,10 @@
REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::trace_lock_);
static TracingMode GetMethodTracingMode() REQUIRES(!Locks::trace_lock_);
+ // Flush the per-thread buffer. This is called when the thread is about to detach.
+ static void FlushThreadBuffer(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::trace_lock_) NO_THREAD_SAFETY_ANALYSIS;
+
bool UseWallClock();
bool UseThreadCpuClock();
void MeasureClockOverhead();
@@ -184,7 +187,6 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
override;
void MethodUnwind(Thread* thread,
- Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
@@ -230,6 +232,8 @@
static TraceOutputMode GetOutputMode() REQUIRES(!Locks::trace_lock_);
static TraceMode GetMode() REQUIRES(!Locks::trace_lock_);
static size_t GetBufferSize() REQUIRES(!Locks::trace_lock_);
+ static int GetFlags() REQUIRES(!Locks::trace_lock_);
+ static int GetIntervalInMillis() REQUIRES(!Locks::trace_lock_);
// Used by class linker to prevent class unloading.
static bool IsTracingEnabled() REQUIRES(!Locks::trace_lock_);
@@ -275,13 +279,55 @@
bool RegisterThread(Thread* thread)
REQUIRES(streaming_lock_);
- // Copy a temporary buffer to the main buffer. Used for streaming. Exposed here for lock
- // annotation.
- void WriteToBuf(const uint8_t* src, size_t src_size)
- REQUIRES(streaming_lock_);
- // Flush the main buffer to file. Used for streaming. Exposed here for lock annotation.
- void FlushBuf()
- REQUIRES(streaming_lock_);
+ void RecordMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) REQUIRES(!unique_methods_lock_);
+
+ // Encodes event in non-streaming mode. This assumes that there is enough space reserved to
+ // encode the entry.
+ void EncodeEventEntry(uint8_t* ptr,
+ Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) REQUIRES(!unique_methods_lock_);
+
+ // These methods are used to encode events in streaming mode.
+
+ // This records the method event in the per-thread buffer if there is sufficient space for the
+ // entire record. If the buffer is full then it just flushes the buffer and then records the
+ // entry.
+ void RecordStreamingMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!unique_methods_lock_) REQUIRES(!streaming_lock_);
+ // This encodes all the events in the per-thread trace buffer and writes it to the trace file.
+ // This acquires streaming lock to prevent any other threads writing concurrently. It is required
+ // to serialize these since each method is encoded with a unique id which is assigned when the
+ // method is seen for the first time in the recoreded events. So we need to serialize these
+ // flushes across threads.
+ void FlushStreamingBuffer(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!unique_methods_lock_) REQUIRES(!streaming_lock_);
+ // Ensures there is sufficient space in the buffer to record the requested_size. If there is not
+ // enough sufficient space the current contents of the buffer are written to the file and
+ // current_index is reset to 0. This doesn't check if buffer_size is big enough to hold the
+ // requested size.
+ void EnsureSpace(uint8_t* buffer,
+ size_t* current_index,
+ size_t buffer_size,
+ size_t required_size);
+ // Writes header followed by data to the buffer at the current_index. This also updates the
+ // current_index to point to the next entry.
+ void WriteToBuf(uint8_t* header,
+ size_t header_size,
+ const std::string& data,
+ size_t* current_index,
+ uint8_t* buffer,
+ size_t buffer_size);
uint32_t EncodeTraceMethod(ArtMethod* method) REQUIRES(!unique_methods_lock_);
uint32_t EncodeTraceMethodAndAction(ArtMethod* method, TraceAction action)
@@ -369,7 +415,6 @@
// Streaming mode data.
Mutex* streaming_lock_;
std::map<const DexFile*, DexIndexBitSet*> seen_methods_ GUARDED_BY(streaming_lock_);
- std::unique_ptr<ThreadIDBitSet> seen_threads_ GUARDED_BY(streaming_lock_);
// Bijective map from ArtMethod* to index.
// Map from ArtMethod* to index in unique_methods_;
diff --git a/runtime/transaction.h b/runtime/transaction.h
index d147038..6fa8e58 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -245,15 +245,17 @@
REQUIRES(Locks::intern_table_lock_);
void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
- InternStringLog() = default;
+ // Only the move constructor is supported.
+ InternStringLog() = delete;
+ InternStringLog(const InternStringLog& log) = delete;
+ InternStringLog& operator=(const InternStringLog& log) = delete;
InternStringLog(InternStringLog&& log) = default;
+ InternStringLog& operator=(InternStringLog&& log) = delete;
private:
mutable GcRoot<mirror::String> str_;
const StringKind string_kind_;
const StringOp string_op_;
-
- DISALLOW_COPY_AND_ASSIGN(InternStringLog);
};
class ResolveStringLog : public ValueObject {
diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc
index 73b4dda..0c7e3bd 100644
--- a/runtime/var_handles.cc
+++ b/runtime/var_handles.cc
@@ -41,7 +41,7 @@
const size_t num_vregs = accessor_type->NumberOfVRegs();
const int num_params = accessor_type->GetPTypes()->GetLength();
ShadowFrameAllocaUniquePtr accessor_frame =
- CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
+ CREATE_SHADOW_FRAME(num_vregs, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
ShadowFrameGetter getter(shadow_frame, operands);
static const uint32_t kFirstDestinationReg = 0;
ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index fb3f809..f07233c 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -49,10 +49,6 @@
using android::base::StringPrintf;
-constexpr uint8_t VdexFile::VdexFileHeader::kVdexInvalidMagic[4];
-constexpr uint8_t VdexFile::VdexFileHeader::kVdexMagic[4];
-constexpr uint8_t VdexFile::VdexFileHeader::kVdexVersion[4];
-
bool VdexFile::VdexFileHeader::IsMagicValid() const {
return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0);
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 3ccbfa5..a35d720 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -246,8 +246,8 @@
const uint8_t* Begin() const { return mmap_.Begin(); }
const uint8_t* End() const { return mmap_.End(); }
size_t Size() const { return mmap_.Size(); }
- bool Contains(const uint8_t* pointer) const {
- return pointer >= Begin() && pointer < End();
+ bool Contains(const uint8_t* pointer, size_t size) const {
+ return Begin() <= pointer && size <= Size() && pointer <= End() - size;
}
const VdexFileHeader& GetVdexFileHeader() const {
diff --git a/runtime/vdex_file_test.cc b/runtime/vdex_file_test.cc
index 565487e..1ac10eb 100644
--- a/runtime/vdex_file_test.cc
+++ b/runtime/vdex_file_test.cc
@@ -20,12 +20,11 @@
#include <gtest/gtest.h>
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
namespace art {
-class VdexFileTest : public CommonRuntimeTest {
-};
+class VdexFileTest : public CommonArtTest {};
TEST_F(VdexFileTest, OpenEmptyVdex) {
// Verify we fail to open an empty vdex file.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ec0ac73..1d1d3ee 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1054,6 +1054,10 @@
// We can't assume the instruction is well formed, handle the case where calculating the size
// goes past the end of the code item.
SafeDexInstructionIterator it(code_item_accessor_.begin(), code_item_accessor_.end());
+ if (it == code_item_accessor_.end()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code item has no opcode";
+ return false;
+ }
for ( ; !it.IsErrorState() && it < code_item_accessor_.end(); ++it) {
// In case the instruction goes past the end of the code item, make sure to not process it.
SafeDexInstructionIterator next = it;
@@ -4899,7 +4903,7 @@
template <bool kVerifierDebug>
bool MethodVerifier<kVerifierDebug>::PotentiallyMarkRuntimeThrow() {
- if (IsAotMode() || IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kT)) {
+ if (IsAotMode() || IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kS_V2)) {
return false;
}
// Compatibility mode: we treat the following code unreachable and the verifier
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index ec07047..0f0fd17 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -37,6 +37,10 @@
class MethodVerifierTest : public CommonRuntimeTest {
protected:
+ MethodVerifierTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void VerifyClass(const std::string& descriptor)
REQUIRES_SHARED(Locks::mutator_lock_) {
ASSERT_FALSE(descriptor.empty());
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index a157464..a36cf71 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -32,7 +32,7 @@
namespace verifier {
class RegTypeTest : public CommonRuntimeTest {
- public:
+ protected:
RegTypeTest() {
use_boot_image_ = true; // Make the Runtime creation cheaper.
}
@@ -360,7 +360,7 @@
EXPECT_TRUE(double_reg_type.HasClass());
}
-class RegTypeReferenceTest : public CommonRuntimeTest {};
+class RegTypeReferenceTest : public RegTypeTest {};
TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) {
// Tests matching precisions. A reference type that was created precise doesn't
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index db5fa0f..781cf7b 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -760,6 +760,18 @@
return true;
}
+void VerifierDeps::ClearData(const std::vector<const DexFile*>& dex_files) {
+ for (const DexFile* dex_file : dex_files) {
+ auto it = dex_deps_.find(dex_file);
+ if (it == dex_deps_.end()) {
+ continue;
+ }
+ std::unique_ptr<DexFileDeps> deps(new DexFileDeps(dex_file->NumClassDefs()));
+ it->second.swap(deps);
+ }
+}
+
+
bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFileDeps& deps,
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 8e3b8a7..46b3554 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -137,6 +137,9 @@
return GetDexFileDeps(dex_file) != nullptr;
}
+ // Resets the data related to the given dex files.
+ void ClearData(const std::vector<const DexFile*>& dex_files);
+
// Parses raw VerifierDeps data to extract bitvectors of which class def indices
// were verified or not. The given `dex_files` must match the order and count of
// dex files used to create the VerifierDeps.
diff --git a/runtime/well_known_classes-inl.h b/runtime/well_known_classes-inl.h
new file mode 100644
index 0000000..23fe145
--- /dev/null
+++ b/runtime/well_known_classes-inl.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_WELL_KNOWN_CLASSES_INL_H_
+#define ART_RUNTIME_WELL_KNOWN_CLASSES_INL_H_
+
+#include "well_known_classes.h"
+
+#include "art_field-inl.h"
+#include "art_method-inl.h"
+
+namespace art {
+namespace detail {
+
+template <typename MemberType, MemberType** kMember>
+template <ReadBarrierOption kReadBarrierOption>
+ObjPtr<mirror::Class> ClassFromMember<MemberType, kMember>::Get() {
+ return (*kMember)->template GetDeclaringClass<kReadBarrierOption>();
+}
+
+template <typename MemberType, MemberType** kMember>
+mirror::Class* ClassFromMember<MemberType, kMember>::operator->() const {
+ return Get().Ptr();
+}
+
+template <typename MemberType, MemberType** kMember>
+inline bool operator==(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs) {
+ return lhs.Get() == rhs;
+}
+
+template <typename MemberType, MemberType** kMember>
+inline bool operator==(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs) {
+ return rhs == lhs;
+}
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs) {
+ return !(rhs == lhs);
+}
+
+} // namespace detail
+} // namespace art
+
+#endif // ART_RUNTIME_WELL_KNOWN_CLASSES_INL_H_
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index cf9e321..6a4a87f 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -23,11 +23,15 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include "art_method-inl.h"
+#include "base/casts.h"
#include "base/enums.h"
#include "class_linker.h"
+#include "class_root-inl.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "entrypoints/runtime_asm_entrypoints.h"
+#include "handle_scope-inl.h"
#include "hidden_api.h"
+#include "jni/java_vm_ext.h"
#include "jni/jni_internal.h"
#include "jni_id_type.h"
#include "mirror/class.h"
@@ -44,139 +48,126 @@
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
jclass WellKnownClasses::dalvik_annotation_optimization_NeverCompile;
-jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
-jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
-jclass WellKnownClasses::dalvik_system_DexClassLoader;
-jclass WellKnownClasses::dalvik_system_DexFile;
-jclass WellKnownClasses::dalvik_system_DexPathList;
-jclass WellKnownClasses::dalvik_system_DexPathList__Element;
-jclass WellKnownClasses::dalvik_system_EmulatedStackFrame;
-jclass WellKnownClasses::dalvik_system_InMemoryDexClassLoader;
-jclass WellKnownClasses::dalvik_system_PathClassLoader;
-jclass WellKnownClasses::dalvik_system_VMRuntime;
+jclass WellKnownClasses::dalvik_annotation_optimization_NeverInline;
jclass WellKnownClasses::java_lang_annotation_Annotation__array;
-jclass WellKnownClasses::java_lang_BootClassLoader;
-jclass WellKnownClasses::java_lang_ClassLoader;
-jclass WellKnownClasses::java_lang_ClassNotFoundException;
-jclass WellKnownClasses::java_lang_Daemons;
-jclass WellKnownClasses::java_lang_Error;
-jclass WellKnownClasses::java_lang_IllegalAccessError;
-jclass WellKnownClasses::java_lang_NoClassDefFoundError;
-jclass WellKnownClasses::java_lang_Object;
-jclass WellKnownClasses::java_lang_OutOfMemoryError;
-jclass WellKnownClasses::java_lang_reflect_InvocationTargetException;
-jclass WellKnownClasses::java_lang_reflect_Parameter;
+jclass WellKnownClasses::java_lang_ClassValue;
jclass WellKnownClasses::java_lang_reflect_Parameter__array;
-jclass WellKnownClasses::java_lang_reflect_Proxy;
-jclass WellKnownClasses::java_lang_RuntimeException;
-jclass WellKnownClasses::java_lang_StackOverflowError;
-jclass WellKnownClasses::java_lang_String;
jclass WellKnownClasses::java_lang_StringFactory;
jclass WellKnownClasses::java_lang_System;
-jclass WellKnownClasses::java_lang_Thread;
-jclass WellKnownClasses::java_lang_ThreadGroup;
-jclass WellKnownClasses::java_lang_Throwable;
jclass WellKnownClasses::java_lang_Void;
-jclass WellKnownClasses::java_nio_Buffer;
-jclass WellKnownClasses::java_nio_ByteBuffer;
-jclass WellKnownClasses::java_nio_DirectByteBuffer;
-jclass WellKnownClasses::java_util_Collections;
-jclass WellKnownClasses::java_util_function_Consumer;
-jclass WellKnownClasses::libcore_reflect_AnnotationFactory;
-jclass WellKnownClasses::libcore_reflect_AnnotationMember;
-jclass WellKnownClasses::libcore_util_EmptyArray;
-jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
-jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer;
+jclass WellKnownClasses::libcore_reflect_AnnotationMember__array;
-jmethodID WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath;
-jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization;
-jmethodID WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed;
-jmethodID WellKnownClasses::java_lang_Boolean_valueOf;
-jmethodID WellKnownClasses::java_lang_Byte_valueOf;
-jmethodID WellKnownClasses::java_lang_Character_valueOf;
-jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass;
-jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init;
-jmethodID WellKnownClasses::java_lang_Daemons_start;
-jmethodID WellKnownClasses::java_lang_Daemons_stop;
-jmethodID WellKnownClasses::java_lang_Daemons_waitForDaemonStart;
-jmethodID WellKnownClasses::java_lang_Double_doubleToRawLongBits;
-jmethodID WellKnownClasses::java_lang_Double_valueOf;
-jmethodID WellKnownClasses::java_lang_Float_floatToRawIntBits;
-jmethodID WellKnownClasses::java_lang_Float_valueOf;
-jmethodID WellKnownClasses::java_lang_Integer_valueOf;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_asType;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
-jmethodID WellKnownClasses::java_lang_Long_valueOf;
-jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
-jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
-jmethodID WellKnownClasses::java_lang_reflect_InvocationTargetException_init;
-jmethodID WellKnownClasses::java_lang_reflect_Parameter_init;
-jmethodID WellKnownClasses::java_lang_reflect_Proxy_init;
-jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
-jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
-jmethodID WellKnownClasses::java_lang_Short_valueOf;
-jmethodID WellKnownClasses::java_lang_String_charAt;
-jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
-jmethodID WellKnownClasses::java_lang_Thread_init;
-jmethodID WellKnownClasses::java_lang_Thread_run;
-jmethodID WellKnownClasses::java_lang_ThreadGroup_add;
-jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
-jmethodID WellKnownClasses::java_nio_Buffer_isDirect;
-jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
-jmethodID WellKnownClasses::java_util_function_Consumer_accept;
-jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
-jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init;
-jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
-jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ArtMethod* WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath;
+ArtMethod* WellKnownClasses::dalvik_system_DelegateLastClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_DexClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_InMemoryDexClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_PathClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed;
+ArtMethod* WellKnownClasses::java_lang_Boolean_valueOf;
+ArtMethod* WellKnownClasses::java_lang_BootClassLoader_init;
+ArtMethod* WellKnownClasses::java_lang_Byte_valueOf;
+ArtMethod* WellKnownClasses::java_lang_Character_valueOf;
+ArtMethod* WellKnownClasses::java_lang_ClassLoader_loadClass;
+ArtMethod* WellKnownClasses::java_lang_ClassNotFoundException_init;
+ArtMethod* WellKnownClasses::java_lang_Daemons_start;
+ArtMethod* WellKnownClasses::java_lang_Daemons_stop;
+ArtMethod* WellKnownClasses::java_lang_Daemons_waitForDaemonStart;
+ArtMethod* WellKnownClasses::java_lang_Double_doubleToRawLongBits;
+ArtMethod* WellKnownClasses::java_lang_Double_valueOf;
+ArtMethod* WellKnownClasses::java_lang_Error_init;
+ArtMethod* WellKnownClasses::java_lang_Float_floatToRawIntBits;
+ArtMethod* WellKnownClasses::java_lang_Float_valueOf;
+ArtMethod* WellKnownClasses::java_lang_IllegalAccessError_init;
+ArtMethod* WellKnownClasses::java_lang_Integer_valueOf;
+ArtMethod* WellKnownClasses::java_lang_Long_valueOf;
+ArtMethod* WellKnownClasses::java_lang_NoClassDefFoundError_init;
+ArtMethod* WellKnownClasses::java_lang_OutOfMemoryError_init;
+ArtMethod* WellKnownClasses::java_lang_Runtime_nativeLoad;
+ArtMethod* WellKnownClasses::java_lang_RuntimeException_init;
+ArtMethod* WellKnownClasses::java_lang_Short_valueOf;
+ArtMethod* WellKnownClasses::java_lang_StackOverflowError_init;
+ArtMethod* WellKnownClasses::java_lang_String_charAt;
+ArtMethod* WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
+ArtMethod* WellKnownClasses::java_lang_Thread_init;
+ArtMethod* WellKnownClasses::java_lang_Thread_run;
+ArtMethod* WellKnownClasses::java_lang_ThreadGroup_add;
+ArtMethod* WellKnownClasses::java_lang_ThreadGroup_threadTerminated;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_asType;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
+ArtMethod* WellKnownClasses::java_lang_ref_FinalizerReference_add;
+ArtMethod* WellKnownClasses::java_lang_ref_ReferenceQueue_add;
+ArtMethod* WellKnownClasses::java_lang_reflect_InvocationTargetException_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Parameter_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_invoke;
+ArtMethod* WellKnownClasses::java_nio_Buffer_isDirect;
+ArtMethod* WellKnownClasses::java_nio_DirectByteBuffer_init;
+ArtMethod* WellKnownClasses::java_util_function_Consumer_accept;
+ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D;
+ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F;
+ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars;
+ArtMethod* WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
+ArtMethod* WellKnownClasses::libcore_reflect_AnnotationMember_init;
+ArtMethod* WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
+ArtMethod* WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
-jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
-jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
-jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
-jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
-jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
-jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
-jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
-jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
-jfieldID WellKnownClasses::java_io_FileDescriptor_descriptor;
-jfieldID WellKnownClasses::java_lang_ClassLoader_parent;
-jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
-jfieldID WellKnownClasses::java_lang_Thread_daemon;
-jfieldID WellKnownClasses::java_lang_Thread_group;
-jfieldID WellKnownClasses::java_lang_Thread_lock;
-jfieldID WellKnownClasses::java_lang_Thread_name;
-jfieldID WellKnownClasses::java_lang_Thread_priority;
-jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
-jfieldID WellKnownClasses::java_lang_Thread_systemDaemon;
-jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_name;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_parent;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
-jfieldID WellKnownClasses::java_lang_Throwable_cause;
-jfieldID WellKnownClasses::java_lang_Throwable_detailMessage;
-jfieldID WellKnownClasses::java_lang_Throwable_stackTrace;
-jfieldID WellKnownClasses::java_lang_Throwable_stackState;
-jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions;
-jfieldID WellKnownClasses::java_nio_Buffer_address;
-jfieldID WellKnownClasses::java_nio_Buffer_capacity;
-jfieldID WellKnownClasses::java_nio_Buffer_elementSizeShift;
-jfieldID WellKnownClasses::java_nio_Buffer_limit;
-jfieldID WellKnownClasses::java_nio_Buffer_position;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_address;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_hb;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_limit;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_offset;
-jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST;
-jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type;
+ArtField* WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
+ArtField* WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
+ArtField* WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
+ArtField* WellKnownClasses::dalvik_system_DexFile_cookie;
+ArtField* WellKnownClasses::dalvik_system_DexFile_fileName;
+ArtField* WellKnownClasses::dalvik_system_DexPathList_dexElements;
+ArtField* WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
+ArtField* WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ArtField* WellKnownClasses::java_io_FileDescriptor_descriptor;
+ArtField* WellKnownClasses::java_lang_ClassLoader_parent;
+ArtField* WellKnownClasses::java_lang_Thread_parkBlocker;
+ArtField* WellKnownClasses::java_lang_Thread_daemon;
+ArtField* WellKnownClasses::java_lang_Thread_group;
+ArtField* WellKnownClasses::java_lang_Thread_lock;
+ArtField* WellKnownClasses::java_lang_Thread_name;
+ArtField* WellKnownClasses::java_lang_Thread_priority;
+ArtField* WellKnownClasses::java_lang_Thread_nativePeer;
+ArtField* WellKnownClasses::java_lang_Thread_systemDaemon;
+ArtField* WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_groups;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_ngroups;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_name;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_parent;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ArtField* WellKnownClasses::java_lang_Throwable_cause;
+ArtField* WellKnownClasses::java_lang_Throwable_detailMessage;
+ArtField* WellKnownClasses::java_lang_Throwable_stackTrace;
+ArtField* WellKnownClasses::java_lang_Throwable_stackState;
+ArtField* WellKnownClasses::java_lang_Throwable_suppressedExceptions;
+ArtField* WellKnownClasses::java_nio_Buffer_address;
+ArtField* WellKnownClasses::java_nio_Buffer_capacity;
+ArtField* WellKnownClasses::java_nio_Buffer_elementSizeShift;
+ArtField* WellKnownClasses::java_nio_Buffer_limit;
+ArtField* WellKnownClasses::java_nio_Buffer_position;
+ArtField* WellKnownClasses::java_nio_ByteBuffer_hb;
+ArtField* WellKnownClasses::java_nio_ByteBuffer_isReadOnly;
+ArtField* WellKnownClasses::java_nio_ByteBuffer_offset;
+ArtField* WellKnownClasses::java_util_Collections_EMPTY_LIST;
+ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
+ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
+ArtField* WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type;
+
+static ObjPtr<mirror::Class> FindSystemClass(ClassLinker* class_linker,
+ Thread* self,
+ const char* descriptor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> klass = class_linker->FindSystemClass(self, descriptor);
+ CHECK(klass != nullptr) << "Couldn't find system class: " << descriptor;
+ return klass;
+}
static jclass CacheClass(JNIEnv* env, const char* jni_class_name) {
ScopedLocalRef<jclass> c(env, env->FindClass(jni_class_name));
@@ -186,73 +177,57 @@
return reinterpret_cast<jclass>(env->NewGlobalRef(c.get()));
}
-static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static,
- const char* name, const char* signature) {
- jfieldID fid;
- {
- ScopedObjectAccess soa(env);
- if (Runtime::Current()->GetJniIdType() != JniIdType::kSwapablePointer) {
- fid = jni::EncodeArtField</*kEnableIndexIds*/ true>(
- FindFieldJNI(soa, c, name, signature, is_static));
- } else {
- fid = jni::EncodeArtField</*kEnableIndexIds*/ false>(
- FindFieldJNI(soa, c, name, signature, is_static));
- }
- }
- if (fid == nullptr) {
- ScopedObjectAccess soa(env);
- if (soa.Self()->IsExceptionPending()) {
- LOG(FATAL_WITHOUT_ABORT) << soa.Self()->GetException()->Dump();
- }
+static ArtField* CacheField(ObjPtr<mirror::Class> klass,
+ bool is_static,
+ const char* name,
+ const char* signature) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* field = is_static
+ ? klass->FindDeclaredStaticField(name, signature)
+ : klass->FindDeclaredInstanceField(name, signature);
+ if (UNLIKELY(field == nullptr)) {
std::ostringstream os;
- WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
- LOG(FATAL) << "Couldn't find field \"" << name << "\" with signature \"" << signature << "\": "
- << os.str();
+ klass->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+ LOG(FATAL) << "Couldn't find " << (is_static ? "static" : "instance") << " field \""
+ << name << "\" with signature \"" << signature << "\": " << os.str();
+ UNREACHABLE();
}
- return fid;
+ return field;
}
-static jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
- const char* name, const char* signature) {
- jmethodID mid;
- {
- ScopedObjectAccess soa(env);
- if (Runtime::Current()->GetJniIdType() != JniIdType::kSwapablePointer) {
- mid = jni::EncodeArtMethod</*kEnableIndexIds*/ true>(
- FindMethodJNI(soa, c, name, signature, is_static));
- } else {
- mid = jni::EncodeArtMethod</*kEnableIndexIds*/ false>(
- FindMethodJNI(soa, c, name, signature, is_static));
- }
- }
- if (mid == nullptr) {
- ScopedObjectAccess soa(env);
- if (soa.Self()->IsExceptionPending()) {
- LOG(FATAL_WITHOUT_ABORT) << soa.Self()->GetException()->Dump();
- }
+static ArtMethod* CacheMethod(ObjPtr<mirror::Class> klass,
+ bool is_static,
+ const char* name,
+ const char* signature,
+ PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* method = klass->IsInterface()
+ ? klass->FindInterfaceMethod(name, signature, pointer_size)
+ : klass->FindClassMethod(name, signature, pointer_size);
+ if (UNLIKELY(method == nullptr) || UNLIKELY(is_static != method->IsStatic())) {
std::ostringstream os;
- WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
- LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\": "
- << os.str();
+ klass->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+ LOG(FATAL) << "Couldn't find " << (is_static ? "static" : "instance") << " method \""
+ << name << "\" with signature \"" << signature << "\": " << os.str();
+ UNREACHABLE();
}
- return mid;
+ DCHECK(method->GetDeclaringClass() == klass);
+ return method;
}
-static jmethodID CacheMethod(JNIEnv* env, const char* klass, bool is_static,
- const char* name, const char* signature) {
- ScopedLocalRef<jclass> java_class(env, env->FindClass(klass));
- return CacheMethod(env, java_class.get(), is_static, name, signature);
-}
-
-static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const char* boxed_name) {
- ScopedLocalRef<jclass> boxed_class(env, env->FindClass(boxed_name));
- return CacheMethod(env, boxed_class.get(), true, "valueOf",
- android::base::StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
+static ArtMethod* CachePrimitiveBoxingMethod(ClassLinker* class_linker,
+ Thread* self,
+ char prim_name,
+ const char* boxed_name)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> boxed_class = FindSystemClass(class_linker, self, boxed_name);
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
+ std::string signature = android::base::StringPrintf("(%c)%s", prim_name, boxed_name);
+ return CacheMethod(boxed_class, /*is_static=*/ true, "valueOf", signature.c_str(), pointer_size);
}
#define STRING_INIT_LIST(V) \
V(java_lang_String_init, "()V", newEmptyString, "newEmptyString", "()Ljava/lang/String;", NewEmptyString) \
V(java_lang_String_init_B, "([B)V", newStringFromBytes_B, "newStringFromBytes", "([B)Ljava/lang/String;", NewStringFromBytes_B) \
+ V(java_lang_String_init_BB, "([BB)V", newStringFromBytes_BB, "newStringFromBytes", "([BB)Ljava/lang/String;", NewStringFromBytes_BB) \
V(java_lang_String_init_BI, "([BI)V", newStringFromBytes_BI, "newStringFromBytes", "([BI)Ljava/lang/String;", NewStringFromBytes_BI) \
V(java_lang_String_init_BII, "([BII)V", newStringFromBytes_BII, "newStringFromBytes", "([BII)Ljava/lang/String;", NewStringFromBytes_BII) \
V(java_lang_String_init_BIII, "([BIII)V", newStringFromBytes_BIII, "newStringFromBytes", "([BIII)Ljava/lang/String;", NewStringFromBytes_BIII) \
@@ -345,50 +320,16 @@
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
dalvik_annotation_optimization_NeverCompile =
CacheClass(env, "dalvik/annotation/optimization/NeverCompile");
- dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
- dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
- dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
- dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
- dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
- dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
- dalvik_system_EmulatedStackFrame = CacheClass(env, "dalvik/system/EmulatedStackFrame");
- dalvik_system_InMemoryDexClassLoader = CacheClass(env, "dalvik/system/InMemoryDexClassLoader");
- dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
- dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime");
+ dalvik_annotation_optimization_NeverInline =
+ CacheClass(env, "dalvik/annotation/optimization/NeverInline");
java_lang_annotation_Annotation__array = CacheClass(env, "[Ljava/lang/annotation/Annotation;");
- java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader");
- java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
- java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException");
- java_lang_Daemons = CacheClass(env, "java/lang/Daemons");
- java_lang_Object = CacheClass(env, "java/lang/Object");
- java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError");
- java_lang_Error = CacheClass(env, "java/lang/Error");
- java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
- java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
- java_lang_reflect_InvocationTargetException = CacheClass(env, "java/lang/reflect/InvocationTargetException");
- java_lang_reflect_Parameter = CacheClass(env, "java/lang/reflect/Parameter");
+ java_lang_ClassValue = CacheClass(env, "java/lang/ClassValue");
java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;");
- java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy");
- java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
- java_lang_StackOverflowError = CacheClass(env, "java/lang/StackOverflowError");
- java_lang_String = CacheClass(env, "java/lang/String");
java_lang_StringFactory = CacheClass(env, "java/lang/StringFactory");
java_lang_System = CacheClass(env, "java/lang/System");
- java_lang_Thread = CacheClass(env, "java/lang/Thread");
- java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
- java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
java_lang_Void = CacheClass(env, "java/lang/Void");
- java_nio_Buffer = CacheClass(env, "java/nio/Buffer");
- java_nio_ByteBuffer = CacheClass(env, "java/nio/ByteBuffer");
- java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
- java_util_Collections = CacheClass(env, "java/util/Collections");
- java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer");
- libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory");
- libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember");
- libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray");
- org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
- org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
+ libcore_reflect_AnnotationMember__array = CacheClass(env, "[Llibcore/reflect/AnnotationMember;");
InitFieldsAndMethodsOnly(env);
}
@@ -397,149 +338,453 @@
hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
hiddenapi::EnforcementPolicy::kDisabled);
- dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(env, dalvik_system_BaseDexClassLoader, false, "getLdLibraryPath", "()Ljava/lang/String;");
- dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V");
- dalvik_system_VMRuntime_hiddenApiUsed = CacheMethod(env, dalvik_system_VMRuntime, true, "hiddenApiUsed", "(ILjava/lang/String;Ljava/lang/String;IZ)V");
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
- java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+ java_lang_Boolean_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'Z', "Ljava/lang/Boolean;");
+ java_lang_Byte_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'B', "Ljava/lang/Byte;");
+ java_lang_Character_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'C', "Ljava/lang/Character;");
+ java_lang_Double_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'D', "Ljava/lang/Double;");
+ java_lang_Float_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'F', "Ljava/lang/Float;");
+ java_lang_Integer_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'I', "Ljava/lang/Integer;");
+ java_lang_Long_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'J', "Ljava/lang/Long;");
+ java_lang_Short_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'S', "Ljava/lang/Short;");
- java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
- java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V");
- java_lang_Daemons_waitForDaemonStart = CacheMethod(env, java_lang_Daemons, true, "waitForDaemonStart", "()V");
- java_lang_invoke_MethodHandle_asType = CacheMethod(env, "java/lang/invoke/MethodHandle", false, "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
- java_lang_invoke_MethodHandle_invokeExact = CacheMethod(env, "java/lang/invoke/MethodHandle", false, "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;");
- java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
- java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
+ StackHandleScope<42u> hs(self);
+ Handle<mirror::Class> d_s_bdcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/BaseDexClassLoader;"));
+ Handle<mirror::Class> d_s_dlcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DelegateLastClassLoader;"));
+ Handle<mirror::Class> d_s_dcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexClassLoader;"));
+ Handle<mirror::Class> d_s_df =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexFile;"));
+ Handle<mirror::Class> d_s_dpl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexPathList;"));
+ Handle<mirror::Class> d_s_dpl_e =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexPathList$Element;"));
+ Handle<mirror::Class> d_s_imdcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/InMemoryDexClassLoader;"));
+ Handle<mirror::Class> d_s_pcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/PathClassLoader;"));
+ Handle<mirror::Class> d_s_vmr =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/VMRuntime;"));
+ Handle<mirror::Class> j_i_fd =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/io/FileDescriptor;"));
+ Handle<mirror::Class> j_l_bcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/BootClassLoader;"));
+ Handle<mirror::Class> j_l_cl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ClassLoader;"));
+ Handle<mirror::Class> j_l_cnfe =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ClassNotFoundException;"));
+ Handle<mirror::Class> j_l_Daemons =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Daemons;"));
+ Handle<mirror::Class> j_l_Error =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Error;"));
+ Handle<mirror::Class> j_l_IllegalAccessError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/IllegalAccessError;"));
+ Handle<mirror::Class> j_l_NoClassDefFoundError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/NoClassDefFoundError;"));
+ Handle<mirror::Class> j_l_OutOfMemoryError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/OutOfMemoryError;"));
+ Handle<mirror::Class> j_l_RuntimeException =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/RuntimeException;"));
+ Handle<mirror::Class> j_l_StackOverflowError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/StackOverflowError;"));
+ Handle<mirror::Class> j_l_Thread =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Thread;"));
+ Handle<mirror::Class> j_l_tg =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ThreadGroup;"));
+ Handle<mirror::Class> j_l_i_MethodHandle =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/invoke/MethodHandle;"));
+ Handle<mirror::Class> j_l_i_MethodHandles =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/invoke/MethodHandles;"));
+ Handle<mirror::Class> j_l_i_MethodHandles_Lookup =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/invoke/MethodHandles$Lookup;"));
+ Handle<mirror::Class> j_l_r_fr =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ref/FinalizerReference;"));
+ Handle<mirror::Class> j_l_r_rq =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ref/ReferenceQueue;"));
+ Handle<mirror::Class> j_l_rl_ite = hs.NewHandle(
+ FindSystemClass(class_linker, self, "Ljava/lang/reflect/InvocationTargetException;"));
+ Handle<mirror::Class> j_l_rl_Parameter =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/reflect/Parameter;"));
+ Handle<mirror::Class> j_n_b =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/Buffer;"));
+ Handle<mirror::Class> j_n_bb =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/ByteBuffer;"));
+ Handle<mirror::Class> j_n_dbb =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/DirectByteBuffer;"));
+ Handle<mirror::Class> j_u_c =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/Collections;"));
+ Handle<mirror::Class> j_u_f_c =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/function/Consumer;"));
+ Handle<mirror::Class> j_i_m_fd =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljdk/internal/math/FloatingDecimal;"));
+ Handle<mirror::Class> j_i_m_fd_btab = hs.NewHandle(FindSystemClass(
+ class_linker, self, "Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;"));
+ Handle<mirror::Class> j_i_m_fd_ebtab = hs.NewHandle(FindSystemClass(
+ class_linker, self, "Ljdk/internal/math/FloatingDecimal$ExceptionalBinaryToASCIIBuffer;"));
+ Handle<mirror::Class> l_r_af =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Llibcore/reflect/AnnotationFactory;"));
+ Handle<mirror::Class> l_r_am =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Llibcore/reflect/AnnotationMember;"));
+ Handle<mirror::Class> l_u_ea =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Llibcore/util/EmptyArray;"));
+ Handle<mirror::Class> o_a_h_d_c =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Lorg/apache/harmony/dalvik/ddmc/Chunk;"));
+ Handle<mirror::Class> o_a_h_d_d_ds =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Lorg/apache/harmony/dalvik/ddmc/DdmServer;"));
- java_lang_ref_FinalizerReference_add = CacheMethod(env, "java/lang/ref/FinalizerReference", true, "add", "(Ljava/lang/Object;)V");
- java_lang_ref_ReferenceQueue_add = CacheMethod(env, "java/lang/ref/ReferenceQueue", true, "add", "(Ljava/lang/ref/Reference;)V");
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
- java_lang_reflect_InvocationTargetException_init = CacheMethod(env, java_lang_reflect_InvocationTargetException, false, "<init>", "(Ljava/lang/Throwable;)V");
- java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
- java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C");
- java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
- java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
- java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
- java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V");
- java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
- java_nio_Buffer_isDirect = CacheMethod(env, java_nio_Buffer, false, "isDirect", "()Z");
- java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
- java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V");
- libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;");
- libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "<init>", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V");
- org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
- org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(
+ d_s_bdcl.Get(),
+ /*is_static=*/ false,
+ "getLdLibraryPath",
+ "()Ljava/lang/String;",
+ pointer_size);
+ dalvik_system_DelegateLastClassLoader_init = CacheMethod(
+ d_s_dlcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;)V",
+ pointer_size);
+ dalvik_system_DexClassLoader_init = CacheMethod(
+ d_s_dcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V",
+ pointer_size);
+ dalvik_system_InMemoryDexClassLoader_init = CacheMethod(
+ d_s_imdcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V",
+ pointer_size);
+ dalvik_system_PathClassLoader_init = CacheMethod(
+ d_s_pcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;)V",
+ pointer_size);
- dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
- dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
- dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoadersAfter", "[Ljava/lang/ClassLoader;");
- dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
- dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
- dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
- dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
- dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;");
+ dalvik_system_VMRuntime_hiddenApiUsed = CacheMethod(
+ d_s_vmr.Get(),
+ /*is_static=*/ true,
+ "hiddenApiUsed",
+ "(ILjava/lang/String;Ljava/lang/String;IZ)V",
+ pointer_size);
- ScopedLocalRef<jclass> java_io_FileDescriptor(env, env->FindClass("java/io/FileDescriptor"));
- java_io_FileDescriptor_descriptor = CacheField(env, java_io_FileDescriptor.get(), false, "descriptor", "I");
+ java_lang_BootClassLoader_init =
+ CacheMethod(j_l_bcl.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_ClassLoader_loadClass = CacheMethod(
+ j_l_cl.Get(),
+ /*is_static=*/ false,
+ "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;",
+ pointer_size);
- java_lang_ClassLoader_parent =
- CacheField(env, java_lang_ClassLoader, false, "parent", "Ljava/lang/ClassLoader;");
- java_lang_Thread_parkBlocker =
- CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
- java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
- java_lang_Thread_group =
- CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
- java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
- java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
- java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
- java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
- java_lang_Thread_systemDaemon = CacheField(env, java_lang_Thread, false, "systemDaemon", "Z");
- java_lang_Thread_unparkedBeforeStart =
- CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
- java_lang_ThreadGroup_groups =
- CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
- java_lang_ThreadGroup_mainThreadGroup =
- CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_name =
- CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
- java_lang_ThreadGroup_parent =
- CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_systemThreadGroup =
- CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
- java_lang_Throwable_cause =
- CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
- java_lang_Throwable_detailMessage =
- CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
- java_lang_Throwable_stackTrace =
- CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
- java_lang_Throwable_stackState =
- CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
- java_lang_Throwable_suppressedExceptions =
- CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
+ java_lang_ClassNotFoundException_init = CacheMethod(
+ j_l_cnfe.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/Throwable;)V",
+ pointer_size);
- java_nio_Buffer_address = CacheField(env, java_nio_Buffer, false, "address", "J");
- java_nio_Buffer_capacity = CacheField(env, java_nio_Buffer, false, "capacity", "I");
- java_nio_Buffer_elementSizeShift =
- CacheField(env, java_nio_Buffer, false, "_elementSizeShift", "I");
- java_nio_Buffer_limit = CacheField(env, java_nio_Buffer, false, "limit", "I");
- java_nio_Buffer_position = CacheField(env, java_nio_Buffer, false, "position", "I");
-
- java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J");
- java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B");
- java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z");
- java_nio_ByteBuffer_limit = CacheField(env, java_nio_ByteBuffer, false, "limit", "I");
- java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I");
- java_util_Collections_EMPTY_LIST =
- CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
- libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(
- env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
- org_apache_harmony_dalvik_ddmc_Chunk_data =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
- org_apache_harmony_dalvik_ddmc_Chunk_length =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I");
- org_apache_harmony_dalvik_ddmc_Chunk_offset =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I");
- org_apache_harmony_dalvik_ddmc_Chunk_type =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "type", "I");
-
- java_lang_Boolean_valueOf = CachePrimitiveBoxingMethod(env, 'Z', "java/lang/Boolean");
- java_lang_Byte_valueOf = CachePrimitiveBoxingMethod(env, 'B', "java/lang/Byte");
- java_lang_Character_valueOf = CachePrimitiveBoxingMethod(env, 'C', "java/lang/Character");
- java_lang_Double_valueOf = CachePrimitiveBoxingMethod(env, 'D', "java/lang/Double");
- java_lang_Float_valueOf = CachePrimitiveBoxingMethod(env, 'F', "java/lang/Float");
- java_lang_Integer_valueOf = CachePrimitiveBoxingMethod(env, 'I', "java/lang/Integer");
- java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long");
- java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short");
-
+ ObjPtr<mirror::Class> j_l_Double = java_lang_Double_valueOf->GetDeclaringClass();
java_lang_Double_doubleToRawLongBits =
- CacheMethod(env, "java/lang/Double", /*is_static=*/ true, "doubleToRawLongBits", "(D)J");
+ CacheMethod(j_l_Double, /*is_static=*/ true, "doubleToRawLongBits", "(D)J", pointer_size);
+ ObjPtr<mirror::Class> j_l_Float = java_lang_Float_valueOf->GetDeclaringClass();
java_lang_Float_floatToRawIntBits =
- CacheMethod(env, "java/lang/Float", /*is_static=*/ true, "floatToRawIntBits", "(F)I");
+ CacheMethod(j_l_Float, /*is_static=*/ true, "floatToRawIntBits", "(F)I", pointer_size);
+
+ java_lang_Daemons_start = CacheMethod(
+ j_l_Daemons.Get(), /*is_static=*/ true, "start", "()V", pointer_size);
+ java_lang_Daemons_stop = CacheMethod(
+ j_l_Daemons.Get(), /*is_static=*/ true, "stop", "()V", pointer_size);
+ java_lang_Daemons_waitForDaemonStart = CacheMethod(
+ j_l_Daemons.Get(), /*is_static=*/ true, "waitForDaemonStart", "()V", pointer_size);
+
+ java_lang_Error_init = CacheMethod(
+ j_l_Error.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_IllegalAccessError_init = CacheMethod(
+ j_l_IllegalAccessError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_NoClassDefFoundError_init = CacheMethod(
+ j_l_NoClassDefFoundError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_OutOfMemoryError_init = CacheMethod(
+ j_l_OutOfMemoryError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_RuntimeException_init = CacheMethod(
+ j_l_RuntimeException.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_StackOverflowError_init = CacheMethod(
+ j_l_StackOverflowError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+
+ ObjPtr<mirror::Class> j_l_String = GetClassRoot<mirror::String>(class_linker);
+ java_lang_String_charAt = CacheMethod(
+ j_l_String, /*is_static=*/ false, "charAt", "(I)C", pointer_size);
+
+ java_lang_Thread_dispatchUncaughtException = CacheMethod(
+ j_l_Thread.Get(),
+ /*is_static=*/ false,
+ "dispatchUncaughtException",
+ "(Ljava/lang/Throwable;)V",
+ pointer_size);
+ java_lang_Thread_init = CacheMethod(
+ j_l_Thread.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V",
+ pointer_size);
+ java_lang_Thread_run = CacheMethod(
+ j_l_Thread.Get(), /*is_static=*/ false, "run", "()V", pointer_size);
+ java_lang_ThreadGroup_add = CacheMethod(
+ j_l_tg.Get(), /*is_static=*/ false, "add", "(Ljava/lang/Thread;)V", pointer_size);
+ java_lang_ThreadGroup_threadTerminated = CacheMethod(
+ j_l_tg.Get(),
+ /*is_static=*/ false,
+ "threadTerminated",
+ "(Ljava/lang/Thread;)V",
+ pointer_size);
+
+ java_lang_invoke_MethodHandle_asType = CacheMethod(
+ j_l_i_MethodHandle.Get(),
+ /*is_static=*/ false,
+ "asType",
+ "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
+ pointer_size);
+ java_lang_invoke_MethodHandle_invokeExact = CacheMethod(
+ j_l_i_MethodHandle.Get(),
+ /*is_static=*/ false,
+ "invokeExact",
+ "([Ljava/lang/Object;)Ljava/lang/Object;",
+ pointer_size);
+ java_lang_invoke_MethodHandles_lookup = CacheMethod(
+ j_l_i_MethodHandles.Get(),
+ /*is_static=*/ true,
+ "lookup",
+ "()Ljava/lang/invoke/MethodHandles$Lookup;",
+ pointer_size);
+ java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(
+ j_l_i_MethodHandles_Lookup.Get(),
+ /*is_static=*/ false,
+ "findConstructor",
+ "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
+ pointer_size);
+
+ java_lang_ref_FinalizerReference_add = CacheMethod(
+ j_l_r_fr.Get(), /*is_static=*/ true, "add", "(Ljava/lang/Object;)V", pointer_size);
+ java_lang_ref_ReferenceQueue_add = CacheMethod(
+ j_l_r_rq.Get(), /*is_static=*/ true, "add", "(Ljava/lang/ref/Reference;)V", pointer_size);
+
+ java_lang_reflect_InvocationTargetException_init = CacheMethod(
+ j_l_rl_ite.Get(), /*is_static=*/ false, "<init>", "(Ljava/lang/Throwable;)V", pointer_size);
+ java_lang_reflect_Parameter_init = CacheMethod(
+ j_l_rl_Parameter.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V",
+ pointer_size);
+
+ ObjPtr<mirror::Class> j_l_rl_Proxy = GetClassRoot<mirror::Proxy>(class_linker);
+ java_lang_reflect_Proxy_init = CacheMethod(
+ j_l_rl_Proxy,
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/reflect/InvocationHandler;)V",
+ pointer_size);
+ java_lang_reflect_Proxy_invoke = CacheMethod(
+ j_l_rl_Proxy,
+ /*is_static=*/ true,
+ "invoke",
+ "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
+ pointer_size);
+
+ java_nio_Buffer_isDirect =
+ CacheMethod(j_n_b.Get(), /*is_static=*/ false, "isDirect", "()Z", pointer_size);
+ java_nio_DirectByteBuffer_init =
+ CacheMethod(j_n_dbb.Get(), /*is_static=*/ false, "<init>", "(JI)V", pointer_size);
+
+ java_util_function_Consumer_accept = CacheMethod(
+ j_u_f_c.Get(), /*is_static=*/ false, "accept", "(Ljava/lang/Object;)V", pointer_size);
+
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D = CacheMethod(
+ j_i_m_fd.Get(),
+ /*is_static=*/ true,
+ "getBinaryToASCIIConverter",
+ "(D)Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;",
+ pointer_size);
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F = CacheMethod(
+ j_i_m_fd.Get(),
+ /*is_static=*/ true,
+ "getBinaryToASCIIConverter",
+ "(F)Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;",
+ pointer_size);
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars =
+ CacheMethod(j_i_m_fd_btab.Get(), /*is_static=*/ false, "getChars", "([C)I", pointer_size);
+
+ libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(
+ l_r_af.Get(),
+ /*is_static=*/ true,
+ "createAnnotation",
+ "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;",
+ pointer_size);
+ libcore_reflect_AnnotationMember_init = CacheMethod(
+ l_r_am.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V",
+ pointer_size);
+
+ org_apache_harmony_dalvik_ddmc_DdmServer_broadcast =
+ CacheMethod(o_a_h_d_d_ds.Get(), /*is_static=*/ true, "broadcast", "(I)V", pointer_size);
+ org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(
+ o_a_h_d_d_ds.Get(),
+ /*is_static=*/ true,
+ "dispatch",
+ "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;",
+ pointer_size);
+
+ dalvik_system_BaseDexClassLoader_pathList = CacheField(
+ d_s_bdcl.Get(), /*is_static=*/ false, "pathList", "Ldalvik/system/DexPathList;");
+ dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(
+ d_s_bdcl.Get(), /*is_static=*/ false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
+ dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter = CacheField(
+ d_s_bdcl.Get(),
+ /*is_static=*/ false,
+ "sharedLibraryLoadersAfter",
+ "[Ljava/lang/ClassLoader;");
+ dalvik_system_DexFile_cookie = CacheField(
+ d_s_df.Get(), /*is_static=*/ false, "mCookie", "Ljava/lang/Object;");
+ dalvik_system_DexFile_fileName = CacheField(
+ d_s_df.Get(), /*is_static=*/ false, "mFileName", "Ljava/lang/String;");
+ dalvik_system_DexPathList_dexElements = CacheField(
+ d_s_dpl.Get(), /*is_static=*/ false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
+ dalvik_system_DexPathList__Element_dexFile = CacheField(
+ d_s_dpl_e.Get(), /*is_static=*/ false, "dexFile", "Ldalvik/system/DexFile;");
+
+ dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(
+ d_s_vmr.Get(),
+ /*is_static=*/ true,
+ "nonSdkApiUsageConsumer",
+ "Ljava/util/function/Consumer;");
+
+ java_io_FileDescriptor_descriptor = CacheField(
+ j_i_fd.Get(), /*is_static=*/ false, "descriptor", "I");
+
+ java_lang_ClassLoader_parent = CacheField(
+ j_l_cl.Get(), /*is_static=*/ false, "parent", "Ljava/lang/ClassLoader;");
+
+ java_lang_Thread_parkBlocker =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "parkBlocker", "Ljava/lang/Object;");
+ java_lang_Thread_daemon = CacheField(j_l_Thread.Get(), /*is_static=*/ false, "daemon", "Z");
+ java_lang_Thread_group =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "group", "Ljava/lang/ThreadGroup;");
+ java_lang_Thread_lock =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "lock", "Ljava/lang/Object;");
+ java_lang_Thread_name =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "name", "Ljava/lang/String;");
+ java_lang_Thread_priority = CacheField(j_l_Thread.Get(), /*is_static=*/ false, "priority", "I");
+ java_lang_Thread_nativePeer =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "nativePeer", "J");
+ java_lang_Thread_systemDaemon =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "systemDaemon", "Z");
+ java_lang_Thread_unparkedBeforeStart =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "unparkedBeforeStart", "Z");
+
+ java_lang_ThreadGroup_groups =
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "groups", "[Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_ngroups = CacheField(j_l_tg.Get(), /*is_static=*/ false, "ngroups", "I");
+ java_lang_ThreadGroup_mainThreadGroup =
+ CacheField(j_l_tg.Get(), /*is_static=*/ true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_name =
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "name", "Ljava/lang/String;");
+ java_lang_ThreadGroup_parent =
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "parent", "Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_systemThreadGroup =
+ CacheField(j_l_tg.Get(), /*is_static=*/ true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
+
+ ObjPtr<mirror::Class> j_l_Throwable = GetClassRoot<mirror::Throwable>(class_linker);
+ java_lang_Throwable_cause = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "cause", "Ljava/lang/Throwable;");
+ java_lang_Throwable_detailMessage = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "detailMessage", "Ljava/lang/String;");
+ java_lang_Throwable_stackTrace = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "stackTrace", "[Ljava/lang/StackTraceElement;");
+ java_lang_Throwable_stackState = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "backtrace", "Ljava/lang/Object;");
+ java_lang_Throwable_suppressedExceptions = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "suppressedExceptions", "Ljava/util/List;");
+
+ java_nio_Buffer_address = CacheField(j_n_b.Get(), /*is_static=*/ false, "address", "J");
+ java_nio_Buffer_capacity = CacheField(j_n_b.Get(), /*is_static=*/ false, "capacity", "I");
+ java_nio_Buffer_elementSizeShift =
+ CacheField(j_n_b.Get(), /*is_static=*/ false, "_elementSizeShift", "I");
+ java_nio_Buffer_limit = CacheField(j_n_b.Get(), /*is_static=*/ false, "limit", "I");
+ java_nio_Buffer_position = CacheField(j_n_b.Get(), /*is_static=*/ false, "position", "I");
+
+ java_nio_ByteBuffer_hb = CacheField(j_n_bb.Get(), /*is_static=*/ false, "hb", "[B");
+ java_nio_ByteBuffer_isReadOnly =
+ CacheField(j_n_bb.Get(), /*is_static=*/ false, "isReadOnly", "Z");
+ java_nio_ByteBuffer_offset = CacheField(j_n_bb.Get(), /*is_static=*/ false, "offset", "I");
+
+ java_util_Collections_EMPTY_LIST =
+ CacheField(j_u_c.Get(), /*is_static=*/ true, "EMPTY_LIST", "Ljava/util/List;");
+
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer =
+ CacheField(j_i_m_fd_btab.Get(), /*is_static=*/ false, "buffer", "[C");
+ jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = CacheField(
+ j_i_m_fd_ebtab.Get(), /*is_static=*/ false, "image", "Ljava/lang/String;");
+
+ libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(
+ l_u_ea.Get(), /*is_static=*/ true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
+
+ org_apache_harmony_dalvik_ddmc_Chunk_data =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "data", "[B");
+ org_apache_harmony_dalvik_ddmc_Chunk_length =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "length", "I");
+ org_apache_harmony_dalvik_ddmc_Chunk_offset =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "offset", "I");
+ org_apache_harmony_dalvik_ddmc_Chunk_type =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "type", "I");
}
void WellKnownClasses::LateInit(JNIEnv* env) {
- // CacheField and CacheMethod will initialize their classes. Classes below
- // have clinit sections that call JNI methods. Late init is required
- // to make sure these JNI methods are available.
- ScopedLocalRef<jclass> java_lang_Runtime(env, env->FindClass("java/lang/Runtime"));
- java_lang_Runtime_nativeLoad =
- CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
- "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)"
- "Ljava/lang/String;");
- java_lang_reflect_Proxy_init =
- CacheMethod(env, java_lang_reflect_Proxy, false, "<init>",
- "(Ljava/lang/reflect/InvocationHandler;)V");
- // This invariant is important since otherwise we will have the entire proxy invoke system
- // confused.
- DCHECK_NE(
- jni::DecodeArtMethod(java_lang_reflect_Proxy_init)->GetEntryPointFromQuickCompiledCode(),
- GetQuickInstrumentationEntryPoint());
- java_lang_reflect_Proxy_invoke =
- CacheMethod(env, java_lang_reflect_Proxy, true, "invoke",
- "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;"
- "[Ljava/lang/Object;)Ljava/lang/Object;");
+ // Initialize the `Runtime` class that was previously initialized
+ // by `CacheMethod()` calling `FindMethodJNI()`.
+ // TODO: Move this initialization to `ClassLinker`.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::Class> j_l_Runtime =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Runtime;"));
+ bool success = class_linker->EnsureInitialized(
+ self, j_l_Runtime, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(success) << "Failed to initialize " << j_l_Runtime->PrettyDescriptor();
+
+ // The function `GetClassLoader()` in `jni_internal.cc` is checking if the caller
+ // is `java_lang_Runtime_nativeLoad` and, if so, returns the class loader override.
+ // However, this function is used several times between `WellKnownClasses::Init()`
+ // and setting up the override by the `Runtime` and requires that we take the other
+ // path, rather than returning the uninitialized override. Therefore we cannot
+ // initialize this well-known method early and require the `LateInit()`.
+ // TODO: Clean up the initialization steps.
+ java_lang_Runtime_nativeLoad = CacheMethod(
+ j_l_Runtime.Get(),
+ /*is_static=*/ true,
+ "nativeLoad",
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/String;",
+ class_linker->GetImagePointerSize());
}
void WellKnownClasses::HandleJniIdTypeChange(JNIEnv* env) {
@@ -551,85 +796,66 @@
dalvik_annotation_optimization_CriticalNative = nullptr;
dalvik_annotation_optimization_FastNative = nullptr;
dalvik_annotation_optimization_NeverCompile = nullptr;
- dalvik_system_BaseDexClassLoader = nullptr;
- dalvik_system_DelegateLastClassLoader = nullptr;
- dalvik_system_DexClassLoader = nullptr;
- dalvik_system_DexFile = nullptr;
- dalvik_system_DexPathList = nullptr;
- dalvik_system_DexPathList__Element = nullptr;
- dalvik_system_EmulatedStackFrame = nullptr;
- dalvik_system_PathClassLoader = nullptr;
- dalvik_system_VMRuntime = nullptr;
+ dalvik_annotation_optimization_NeverInline = nullptr;
java_lang_annotation_Annotation__array = nullptr;
- java_lang_BootClassLoader = nullptr;
- java_lang_ClassLoader = nullptr;
- java_lang_ClassNotFoundException = nullptr;
- java_lang_Daemons = nullptr;
- java_lang_Error = nullptr;
- java_lang_IllegalAccessError = nullptr;
- java_lang_NoClassDefFoundError = nullptr;
- java_lang_Object = nullptr;
- java_lang_OutOfMemoryError = nullptr;
- java_lang_reflect_InvocationTargetException = nullptr;
- java_lang_reflect_Parameter = nullptr;
+ java_lang_ClassValue = nullptr;
java_lang_reflect_Parameter__array = nullptr;
- java_lang_reflect_Proxy = nullptr;
- java_lang_RuntimeException = nullptr;
- java_lang_StackOverflowError = nullptr;
- java_lang_String = nullptr;
java_lang_StringFactory = nullptr;
java_lang_System = nullptr;
- java_lang_Thread = nullptr;
- java_lang_ThreadGroup = nullptr;
- java_lang_Throwable = nullptr;
java_lang_Void = nullptr;
- java_util_Collections = nullptr;
- java_nio_Buffer = nullptr;
- java_nio_ByteBuffer = nullptr;
- java_nio_DirectByteBuffer = nullptr;
- libcore_reflect_AnnotationFactory = nullptr;
- libcore_reflect_AnnotationMember = nullptr;
- libcore_util_EmptyArray = nullptr;
- org_apache_harmony_dalvik_ddmc_Chunk = nullptr;
- org_apache_harmony_dalvik_ddmc_DdmServer = nullptr;
+ libcore_reflect_AnnotationMember__array = nullptr;
dalvik_system_BaseDexClassLoader_getLdLibraryPath = nullptr;
- dalvik_system_VMRuntime_runFinalization = nullptr;
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader_init = nullptr;
+ WellKnownClasses::dalvik_system_DexClassLoader_init = nullptr;
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader_init = nullptr;
+ WellKnownClasses::dalvik_system_PathClassLoader_init = nullptr;
dalvik_system_VMRuntime_hiddenApiUsed = nullptr;
java_io_FileDescriptor_descriptor = nullptr;
java_lang_Boolean_valueOf = nullptr;
java_lang_Byte_valueOf = nullptr;
java_lang_Character_valueOf = nullptr;
+ java_lang_BootClassLoader_init = nullptr;
java_lang_ClassLoader_loadClass = nullptr;
java_lang_ClassNotFoundException_init = nullptr;
java_lang_Daemons_start = nullptr;
java_lang_Daemons_stop = nullptr;
+ java_lang_Daemons_waitForDaemonStart = nullptr;
java_lang_Double_doubleToRawLongBits = nullptr;
java_lang_Double_valueOf = nullptr;
+ java_lang_Error_init = nullptr;
java_lang_Float_floatToRawIntBits = nullptr;
java_lang_Float_valueOf = nullptr;
+ java_lang_IllegalAccessError_init = nullptr;
java_lang_Integer_valueOf = nullptr;
+ java_lang_Long_valueOf = nullptr;
+ java_lang_NoClassDefFoundError_init = nullptr;
+ java_lang_OutOfMemoryError_init = nullptr;
+ java_lang_Runtime_nativeLoad = nullptr;
+ java_lang_RuntimeException_init = nullptr;
+ java_lang_Short_valueOf = nullptr;
+ java_lang_StackOverflowError_init = nullptr;
+ java_lang_String_charAt = nullptr;
+ java_lang_Thread_dispatchUncaughtException = nullptr;
+ java_lang_Thread_init = nullptr;
+ java_lang_Thread_run = nullptr;
+ java_lang_ThreadGroup_add = nullptr;
+ java_lang_ThreadGroup_threadTerminated = nullptr;
java_lang_invoke_MethodHandle_asType = nullptr;
java_lang_invoke_MethodHandle_invokeExact = nullptr;
java_lang_invoke_MethodHandles_lookup = nullptr;
java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr;
- java_lang_Long_valueOf = nullptr;
java_lang_ref_FinalizerReference_add = nullptr;
java_lang_ref_ReferenceQueue_add = nullptr;
java_lang_reflect_InvocationTargetException_init = nullptr;
java_lang_reflect_Parameter_init = nullptr;
java_lang_reflect_Proxy_init = nullptr;
java_lang_reflect_Proxy_invoke = nullptr;
- java_lang_Runtime_nativeLoad = nullptr;
- java_lang_Short_valueOf = nullptr;
- java_lang_String_charAt = nullptr;
- java_lang_Thread_dispatchUncaughtException = nullptr;
- java_lang_Thread_init = nullptr;
- java_lang_Thread_run = nullptr;
- java_lang_ThreadGroup_add = nullptr;
- java_lang_ThreadGroup_removeThread = nullptr;
java_nio_Buffer_isDirect = nullptr;
java_nio_DirectByteBuffer_init = nullptr;
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D = nullptr;
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F = nullptr;
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars = nullptr;
libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
libcore_reflect_AnnotationMember_init = nullptr;
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = nullptr;
@@ -664,12 +890,12 @@
java_nio_Buffer_elementSizeShift = nullptr;
java_nio_Buffer_limit = nullptr;
java_nio_Buffer_position = nullptr;
- java_nio_ByteBuffer_address = nullptr;
java_nio_ByteBuffer_hb = nullptr;
java_nio_ByteBuffer_isReadOnly = nullptr;
- java_nio_ByteBuffer_limit = nullptr;
java_nio_ByteBuffer_offset = nullptr;
java_util_Collections_EMPTY_LIST = nullptr;
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer = nullptr;
+ jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = nullptr;
libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr;
org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr;
org_apache_harmony_dalvik_ddmc_Chunk_length = nullptr;
@@ -678,7 +904,8 @@
}
ObjPtr<mirror::Class> WellKnownClasses::ToClass(jclass global_jclass) {
- auto ret = ObjPtr<mirror::Class>::DownCast(Thread::Current()->DecodeJObject(global_jclass));
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ auto ret = ObjPtr<mirror::Class>::DownCast(vm->DecodeGlobal(global_jclass));
DCHECK(!ret.IsNull());
return ret;
}
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c334f5d..753d386 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -20,15 +20,45 @@
#include "base/locks.h"
#include "jni.h"
#include "obj_ptr.h"
+#include "read_barrier_option.h"
namespace art {
+class ArtField;
class ArtMethod;
namespace mirror {
class Class;
} // namespace mirror
+namespace detail {
+
+template <typename MemberType, MemberType** kMember>
+struct ClassFromMember {
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ static ObjPtr<mirror::Class> Get() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ mirror::Class* operator->() const REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
+template <typename MemberType, MemberType** kMember>
+bool operator==(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template <typename MemberType, MemberType** kMember>
+bool operator==(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+} // namespace detail
+
// Various classes used in JNI. We cache them so we don't have to keep looking them up.
struct WellKnownClasses {
@@ -53,144 +83,164 @@
private:
static void InitFieldsAndMethodsOnly(JNIEnv* env);
+ template <ArtMethod** kMethod>
+ using ClassFromMethod = detail::ClassFromMember<ArtMethod, kMethod>;
+
+ template <ArtField** kField>
+ using ClassFromField = detail::ClassFromMember<ArtField, kField>;
+
public:
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_annotation_optimization_NeverCompile;
- static jclass dalvik_system_BaseDexClassLoader;
- static jclass dalvik_system_DelegateLastClassLoader;
- static jclass dalvik_system_DexClassLoader;
- static jclass dalvik_system_DexFile;
- static jclass dalvik_system_DexPathList;
- static jclass dalvik_system_DexPathList__Element;
- static jclass dalvik_system_EmulatedStackFrame;
- static jclass dalvik_system_InMemoryDexClassLoader;
- static jclass dalvik_system_PathClassLoader;
- static jclass dalvik_system_VMRuntime;
+ static jclass dalvik_annotation_optimization_NeverInline;
static jclass java_lang_annotation_Annotation__array;
- static jclass java_lang_BootClassLoader;
- static jclass java_lang_ClassLoader;
- static jclass java_lang_ClassNotFoundException;
- static jclass java_lang_Daemons;
- static jclass java_lang_Error;
- static jclass java_lang_IllegalAccessError;
- static jclass java_lang_NoClassDefFoundError;
- static jclass java_lang_Object;
- static jclass java_lang_OutOfMemoryError;
- static jclass java_lang_reflect_InvocationTargetException;
- static jclass java_lang_reflect_Parameter;
+ static jclass java_lang_ClassValue;
static jclass java_lang_reflect_Parameter__array;
- static jclass java_lang_reflect_Proxy;
- static jclass java_lang_RuntimeException;
- static jclass java_lang_StackOverflowError;
- static jclass java_lang_String;
static jclass java_lang_StringFactory;
static jclass java_lang_System;
- static jclass java_lang_Thread;
- static jclass java_lang_ThreadGroup;
- static jclass java_lang_Throwable;
static jclass java_lang_Void;
- static jclass java_nio_Buffer;
- static jclass java_nio_ByteBuffer;
- static jclass java_nio_DirectByteBuffer;
- static jclass java_util_Collections;
- static jclass java_util_function_Consumer;
- static jclass libcore_reflect_AnnotationFactory;
- static jclass libcore_reflect_AnnotationMember;
- static jclass libcore_util_EmptyArray;
- static jclass org_apache_harmony_dalvik_ddmc_Chunk;
- static jclass org_apache_harmony_dalvik_ddmc_DdmServer;
+ static jclass libcore_reflect_AnnotationMember__array;
- static jmethodID dalvik_system_BaseDexClassLoader_getLdLibraryPath;
- static jmethodID dalvik_system_VMRuntime_runFinalization;
- static jmethodID dalvik_system_VMRuntime_hiddenApiUsed;
- static jmethodID java_lang_Boolean_valueOf;
- static jmethodID java_lang_Byte_valueOf;
- static jmethodID java_lang_Character_valueOf;
- static jmethodID java_lang_ClassLoader_loadClass;
- static jmethodID java_lang_ClassNotFoundException_init;
- static jmethodID java_lang_Daemons_start;
- static jmethodID java_lang_Daemons_stop;
- static jmethodID java_lang_Daemons_waitForDaemonStart;
- static jmethodID java_lang_Double_doubleToRawLongBits;
- static jmethodID java_lang_Double_valueOf;
- static jmethodID java_lang_Float_floatToRawIntBits;
- static jmethodID java_lang_Float_valueOf;
- static jmethodID java_lang_Integer_valueOf;
- static jmethodID java_lang_invoke_MethodHandle_asType;
- static jmethodID java_lang_invoke_MethodHandle_invokeExact;
- static jmethodID java_lang_invoke_MethodHandles_lookup;
- static jmethodID java_lang_invoke_MethodHandles_Lookup_findConstructor;
- static jmethodID java_lang_Long_valueOf;
- static jmethodID java_lang_ref_FinalizerReference_add;
- static jmethodID java_lang_ref_ReferenceQueue_add;
- static jmethodID java_lang_reflect_InvocationTargetException_init;
- static jmethodID java_lang_reflect_Parameter_init;
- static jmethodID java_lang_reflect_Proxy_init;
- static jmethodID java_lang_reflect_Proxy_invoke;
- static jmethodID java_lang_Runtime_nativeLoad;
- static jmethodID java_lang_Short_valueOf;
- static jmethodID java_lang_String_charAt;
- static jmethodID java_lang_Thread_dispatchUncaughtException;
- static jmethodID java_lang_Thread_init;
- static jmethodID java_lang_Thread_run;
- static jmethodID java_lang_ThreadGroup_add;
- static jmethodID java_lang_ThreadGroup_removeThread;
- static jmethodID java_nio_Buffer_isDirect;
- static jmethodID java_nio_DirectByteBuffer_init;
- static jmethodID java_util_function_Consumer_accept;
- static jmethodID libcore_reflect_AnnotationFactory_createAnnotation;
- static jmethodID libcore_reflect_AnnotationMember_init;
- static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
- static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ static ArtMethod* dalvik_system_BaseDexClassLoader_getLdLibraryPath;
+ static ArtMethod* dalvik_system_DelegateLastClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_DexClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_InMemoryDexClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_PathClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_VMRuntime_hiddenApiUsed;
+ static ArtMethod* java_lang_Boolean_valueOf;
+ static ArtMethod* java_lang_BootClassLoader_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Byte_valueOf;
+ static ArtMethod* java_lang_Character_valueOf;
+ static ArtMethod* java_lang_ClassLoader_loadClass;
+ static ArtMethod* java_lang_ClassNotFoundException_init;
+ static ArtMethod* java_lang_Daemons_start;
+ static ArtMethod* java_lang_Daemons_stop;
+ static ArtMethod* java_lang_Daemons_waitForDaemonStart;
+ static ArtMethod* java_lang_Double_doubleToRawLongBits;
+ static ArtMethod* java_lang_Double_valueOf;
+ static ArtMethod* java_lang_Error_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Float_floatToRawIntBits;
+ static ArtMethod* java_lang_Float_valueOf;
+ static ArtMethod* java_lang_IllegalAccessError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Integer_valueOf;
+ static ArtMethod* java_lang_Long_valueOf;
+ static ArtMethod* java_lang_NoClassDefFoundError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_OutOfMemoryError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Runtime_nativeLoad;
+ static ArtMethod* java_lang_RuntimeException_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Short_valueOf;
+ static ArtMethod* java_lang_StackOverflowError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_String_charAt;
+ static ArtMethod* java_lang_Thread_dispatchUncaughtException;
+ static ArtMethod* java_lang_Thread_init;
+ static ArtMethod* java_lang_Thread_run;
+ static ArtMethod* java_lang_ThreadGroup_add;
+ static ArtMethod* java_lang_ThreadGroup_threadTerminated;
+ static ArtMethod* java_lang_invoke_MethodHandle_asType;
+ static ArtMethod* java_lang_invoke_MethodHandle_invokeExact;
+ static ArtMethod* java_lang_invoke_MethodHandles_lookup;
+ static ArtMethod* java_lang_invoke_MethodHandles_Lookup_findConstructor;
+ static ArtMethod* java_lang_ref_FinalizerReference_add;
+ static ArtMethod* java_lang_ref_ReferenceQueue_add;
+ static ArtMethod* java_lang_reflect_InvocationTargetException_init;
+ static ArtMethod* java_lang_reflect_Parameter_init;
+ static ArtMethod* java_lang_reflect_Proxy_init;
+ static ArtMethod* java_lang_reflect_Proxy_invoke;
+ static ArtMethod* java_nio_Buffer_isDirect;
+ static ArtMethod* java_nio_DirectByteBuffer_init;
+ static ArtMethod* java_util_function_Consumer_accept;
+ static ArtMethod* jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D;
+ static ArtMethod* jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F;
+ static ArtMethod* jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars;
+ static ArtMethod* libcore_reflect_AnnotationFactory_createAnnotation;
+ static ArtMethod* libcore_reflect_AnnotationMember_init;
+ static ArtMethod* org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
+ static ArtMethod* org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
- static jfieldID dalvik_system_BaseDexClassLoader_pathList;
- static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
- static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
- static jfieldID dalvik_system_DexFile_cookie;
- static jfieldID dalvik_system_DexFile_fileName;
- static jfieldID dalvik_system_DexPathList_dexElements;
- static jfieldID dalvik_system_DexPathList__Element_dexFile;
- static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
- static jfieldID java_io_FileDescriptor_descriptor;
- static jfieldID java_lang_ClassLoader_parent;
- static jfieldID java_lang_Thread_parkBlocker;
- static jfieldID java_lang_Thread_daemon;
- static jfieldID java_lang_Thread_group;
- static jfieldID java_lang_Thread_lock;
- static jfieldID java_lang_Thread_name;
- static jfieldID java_lang_Thread_priority;
- static jfieldID java_lang_Thread_nativePeer;
- static jfieldID java_lang_Thread_systemDaemon;
- static jfieldID java_lang_Thread_unparkedBeforeStart;
- static jfieldID java_lang_ThreadGroup_groups;
- static jfieldID java_lang_ThreadGroup_ngroups;
- static jfieldID java_lang_ThreadGroup_mainThreadGroup;
- static jfieldID java_lang_ThreadGroup_name;
- static jfieldID java_lang_ThreadGroup_parent;
- static jfieldID java_lang_ThreadGroup_systemThreadGroup;
- static jfieldID java_lang_Throwable_cause;
- static jfieldID java_lang_Throwable_detailMessage;
- static jfieldID java_lang_Throwable_stackTrace;
- static jfieldID java_lang_Throwable_stackState;
- static jfieldID java_lang_Throwable_suppressedExceptions;
- static jfieldID java_nio_Buffer_address;
- static jfieldID java_nio_Buffer_capacity;
- static jfieldID java_nio_Buffer_elementSizeShift;
- static jfieldID java_nio_Buffer_limit;
- static jfieldID java_nio_Buffer_position;
- static jfieldID java_nio_ByteBuffer_address;
- static jfieldID java_nio_ByteBuffer_hb;
- static jfieldID java_nio_ByteBuffer_isReadOnly;
- static jfieldID java_nio_ByteBuffer_limit;
- static jfieldID java_nio_ByteBuffer_offset;
+ static ArtField* dalvik_system_BaseDexClassLoader_pathList;
+ static ArtField* dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
+ static ArtField* dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
+ static ArtField* dalvik_system_DexFile_cookie;
+ static ArtField* dalvik_system_DexFile_fileName;
+ static ArtField* dalvik_system_DexPathList_dexElements;
+ static ArtField* dalvik_system_DexPathList__Element_dexFile;
+ static ArtField* dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ static ArtField* java_io_FileDescriptor_descriptor;
+ static ArtField* java_lang_ClassLoader_parent;
+ static ArtField* java_lang_Thread_parkBlocker;
+ static ArtField* java_lang_Thread_daemon;
+ static ArtField* java_lang_Thread_group;
+ static ArtField* java_lang_Thread_lock;
+ static ArtField* java_lang_Thread_name;
+ static ArtField* java_lang_Thread_priority;
+ static ArtField* java_lang_Thread_nativePeer;
+ static ArtField* java_lang_Thread_systemDaemon;
+ static ArtField* java_lang_Thread_unparkedBeforeStart;
+ static ArtField* java_lang_ThreadGroup_groups;
+ static ArtField* java_lang_ThreadGroup_ngroups;
+ static ArtField* java_lang_ThreadGroup_mainThreadGroup;
+ static ArtField* java_lang_ThreadGroup_name;
+ static ArtField* java_lang_ThreadGroup_parent;
+ static ArtField* java_lang_ThreadGroup_systemThreadGroup;
+ static ArtField* java_lang_Throwable_cause;
+ static ArtField* java_lang_Throwable_detailMessage;
+ static ArtField* java_lang_Throwable_stackTrace;
+ static ArtField* java_lang_Throwable_stackState;
+ static ArtField* java_lang_Throwable_suppressedExceptions;
+ static ArtField* java_nio_Buffer_address;
+ static ArtField* java_nio_Buffer_capacity;
+ static ArtField* java_nio_Buffer_elementSizeShift;
+ static ArtField* java_nio_Buffer_limit;
+ static ArtField* java_nio_Buffer_position;
+ static ArtField* java_nio_ByteBuffer_hb;
+ static ArtField* java_nio_ByteBuffer_isReadOnly;
+ static ArtField* java_nio_ByteBuffer_offset;
+ static ArtField* java_util_Collections_EMPTY_LIST;
+ static ArtField* jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
+ static ArtField* jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
+ static ArtField* libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_data;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_length;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_offset;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_type;
- static jfieldID java_util_Collections_EMPTY_LIST;
- static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_type;
+ static constexpr ClassFromField<&dalvik_system_BaseDexClassLoader_pathList>
+ dalvik_system_BaseDexClassLoader;
+ static constexpr ClassFromMethod<&dalvik_system_DelegateLastClassLoader_init>
+ dalvik_system_DelegateLastClassLoader;
+ static constexpr ClassFromMethod<&dalvik_system_DexClassLoader_init>
+ dalvik_system_DexClassLoader;
+ static constexpr ClassFromField<&dalvik_system_DexFile_cookie> dalvik_system_DexFile;
+ static constexpr ClassFromField<&dalvik_system_DexPathList_dexElements> dalvik_system_DexPathList;
+ static constexpr ClassFromField<&dalvik_system_DexPathList__Element_dexFile>
+ dalvik_system_DexPathList__Element;
+ static constexpr ClassFromMethod<&dalvik_system_InMemoryDexClassLoader_init>
+ dalvik_system_InMemoryDexClassLoader;
+ static constexpr ClassFromMethod<&dalvik_system_PathClassLoader_init>
+ dalvik_system_PathClassLoader;
+ static constexpr ClassFromMethod<&java_lang_BootClassLoader_init> java_lang_BootClassLoader;
+ static constexpr ClassFromField<&java_lang_ClassLoader_parent> java_lang_ClassLoader;
+ static constexpr ClassFromMethod<&java_lang_Daemons_start> java_lang_Daemons;
+ static constexpr ClassFromMethod<&java_lang_Error_init> java_lang_Error;
+ static constexpr ClassFromMethod<&java_lang_IllegalAccessError_init>
+ java_lang_IllegalAccessError;
+ static constexpr ClassFromMethod<&java_lang_NoClassDefFoundError_init>
+ java_lang_NoClassDefFoundError;
+ static constexpr ClassFromMethod<&java_lang_OutOfMemoryError_init> java_lang_OutOfMemoryError;
+ static constexpr ClassFromMethod<&java_lang_RuntimeException_init> java_lang_RuntimeException;
+ static constexpr ClassFromMethod<&java_lang_StackOverflowError_init>
+ java_lang_StackOverflowError;
+ static constexpr ClassFromField<&java_lang_Thread_daemon> java_lang_Thread;
+ static constexpr ClassFromField<&java_lang_ThreadGroup_groups> java_lang_ThreadGroup;
+ static constexpr ClassFromMethod<&java_lang_reflect_InvocationTargetException_init>
+ java_lang_reflect_InvocationTargetException;
+ static constexpr ClassFromMethod<&java_lang_reflect_Parameter_init>
+ java_lang_reflect_Parameter;
+ static constexpr ClassFromField<&java_nio_Buffer_address> java_nio_Buffer;
+ static constexpr ClassFromField<&java_util_Collections_EMPTY_LIST> java_util_Collections;
+ static constexpr ClassFromField<&libcore_util_EmptyArray_STACK_TRACE_ELEMENT>
+ libcore_util_EmptyArray;
};
} // namespace art
diff --git a/sigchainlib/libsigchain.map.txt b/sigchainlib/libsigchain.map.txt
index 02c20c7..377662a 100644
--- a/sigchainlib/libsigchain.map.txt
+++ b/sigchainlib/libsigchain.map.txt
@@ -19,7 +19,7 @@
# Export no symbols - the only external entry points are libc overrides.
# Since this section cannot be empty for APEX stubs generation we provide a
# phony entry.
- LibsigchainNonexistentFunction;
+ LibsigchainNonexistentFunction; # apex
local:
*;
};
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 5bad856..4b8b468 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -27,6 +27,8 @@
#endif
#include <algorithm>
+#include <atomic>
+#include <bitset>
#include <initializer_list>
#include <mutex>
#include <type_traits>
@@ -151,11 +153,17 @@
});
}
+// Use a bitmap to indicate which signal is being handled so that other
+// non-blocked signals are allowed to be handled, if raised.
+static constexpr size_t kSignalSetLength = _NSIG - 1;
+using KeyValueType = std::bitset<kSignalSetLength>;
static pthread_key_t GetHandlingSignalKey() {
static pthread_key_t key;
static std::once_flag once;
std::call_once(once, []() {
- int rc = pthread_key_create(&key, nullptr);
+ int rc = pthread_key_create(&key, [](void* ptr) {
+ delete reinterpret_cast<KeyValueType*>(ptr);
+ });
if (rc != 0) {
fatal("failed to create sigchain pthread key: %s", strerror(rc));
}
@@ -164,25 +172,59 @@
}
static bool GetHandlingSignal() {
- void* result = pthread_getspecific(GetHandlingSignalKey());
- return reinterpret_cast<uintptr_t>(result);
+ pthread_key_t key = GetHandlingSignalKey();
+ void* result = pthread_getspecific(key);
+ if (result == nullptr) {
+ result = new KeyValueType();
+ pthread_setspecific(key, result);
+ return false;
+ }
+ return reinterpret_cast<KeyValueType*>(result)->any();
}
-static void SetHandlingSignal(bool value) {
- pthread_setspecific(GetHandlingSignalKey(),
- reinterpret_cast<void*>(static_cast<uintptr_t>(value)));
+static bool GetHandlingSignal(int signo) {
+ pthread_key_t key = GetHandlingSignalKey();
+ void* result = pthread_getspecific(key);
+ if (result == nullptr) {
+ result = new KeyValueType();
+ pthread_setspecific(key, result);
+ return false;
+ }
+ return reinterpret_cast<KeyValueType*>(result)->test(signo - 1);
+}
+
+static bool SetHandlingSignal(int signo, bool value) {
+ // Use signe-fence to ensure that compiler doesn't reorder generated code
+ // across signal handlers.
+ pthread_key_t key = GetHandlingSignalKey();
+ std::atomic_signal_fence(std::memory_order_seq_cst);
+ void* result = pthread_getspecific(key);
+ size_t idx = signo - 1;
+ bool old;
+ if (result == nullptr) {
+ result = new KeyValueType();
+ pthread_setspecific(key, result);
+ old = false;
+ } else {
+ old = reinterpret_cast<KeyValueType*>(result)->test(idx);
+ }
+ reinterpret_cast<KeyValueType*>(result)->set(idx, value);
+ std::atomic_signal_fence(std::memory_order_seq_cst);
+ return old;
}
class ScopedHandlingSignal {
public:
- ScopedHandlingSignal() : original_value_(GetHandlingSignal()) {
- }
+ ScopedHandlingSignal(int signo, bool set)
+ : signo_(signo),
+ original_value_(set ? SetHandlingSignal(signo, true) : GetHandlingSignal(signo)) {}
~ScopedHandlingSignal() {
- SetHandlingSignal(original_value_);
+ SetHandlingSignal(signo_, original_value_);
}
private:
+ int signo_;
bool original_value_;
};
@@ -336,14 +378,20 @@
// _NSIG is 1 greater than the highest valued signal, but signals start from 1.
// Leave an empty element at index 0 for convenience.
-static SignalChain chains[_NSIG + 1];
+static SignalChain chains[_NSIG];
static bool is_signal_hook_debuggable = false;
+// Weak linkage, as the ART APEX might be deployed on devices where this symbol doesn't exist (i.e.
+// all OS's before Android U). This symbol comes from libdl.
+__attribute__((weak)) extern "C" bool android_handle_signal(int signal_number,
+ siginfo_t* info,
+ void* context);
+
void SignalChain::Handler(int signo, siginfo_t* siginfo, void* ucontext_raw) {
// Try the special handlers first.
// If one of them crashes, we'll reenter this handler and pass that crash onto the user handler.
- if (!GetHandlingSignal()) {
+ if (!GetHandlingSignal(signo)) {
for (const auto& handler : chains[signo].special_handlers_) {
if (handler.sc_sigaction == nullptr) {
break;
@@ -356,10 +404,7 @@
sigset_t previous_mask;
linked_sigprocmask(SIG_SETMASK, &handler.sc_mask, &previous_mask);
- ScopedHandlingSignal restorer;
- if (!handler_noreturn) {
- SetHandlingSignal(true);
- }
+ ScopedHandlingSignal restorer(signo, !handler_noreturn);
if (handler.sc_sigaction(signo, siginfo, ucontext_raw)) {
return;
@@ -369,6 +414,25 @@
}
}
+ // In Android 14, there's a special feature called "recoverable" GWP-ASan. GWP-ASan is a tool that
+ // finds heap-buffer-overflow and heap-use-after-free on native heap allocations (e.g. malloc()
+ // inside of JNI, not the ART heap). The way it catches buffer overflow (roughly) is by rounding
+ // up the malloc() so that it's page-sized, and mapping an inaccessible page on the left- and
+ // right-hand side. It catches use-after-free by mprotecting the allocation page to be PROT_NONE
+ // on free(). The new "recoverable" mode is designed to allow debuggerd to print a crash report,
+ // but for the app or process in question to not crash (i.e. recover) and continue even after the
+ // bug is detected. Sigchain thus must allow debuggerd to handle the signal first, and if
+ // debuggerd has promised that it can recover, and it's done the steps to allow recovery (as
+ // identified by android_handle_signal returning true), then we should return from this handler
+ // and let the app continue.
+ //
+ // For all non-GWP-ASan-recoverable crashes, or crashes where recovery is not possible,
+ // android_handle_signal returns false, and we will continue to the rest of the sigchain handler
+ // logic.
+ if (android_handle_signal != nullptr && android_handle_signal(signo, siginfo, ucontext_raw)) {
+ return;
+ }
+
// Forward to the user's signal handler.
int handler_flags = chains[signo].action_.sa_flags;
ucontext_t* ucontext = static_cast<ucontext_t*>(ucontext_raw);
@@ -407,7 +471,15 @@
if (handler == SIG_IGN) {
return;
} else if (handler == SIG_DFL) {
- fatal("exiting due to SIG_DFL handler for signal %d, ucontext %p", signo, ucontext);
+ // We'll only get here if debuggerd is disabled. In that case, whatever next tries to handle
+ // the crash will have no way to know our ucontext, and thus no way to dump the original crash
+ // stack (since we're on an alternate stack.) Let's remove our handler and return. Then the
+ // pre-crash state is restored, the crash happens again, and the next handler gets a chance.
+ log("reverting to SIG_DFL handler for signal %d, ucontext %p", signo, ucontext);
+ struct sigaction dfl = {};
+ dfl.sa_handler = SIG_DFL;
+ linked_sigaction(signo, &dfl, nullptr);
+ return;
} else {
handler(signo);
}
diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc
index d879f5a..5e9c7fe 100644
--- a/sigchainlib/sigchain_test.cc
+++ b/sigchainlib/sigchain_test.cc
@@ -37,6 +37,12 @@
#include "sigchain.h"
+#if defined(__clang__) && __has_feature(hwaddress_sanitizer)
+#define DISABLE_HWASAN __attribute__((no_sanitize("hwaddress")))
+#else
+#define DISABLE_HWASAN
+#endif
+
#if !defined(__BIONIC__)
using sigset64_t = sigset_t;
@@ -75,13 +81,17 @@
void RaiseHandled() {
sigval value;
value.sival_ptr = &value;
- pthread_sigqueue(pthread_self(), SIGSEGV, value);
+ // pthread_sigqueue would guarantee the signal is delivered to this
+ // thread, but it is a nonstandard extension and does not exist in
+ // musl. Gtest is single threaded, and these tests don't create any
+ // threads, so sigqueue can be used and will deliver to this thread.
+ sigqueue(getpid(), SIGSEGV, value);
}
void RaiseUnhandled() {
sigval value;
value.sival_ptr = nullptr;
- pthread_sigqueue(pthread_self(), SIGSEGV, value);
+ sigqueue(getpid(), SIGSEGV, value);
}
};
@@ -245,9 +255,10 @@
called = 0;
}
-TEST_F(SigchainTest, fault_address_tag) {
-#define SA_EXPOSE_TAGBITS 0x00000800
#if defined(__aarch64__)
+// The test intentionally dereferences (tagged) null to trigger SIGSEGV.
+// We need to disable HWASAN since it would catch the dereference first.
+DISABLE_HWASAN void fault_address_tag_impl() {
struct sigaction action = {};
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = [](int, siginfo_t* siginfo, void*) {
@@ -269,6 +280,13 @@
EXPECT_EXIT({ volatile int load __attribute__((unused)) = *tagged_null; },
testing::ExitedWithCode(0x2b), "");
}
+}
+#endif
+
+TEST_F(SigchainTest, fault_address_tag) {
+#define SA_EXPOSE_TAGBITS 0x00000800
+#if defined(__aarch64__)
+ fault_address_tag_impl();
#else
GTEST_SKIP() << "arm64 only";
#endif
diff --git a/test/000-nop/build b/test/000-nop/build
deleted file mode 100644
index 5233a2d..0000000
--- a/test/000-nop/build
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-# Nothing to do here.
diff --git a/test/000-nop/build.py b/test/000-nop/build.py
new file mode 100644
index 0000000..54f21cc
--- /dev/null
+++ b/test/000-nop/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ pass # Nothing to do here.
diff --git a/test/000-nop/run b/test/000-nop/run
deleted file mode 100644
index 210296b..0000000
--- a/test/000-nop/run
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo "Blort."
diff --git a/test/000-nop/run.py b/test/000-nop/run.py
new file mode 100644
index 0000000..9239ae8
--- /dev/null
+++ b/test/000-nop/run.py
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+
+def run(ctx, args):
+ ctx.echo("Blort.")
diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build
deleted file mode 100644
index a14ddc9..0000000
--- a/test/003-omnibus-opcodes/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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
-
-./default-build "$@"
diff --git a/test/003-omnibus-opcodes/javac_post.sh b/test/003-omnibus-opcodes/javac_post.sh
new file mode 100755
index 0000000..6065423
--- /dev/null
+++ b/test/003-omnibus-opcodes/javac_post.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+rm -f classes/UnresClass.class
diff --git a/test/003-omnibus-opcodes/javac_wrapper.sh b/test/003-omnibus-opcodes/javac_wrapper.sh
deleted file mode 100755
index 62e94f8..0000000
--- a/test/003-omnibus-opcodes/javac_wrapper.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-rm -f classes/UnresClass.class
diff --git a/test/003-omnibus-opcodes/src/Goto.java b/test/003-omnibus-opcodes/src/Goto.java
index d56ceae..35b4e66 100644
--- a/test/003-omnibus-opcodes/src/Goto.java
+++ b/test/003-omnibus-opcodes/src/Goto.java
@@ -44,6 +44,7 @@
if (which) {
i += filler(i);
} else {
+ // clang-format off
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
@@ -54,6 +55,7 @@
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ // clang-format on
}
return i;
@@ -67,6 +69,7 @@
if (which) {
i += filler(i);
} else {
+ // clang-format off
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
@@ -2392,6 +2395,7 @@
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ // clang-format on
}
return i;
diff --git a/test/004-JniTest/build b/test/004-JniTest/build
deleted file mode 100755
index 460f2db..0000000
--- a/test/004-JniTest/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-#
-# Perform a mostly normal build.
-# Since this test imports 'dalvik.annotation.optimization.FastNative' (and CriticalNative),
-# we put them to src-aotex/ to allow the annotations to be used at javac-compile time,
-# but remove compiled *-aotex* artifacts afterwards.
-#
-# This enables the test to compile with vanilla RI javac and work on either ART or RI.
-# TODO: The test is currently disabled for RI anyway, presumably because @CriticalNative
-# has a different ABI and cannot be tested on RI.
-#
-
-# Stop on failure.
-set -e
-
-# Use release mode to check optimizations do not break JNI.
-export D8_FLAGS=--release
-./default-build "$@"
-
-# Remove the *-aotex build artifacts (but keep src-aotex) with dalvik.* annotations.
-rm -rf classes-aotex classes-aotex.jar $TEST_NAME-aotex.jar
diff --git a/test/004-JniTest/build.py b/test/004-JniTest/build.py
new file mode 100644
index 0000000..b701d5a
--- /dev/null
+++ b/test/004-JniTest/build.py
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2022 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 shutil, os
+
+#
+# Perform a mostly normal build.
+# Since this test imports 'dalvik.annotation.optimization.FastNative' (and CriticalNative),
+# we put them to src-aotex/ to allow the annotations to be used at javac-compile time,
+# but remove compiled *-aotex* artifacts afterwards.
+#
+# This enables the test to compile with vanilla RI javac and work on either ART or RI.
+# TODO: The test is currently disabled for RI anyway, presumably because @CriticalNative
+# has a different ABI and cannot be tested on RI.
+#
+
+
+# Use release mode to check optimizations do not break JNI.
+def build(ctx):
+ ctx.default_build(d8_flags=["--release"])
+
+ # Remove the *-aotex build artifacts (but keep src-aotex) with dalvik.* annotations.
+ shutil.rmtree(ctx.test_dir / "classes-aotex")
+ if not ctx.jvm:
+ os.remove(ctx.test_dir / "classes-aotex.jar")
+ os.remove(ctx.test_dir / "004-JniTest-aotex.jar")
diff --git a/test/004-ReferenceMap/build b/test/004-ReferenceMap/build
deleted file mode 100644
index 6203c97..0000000
--- a/test/004-ReferenceMap/build
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stop if something fails.
-set -e
-
-# Do not invoke D8 for this test.
-export D8=':'
-
-######################################################################
-
-${SOONG_ZIP} --jar -o classes.jar -f classes.dex
-./default-build "$@"
diff --git a/test/004-ReferenceMap/build.py b/test/004-ReferenceMap/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/004-ReferenceMap/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/004-ReferenceMap/generate-sources b/test/004-ReferenceMap/generate-sources
new file mode 100755
index 0000000..9edc200
--- /dev/null
+++ b/test/004-ReferenceMap/generate-sources
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+# Do not invoke D8 for this test.
+export D8=':'
+
+######################################################################
+
+${SOONG_ZIP} --jar -o classes.jar -f classes.dex
diff --git a/test/004-ReferenceMap/javac_wrapper.sh b/test/004-ReferenceMap/javac_post.sh
similarity index 100%
rename from test/004-ReferenceMap/javac_wrapper.sh
rename to test/004-ReferenceMap/javac_post.sh
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 0659c0b..c84bd88 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -25,9 +25,8 @@
int t_size = sizeof(t) / sizeof(*t); \
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); \
uintptr_t native_quick_pc = method_header->ToNativeQuickPc(GetMethod(), \
- dex_pc, \
- /* is_catch_handler */ false, \
- abort_if_not_found); \
+ dex_pc, \
+ abort_if_not_found); \
if (native_quick_pc != UINTPTR_MAX) { \
CheckReferences(t, \
t_size, \
diff --git a/test/004-StackWalk/build b/test/004-StackWalk/build
deleted file mode 100644
index 6203c97..0000000
--- a/test/004-StackWalk/build
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stop if something fails.
-set -e
-
-# Do not invoke D8 for this test.
-export D8=':'
-
-######################################################################
-
-${SOONG_ZIP} --jar -o classes.jar -f classes.dex
-./default-build "$@"
diff --git a/test/004-StackWalk/build.py b/test/004-StackWalk/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/004-StackWalk/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/004-StackWalk/generate-sources b/test/004-StackWalk/generate-sources
new file mode 100755
index 0000000..9edc200
--- /dev/null
+++ b/test/004-StackWalk/generate-sources
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+# Do not invoke D8 for this test.
+export D8=':'
+
+######################################################################
+
+${SOONG_ZIP} --jar -o classes.jar -f classes.dex
diff --git a/test/004-StackWalk/javac_wrapper.sh b/test/004-StackWalk/javac_post.sh
similarity index 100%
rename from test/004-StackWalk/javac_wrapper.sh
rename to test/004-StackWalk/javac_post.sh
diff --git a/test/004-ThreadStress/check b/test/004-ThreadStress/check
deleted file mode 100755
index f389e6b..0000000
--- a/test/004-ThreadStress/check
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Do not compare numbers, so replace numbers with 'N'.
-# Remove all messages relating to failing to allocate a java-peer for the
-# shutdown thread. This can occasionally happen with this test but it is not
-# something we really need to worry about here.
-sed '-es/[0-9][0-9]*/N/g' "$2" \
- | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && grep -v "Exception creating thread peer:" "$4" \
- | diff --strip-trailing-cr -q "$3" - >/dev/null
diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run
deleted file mode 100755
index 8004036..0000000
--- a/test/004-ThreadStress/run
+++ /dev/null
@@ -1,28 +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.
-
-# Enable lock contention logging.
-${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
-return_status1=$?
-
-# Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
-# the default 1000 to 100.
-${RUN} --runtime-option -Xlockprofthreshold:10 --runtime-option -Xstackdumplockprofthreshold:20 \
- "${@}" Main --locks-only -o 100
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/004-ThreadStress/run.py b/test/004-ThreadStress/run.py
new file mode 100644
index 0000000..7cc4d79
--- /dev/null
+++ b/test/004-ThreadStress/run.py
@@ -0,0 +1,39 @@
+#!/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.
+
+import sys
+
+
+def run(ctx, args):
+ # Enable lock contention logging.
+ ctx.default_run(args, runtime_option=["-Xlockprofthreshold:10"])
+
+ # Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
+ # the default 1000 to 100.
+ ctx.default_run(
+ args,
+ test_args=["--locks-only -o 100"],
+ runtime_option=[
+ "-Xlockprofthreshold:10", "-Xstackdumplockprofthreshold:20"
+ ])
+
+ # Do not compare numbers, so replace numbers with 'N'.
+ ctx.run(fr"sed -i 's/[0-9][0-9]*/N/g' '{args.stdout_file}'")
+
+ # Remove all messages relating to failing to allocate a java-peer for the
+ # shutdown thread. This can occasionally happen with this test but it is not
+ # something we really need to worry about here.
+ ctx.run(fr"sed -i '/Exception creating thread peer:/d' '{args.stderr_file}'")
diff --git a/test/004-UnsafeTest/build.py b/test/004-UnsafeTest/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/004-UnsafeTest/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/004-UnsafeTest/test-metadata.json b/test/004-UnsafeTest/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/004-UnsafeTest/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/005-annotations/build b/test/005-annotations/build
deleted file mode 100644
index bdc1950..0000000
--- a/test/005-annotations/build
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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
-
-# Build intermediate object to preserve class-retention annotations.
-export D8_FLAGS=--intermediate
-./default-build "$@"
diff --git a/test/005-annotations/build.py b/test/005-annotations/build.py
new file mode 100644
index 0000000..a3aefce
--- /dev/null
+++ b/test/005-annotations/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Build intermediate object to preserve class-retention annotations.
+def build(ctx):
+ ctx.default_build(d8_flags=['--intermediate'])
diff --git a/test/005-annotations/javac_post.sh b/test/005-annotations/javac_post.sh
new file mode 100755
index 0000000..abec8b0
--- /dev/null
+++ b/test/005-annotations/javac_post.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Classes available at compile time, but not at runtime.
+rm -f classes/android/test/anno/MissingAnnotation.class
+rm -f 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class'
+
+# overwrite RenamedEnum in classes
+if [ -f classes2/android/test/anno/RenamedEnumClass.java ] ; then
+ mv classes2/android/test/anno/RenamedEnumClass.java classes/android/test/anno/RenamedEnumClass.java
+fi
diff --git a/test/005-annotations/javac_wrapper.sh b/test/005-annotations/javac_wrapper.sh
deleted file mode 100755
index 69e6161..0000000
--- a/test/005-annotations/javac_wrapper.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Classes available at compile time, but not at runtime.
-rm -f classes/android/test/anno/MissingAnnotation.class
-rm -f 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class'
-
-# overwrite RenamedEnum in classes
-if [ -f classes2/android/test/anno/RenamedEnumClass.java ] ; then
- mv classes2/android/test/anno/RenamedEnumClass.java classes/android/test/anno/RenamedEnumClass.java
-fi
diff --git a/test/018-stack-overflow/Android.bp b/test/018-stack-overflow/Android.bp
index 504bdaa..ef817a8 100644
--- a/test/018-stack-overflow/Android.bp
+++ b/test/018-stack-overflow/Android.bp
@@ -15,12 +15,13 @@
java_test {
name: "art-run-test-018-stack-overflow",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-cts-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-018-stack-overflow-expected-stdout",
":art-run-test-018-stack-overflow-expected-stderr",
],
+ test_suites: ["cts"],
}
// Test's expected standard output.
diff --git a/test/018-stack-overflow/test-metadata.json b/test/018-stack-overflow/test-metadata.json
new file mode 100644
index 0000000..975ada7
--- /dev/null
+++ b/test/018-stack-overflow/test-metadata.json
@@ -0,0 +1,3 @@
+{
+ "test_suites": ["cts"]
+}
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index 39595f3..5da10ca 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -119,6 +119,7 @@
testEqualsConstString();
testConstStringEquals();
testStringConcat();
+ testEmptyWithHighByte();
// Regression tests for String.setCharAt() breaking string compression invariants.
Locale en_US = new Locale("en", "US");
@@ -760,6 +761,11 @@
Assert.assertEquals("abc\u0440xyzw\u0440", "abc\u0440".concat("xyzw\u0440"));
}
+ public static void testEmptyWithHighByte() {
+ String empty = new String(new byte[0], 1);
+ Assert.assertEquals("", empty);
+ }
+
public static boolean $noinline$equalsConstString0(String s) {
return s.equals("");
}
diff --git a/test/023-many-interfaces/build b/test/023-many-interfaces/build
deleted file mode 100644
index 6d1c8e3..0000000
--- a/test/023-many-interfaces/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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
-
-# Write out a bunch of interface source files.
-./iface-gen
-
-./default-build "$@"
diff --git a/test/023-many-interfaces/build.py b/test/023-many-interfaces/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/023-many-interfaces/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/023-many-interfaces/generate-sources b/test/023-many-interfaces/generate-sources
new file mode 100755
index 0000000..f9ba1b8
--- /dev/null
+++ b/test/023-many-interfaces/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 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
+
+# Write out a bunch of interface source files.
+./iface-gen
diff --git a/test/024-illegal-access/Android.bp b/test/024-illegal-access/Android.bp
new file mode 100644
index 0000000..7ed1a67
--- /dev/null
+++ b/test/024-illegal-access/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `024-illegal-access`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-024-illegal-access-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-024-illegal-access",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-024-illegal-access-src"
+ ],
+ data: [
+ ":art-run-test-024-illegal-access-expected-stdout",
+ ":art-run-test-024-illegal-access-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-024-illegal-access-expected-stdout",
+ out: ["art-run-test-024-illegal-access-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-024-illegal-access-expected-stderr",
+ out: ["art-run-test-024-illegal-access-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/030-bad-finalizer/build.py b/test/030-bad-finalizer/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/030-bad-finalizer/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/030-bad-finalizer/expected-stdout.txt b/test/030-bad-finalizer/expected-stdout.txt
index a0ae9ab..63e2895 100644
--- a/test/030-bad-finalizer/expected-stdout.txt
+++ b/test/030-bad-finalizer/expected-stdout.txt
@@ -2,4 +2,3 @@
Finalizer started and sleeping briefly...
Finalizer done snoozing.
Finalizer sleeping forever now.
-exit status: 2
diff --git a/test/030-bad-finalizer/run b/test/030-bad-finalizer/run
deleted file mode 100755
index 54747ee..0000000
--- a/test/030-bad-finalizer/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-
-# Squash the exit status and put it in expected
-./default-run --external-log-tags "${@}"
-echo "exit status:" $?
diff --git a/test/030-bad-finalizer/run.py b/test/030-bad-finalizer/run.py
new file mode 100644
index 0000000..74fcbb9
--- /dev/null
+++ b/test/030-bad-finalizer/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f", expected_exit_code=2)
diff --git a/test/030-bad-finalizer/src/Main.java b/test/030-bad-finalizer/src/Main.java
index f33b3aa..bdc2b41 100644
--- a/test/030-bad-finalizer/src/Main.java
+++ b/test/030-bad-finalizer/src/Main.java
@@ -21,8 +21,10 @@
/**
* Test a class with a bad finalizer.
*
- * This test is inherently flaky. It assumes that the system will schedule the finalizer daemon
- * and finalizer watchdog daemon enough to reach the timeout and throwing the fatal exception.
+ * This test is inherently very slightly flaky. It assumes that the system will schedule the
+ * finalizer daemon and finalizer watchdog daemon soon and often enough to reach the timeout and
+ * throw the fatal exception before we time out. Since we build in a 100 second buffer, failures
+ * should be very rare.
*/
public class Main {
public static void main(String[] args) throws Exception {
@@ -76,6 +78,7 @@
try {
Thread.sleep(ms);
} catch (InterruptedException ie) {
+ System.out.println("Unexpected interrupt");
}
}
diff --git a/test/030-bad-finalizer/test-metadata.json b/test/030-bad-finalizer/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/030-bad-finalizer/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/032-concrete-sub/Android.bp b/test/032-concrete-sub/Android.bp
new file mode 100644
index 0000000..f4c43f8
--- /dev/null
+++ b/test/032-concrete-sub/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `032-concrete-sub`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-032-concrete-sub-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-032-concrete-sub",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-032-concrete-sub-src"
+ ],
+ data: [
+ ":art-run-test-032-concrete-sub-expected-stdout",
+ ":art-run-test-032-concrete-sub-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-032-concrete-sub-expected-stdout",
+ out: ["art-run-test-032-concrete-sub-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-032-concrete-sub-expected-stderr",
+ out: ["art-run-test-032-concrete-sub-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/034-call-null/expected-stdout.txt b/test/034-call-null/expected-stdout.txt
index a9bf5a0..e69de29 100644
--- a/test/034-call-null/expected-stdout.txt
+++ b/test/034-call-null/expected-stdout.txt
@@ -1 +0,0 @@
-exit status: 1
diff --git a/test/034-call-null/run b/test/034-call-null/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/034-call-null/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/034-call-null/run.py b/test/034-call-null/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/034-call-null/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/038-inner-null/expected-stdout.txt b/test/038-inner-null/expected-stdout.txt
index 3d87b3f..24836c2 100644
--- a/test/038-inner-null/expected-stdout.txt
+++ b/test/038-inner-null/expected-stdout.txt
@@ -1,2 +1 @@
new Special()
-exit status: 1
diff --git a/test/038-inner-null/run b/test/038-inner-null/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/038-inner-null/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/038-inner-null/run.py b/test/038-inner-null/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/038-inner-null/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/042-new-instance/Android.bp b/test/042-new-instance/Android.bp
new file mode 100644
index 0000000..fd777be
--- /dev/null
+++ b/test/042-new-instance/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `042-new-instance`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-042-new-instance-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-042-new-instance",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-042-new-instance-src"
+ ],
+ data: [
+ ":art-run-test-042-new-instance-expected-stdout",
+ ":art-run-test-042-new-instance-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-042-new-instance-expected-stdout",
+ out: ["art-run-test-042-new-instance-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-042-new-instance-expected-stderr",
+ out: ["art-run-test-042-new-instance-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/044-proxy/run b/test/044-proxy/run
deleted file mode 100644
index 4a322f3..0000000
--- a/test/044-proxy/run
+++ /dev/null
@@ -1,18 +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.
-
-# Use a smaller heap so it's easier to fill up.
-exec ${RUN} $@ --runtime-option -Xmx4m
diff --git a/test/044-proxy/run.py b/test/044-proxy/run.py
new file mode 100644
index 0000000..1784fc5
--- /dev/null
+++ b/test/044-proxy/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to fill up.
+ ctx.default_run(args, runtime_option=["-Xmx4m"])
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
index 079ad40..33841eb 100644
--- a/test/051-thread/thread_test.cc
+++ b/test/051-thread/thread_test.cc
@@ -22,7 +22,7 @@
extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env,
jclass clazz ATTRIBUTE_UNUSED) {
- return ThreadForEnv(env)->GetNativePriority();
+ return Thread::ForEnv(env)->GetNativePriority();
}
extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(
diff --git a/test/054-uncaught/expected-stdout.txt b/test/054-uncaught/expected-stdout.txt
index 878260a..7d7f03c 100644
--- a/test/054-uncaught/expected-stdout.txt
+++ b/test/054-uncaught/expected-stdout.txt
@@ -18,4 +18,3 @@
java.lang.NullPointerException: Hi diddly-ho, neighborino.
at Main.catchTheUncaught(Main.java:63)
at Main.main(Main.java:26)
-exit status: 1
diff --git a/test/054-uncaught/run b/test/054-uncaught/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/054-uncaught/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/054-uncaught/run.py b/test/054-uncaught/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/054-uncaught/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/055-enum-performance/run b/test/055-enum-performance/run
deleted file mode 100755
index e27a622..0000000
--- a/test/055-enum-performance/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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.
-
-# As this is a performance test we always use the non-debug build.
-exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/055-enum-performance/run.py b/test/055-enum-performance/run.py
new file mode 100644
index 0000000..dbb09fd
--- /dev/null
+++ b/test/055-enum-performance/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 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.
+
+
+def run(ctx, args):
+ # As this is a performance test we always use the non-debug build.
+ ctx.default_run(args, lib="libart.so")
diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build
deleted file mode 100644
index c1d711b..0000000
--- a/test/056-const-string-jumbo/build
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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
-
-# Write out files with 32768 total static string declarations, so that
-# the reference to "zorch" in the real test file will be guaranteed to
-# need a jumbo string reference (it sorts last after all the others).
-# Note: Each string reference is stored in a separate static variable,
-# and that variable's name is also represented in the strings, which
-# is why we can just have 32768 and not 65536 declarations.
-
-awk '
-BEGIN {
- writeFile("Zorch1", 0, 16383);
- writeFile("Zorch2", 16384, 32767);
-}
-function writeFile(name, start, end) {
- fileName = "src/" name ".java";
- printf("public class %s {\n", name) > fileName;
- for (i = start; i <= end; i++) {
- printf(" static public final String s%d = \"%d\";\n",
- i, i) > fileName;
- }
- printf("}\n") > fileName;
-}'
-
-./default-build "$@"
diff --git a/test/056-const-string-jumbo/build.py b/test/056-const-string-jumbo/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/056-const-string-jumbo/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/056-const-string-jumbo/generate-sources b/test/056-const-string-jumbo/generate-sources
new file mode 100755
index 0000000..5c7ade8
--- /dev/null
+++ b/test/056-const-string-jumbo/generate-sources
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 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
+
+# Write out files with 32768 total static string declarations, so that
+# the reference to "zorch" in the real test file will be guaranteed to
+# need a jumbo string reference (it sorts last after all the others).
+# Note: Each string reference is stored in a separate static variable,
+# and that variable's name is also represented in the strings, which
+# is why we can just have 32768 and not 65536 declarations.
+
+awk '
+BEGIN {
+ writeFile("Zorch1", 0, 16383);
+ writeFile("Zorch2", 16384, 32767);
+}
+function writeFile(name, start, end) {
+ fileName = "src/" name ".java";
+ printf("public class %s {\n", name) > fileName;
+ for (i = start; i <= end; i++) {
+ printf(" static public final String s%d = \"%d\";\n",
+ i, i) > fileName;
+ }
+ printf("}\n") > fileName;
+}'
diff --git a/test/059-finalizer-throw/run b/test/059-finalizer-throw/run
deleted file mode 100644
index dda4159..0000000
--- a/test/059-finalizer-throw/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-exec ${RUN} --external-log-tags "${@}"
diff --git a/test/059-finalizer-throw/run.py b/test/059-finalizer-throw/run.py
new file mode 100644
index 0000000..930dc04
--- /dev/null
+++ b/test/059-finalizer-throw/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f")
diff --git a/test/064-field-access/run b/test/064-field-access/run
deleted file mode 100644
index 4a322f3..0000000
--- a/test/064-field-access/run
+++ /dev/null
@@ -1,18 +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.
-
-# Use a smaller heap so it's easier to fill up.
-exec ${RUN} $@ --runtime-option -Xmx4m
diff --git a/test/064-field-access/run.py b/test/064-field-access/run.py
new file mode 100644
index 0000000..1784fc5
--- /dev/null
+++ b/test/064-field-access/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to fill up.
+ ctx.default_run(args, runtime_option=["-Xmx4m"])
diff --git a/test/065-mismatched-implements/build b/test/065-mismatched-implements/build
deleted file mode 100755
index 41823b5..0000000
--- a/test/065-mismatched-implements/build
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-# Make us exit on a failure.
-
-set -e
-
-# Don't use desugar because the build fails when it encounters ICCE.
-#
-# Exception in thread "main" java.lang.IllegalArgumentException
-# at com.google.common.base.Preconditions.checkArgument(Preconditions.java:108)
-# at com.google.devtools.build.android.desugar.DefaultMethodClassFixer$DefaultMethodFinder.visit(DefaultMethodClassFixer.java:295)
-export USE_DESUGAR=false
-
-./default-build "$@"
diff --git a/test/065-mismatched-implements/build.py b/test/065-mismatched-implements/build.py
new file mode 100644
index 0000000..bc346b0
--- /dev/null
+++ b/test/065-mismatched-implements/build.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Don't use desugar because the build fails when it encounters ICCE.
+#
+# Exception in thread "main" java.lang.IllegalArgumentException
+# at com.google.common.base.Preconditions.checkArgument(Preconditions.java:108)
+# at com.google.devtools.build.android.desugar.DefaultMethodClassFixer$DefaultMethodFinder.visit(DefaultMethodClassFixer.java:295)
+def build(ctx):
+ ctx.default_build(use_desugar=False)
diff --git a/test/066-mismatched-super/build b/test/066-mismatched-super/build
deleted file mode 100644
index 50aceb4..0000000
--- a/test/066-mismatched-super/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-USE_DESUGAR=false ./default-build "$@"
diff --git a/test/066-mismatched-super/build.py b/test/066-mismatched-super/build.py
new file mode 100644
index 0000000..59b119d
--- /dev/null
+++ b/test/066-mismatched-super/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(use_desugar=False)
diff --git a/test/066-mismatched-super/src/Main.java b/test/066-mismatched-super/src/Main.java
index 6ae1198..bb042b7 100644
--- a/test/066-mismatched-super/src/Main.java
+++ b/test/066-mismatched-super/src/Main.java
@@ -28,7 +28,8 @@
try {
ExtendsFinal ef = new ExtendsFinal();
System.out.println("Succeeded unexpectedly");
- } catch (VerifyError ve) {
+ } catch (VerifyError | IncompatibleClassChangeError ve) {
+ // b/242985231 - from JDK 17 the JVM throws IncompatibleClassChangeError
System.out.println("Got expected VerifyError");
}
}
diff --git a/test/069-field-type/Android.bp b/test/069-field-type/Android.bp
new file mode 100644
index 0000000..c407a92
--- /dev/null
+++ b/test/069-field-type/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `069-field-type`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-069-field-type-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-069-field-type",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-069-field-type-src"
+ ],
+ data: [
+ ":art-run-test-069-field-type-expected-stdout",
+ ":art-run-test-069-field-type-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-069-field-type-expected-stdout",
+ out: ["art-run-test-069-field-type-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-069-field-type-expected-stderr",
+ out: ["art-run-test-069-field-type-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/071-dexfile-get-static-size/build b/test/071-dexfile-get-static-size/build
deleted file mode 100755
index 4ce24cf..0000000
--- a/test/071-dexfile-get-static-size/build
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-build "$@"
-
-# Bundle with the test the following resources:
-# 1. test1.dex
-# 2. test2.dex
-# 3. test-jar.jar, containing test1.dex as classes.dex
-# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex
-mkdir test-jar
-cp res/test1.dex test-jar/classes.dex
-cp res/test2.dex test-jar/classes2.dex
-${SOONG_ZIP} -j -o res/test-jar.jar -f test-jar/classes.dex
-${SOONG_ZIP} -j -o res/multi-jar.jar -f test-jar/classes.dex -f test-jar/classes2.dex
diff --git a/test/071-dexfile-get-static-size/build.py b/test/071-dexfile-get-static-size/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/071-dexfile-get-static-size/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/071-dexfile-get-static-size/generate-sources b/test/071-dexfile-get-static-size/generate-sources
new file mode 100755
index 0000000..55afc03
--- /dev/null
+++ b/test/071-dexfile-get-static-size/generate-sources
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Bundle with the test the following resources:
+# 1. test1.dex
+# 2. test2.dex
+# 3. test-jar.jar, containing test1.dex as classes.dex
+# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex
+mkdir test-jar
+cp res/test1.dex test-jar/classes.dex
+cp res/test2.dex test-jar/classes2.dex
+${SOONG_ZIP} -j -o res/test-jar.jar -f test-jar/classes.dex
+${SOONG_ZIP} -j -o res/multi-jar.jar -f test-jar/classes.dex -f test-jar/classes2.dex
diff --git a/test/071-dexfile-map-clean/build b/test/071-dexfile-map-clean/build
deleted file mode 100755
index a171fc3..0000000
--- a/test/071-dexfile-map-clean/build
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Any JAR files used by this test shall have their classes.dex be stored, NOT compressed.
-# This is needed for our test correctness which validates classes.dex are mapped file-backed.
-#
-# In addition, align to at least 4 bytes since that's the dex alignment requirement.
-./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/071-dexfile-map-clean/build.py b/test/071-dexfile-map-clean/build.py
new file mode 100644
index 0000000..ab9c4c3
--- /dev/null
+++ b/test/071-dexfile-map-clean/build.py
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Any JAR files used by this test shall have their classes.dex be stored, NOT compressed.
+# This is needed for our test correctness which validates classes.dex are mapped file-backed.
+#
+# In addition, align to at least 4 bytes since that's the dex alignment requirement.
+def build(ctx):
+ ctx.default_build(zip_compression_method="store", zip_align_bytes="4")
diff --git a/test/071-dexfile-map-clean/run b/test/071-dexfile-map-clean/run
deleted file mode 100755
index afa2ff7..0000000
--- a/test/071-dexfile-map-clean/run
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make sure we call 'sync'
-# before executing dalvikvm because otherwise
-# it's highly likely the pushed JAR files haven't
-# been committed to permanent storage yet,
-# and when we mmap them the kernel will think
-# the memory is dirty (despite being file-backed).
-# (Note: this was reproducible 100% of the time on
-# a target angler device).
-./default-run "$@" --sync
diff --git a/test/071-dexfile-map-clean/run.py b/test/071-dexfile-map-clean/run.py
new file mode 100644
index 0000000..eda60fa
--- /dev/null
+++ b/test/071-dexfile-map-clean/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Make sure we call 'sync'
+ # before executing dalvikvm because otherwise
+ # it's highly likely the pushed JAR files haven't
+ # been committed to permanent storage yet,
+ # and when we mmap them the kernel will think
+ # the memory is dirty (despite being file-backed).
+ # (Note: this was reproducible 100% of the time on
+ # a target angler device).
+ ctx.default_run(args, sync=True)
diff --git a/test/073-mismatched-field/Android.bp b/test/073-mismatched-field/Android.bp
new file mode 100644
index 0000000..16ca7da
--- /dev/null
+++ b/test/073-mismatched-field/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `073-mismatched-field`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-073-mismatched-field-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-073-mismatched-field",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-073-mismatched-field-src"
+ ],
+ data: [
+ ":art-run-test-073-mismatched-field-expected-stdout",
+ ":art-run-test-073-mismatched-field-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-073-mismatched-field-expected-stdout",
+ out: ["art-run-test-073-mismatched-field-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-073-mismatched-field-expected-stderr",
+ out: ["art-run-test-073-mismatched-field-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/075-verification-error/Android.bp b/test/075-verification-error/Android.bp
new file mode 100644
index 0000000..ab87fc8
--- /dev/null
+++ b/test/075-verification-error/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `075-verification-error`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-075-verification-error-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-075-verification-error",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-075-verification-error-src"
+ ],
+ data: [
+ ":art-run-test-075-verification-error-expected-stdout",
+ ":art-run-test-075-verification-error-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-075-verification-error-expected-stdout",
+ out: ["art-run-test-075-verification-error-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-075-verification-error-expected-stderr",
+ out: ["art-run-test-075-verification-error-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/077-method-override/Android.bp b/test/077-method-override/Android.bp
new file mode 100644
index 0000000..f33a9ac
--- /dev/null
+++ b/test/077-method-override/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `077-method-override`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-077-method-override-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-077-method-override",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-077-method-override-src"
+ ],
+ data: [
+ ":art-run-test-077-method-override-expected-stdout",
+ ":art-run-test-077-method-override-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-077-method-override-expected-stdout",
+ out: ["art-run-test-077-method-override-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-077-method-override-expected-stderr",
+ out: ["art-run-test-077-method-override-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/080-oom-throw/run b/test/080-oom-throw/run
deleted file mode 100644
index 08db73b..0000000
--- a/test/080-oom-throw/run
+++ /dev/null
@@ -1,31 +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.
-
-# Ensure the minimum log severity is at least 'WARNING' to display the
-# stack trace shown before exception
-#
-# "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying
-# to throw OutOfMemoryError; no stack trace available"
-#
-# is set, to try to understand a recurring crash in this test (b/77567088).
-case "$ANDROID_LOG_TAGS" in
- # Lower the minimum log severity to WARNING if it was initialy set
- # to a higher level ('ERROR', 'FATAL' or 'SILENT' -- see
- # https://developer.android.com/studio/command-line/logcat#filteringOutput).
- (\*:[efs]) export ANDROID_LOG_TAGS='*:w';;
-esac
-
-exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/080-oom-throw/run.py b/test/080-oom-throw/run.py
new file mode 100644
index 0000000..fb10464
--- /dev/null
+++ b/test/080-oom-throw/run.py
@@ -0,0 +1,32 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ensure the minimum log severity is at least 'WARNING' to display the
+ # stack trace shown before exception
+ #
+ # "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying
+ # to throw OutOfMemoryError; no stack trace available"
+ #
+ # is set, to try to understand a recurring crash in this test (b/77567088).
+ if ctx.env.ANDROID_LOG_TAGS in ["*:e", "*:f", "*:s"]:
+ # Lower the minimum log severity to WARNING if it was initialy set
+ # to a higher level ('ERROR', 'FATAL' or 'SILENT' -- see
+ # https://developer.android.com/studio/command-line/logcat#filteringOutput).
+ ctx.env.ANDROID_LOG_TAGS = "*:w"
+
+ ctx.default_run(args, runtime_option=["-Xmx16m"])
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index cd36eff..c9fd361 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -55,12 +55,20 @@
private static int exhaustJavaHeap(Object[] data, int index, int size) {
Runtime.getRuntime().gc();
- while (index != data.length && size != 0) {
+ while (index != data.length) {
try {
data[index] = new byte[size];
++index;
} catch (OutOfMemoryError oome) {
- size /= 2;
+ // Rapidly shrink the object size to fill any remaining space.
+ // Use few different sizes, since detecting out-of-memory is slow.
+ if (size >= 32) {
+ size /= 32;
+ } else if (size > 1) {
+ size = 1;
+ } else {
+ break;
+ }
}
}
return index;
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 9de3f5a..53641b4 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -9756,6 +9756,7 @@
private static int ifLez(int src, int thn, int els) { return (src <= 0) ? thn : els; }
public static void testIfCcz() {
+ // clang-format off
int[] results = new int[] {
ifEqzThen0Else1(-1), 1,
ifEqzThen0Else1(0), 0,
@@ -9826,6 +9827,7 @@
ifGtzThen8Else9(0), 9,
ifGtzThen8Else9(1), 8
};
+ // clang-format on
boolean success = true;
StringBuilder fails = new StringBuilder();
diff --git a/test/088-monitor-verification/src/Main.java b/test/088-monitor-verification/src/Main.java
index 3924fa6..3ed1939 100644
--- a/test/088-monitor-verification/src/Main.java
+++ b/test/088-monitor-verification/src/Main.java
@@ -119,6 +119,7 @@
* Confirms that we can have 32 nested monitors on one method.
*/
void notExcessiveNesting() {
+ // clang-format off
assertIsManaged();
synchronized (this) { // 1
synchronized (this) { // 2
@@ -153,6 +154,7 @@
synchronized (this) { // 31
synchronized (this) { // 32
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+ // clang-format on
}
/**
@@ -160,6 +162,7 @@
* method.
*/
void notNested() {
+ // clang-format off
assertIsManaged();
synchronized (this) {} // 1
synchronized (this) {} // 2
@@ -195,6 +198,7 @@
synchronized (this) {} // 32
synchronized (this) {} // 33
synchronized (this) {} // 34
+ // clang-format on
}
/* does nothing but ensure that the compiler doesn't discard an object */
diff --git a/test/089-many-methods/build b/test/089-many-methods/build
deleted file mode 100644
index 5225e3f..0000000
--- a/test/089-many-methods/build
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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
-
-# Write out files with 65500 total static fields, instance fields, and methods
-# to exceed the dex format's limits.
-mkdir src
-awk '
-BEGIN {
- writeFileField("FillerStatic", "static public int staticInt");
- writeFileField("FillerField", "public int fieldInt");
- writeFileMethod("FillerMethod");
-}
-function writeFileField(name, type) {
- fileName = "src/" name ".java";
- printf("public class %s {\n", name) > fileName;
- for (i = 1; i <= 65500; i++) {
- printf(" %s%d;\n", type, i) > fileName;
- }
- printf("}\n") > fileName;
-}
-function writeFileMethod(name) {
- fileName = "src/" name ".java";
- printf("public class %s {\n", name) > fileName;
- for (i = 1; i <= 65500; i++) {
- printf(" public void meth%d() { }\n", i) > fileName;
- }
- printf("}\n") > fileName;
-}'
-
-# Force DEX generation so test also passes with --jvm.
-export NEED_DEX=true
-
-# Specify old API level as d8 automagically produces a multidex file
-# when the API level is above 20. Failing the build here is deliberate.
-./default-build --api-level 20 "$@" > /dev/null 2> stderr.txt || true
-
-# Check that a build failure happened (the test is not expected to run).
-EXPECTED_ERROR="Cannot fit requested classes in a single dex"
-grep -q "$EXPECTED_ERROR" stderr.txt
-rm stderr.txt # Check passed. Remove output due to non-deterministic paths.
diff --git a/test/089-many-methods/info.txt b/test/089-many-methods/info.txt
deleted file mode 100644
index 4f73bd6..0000000
--- a/test/089-many-methods/info.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Test that we print a reasonable message when the application exceeds more
-than 65536 methods.
diff --git a/test/089-many-methods/run b/test/089-many-methods/run
deleted file mode 100644
index 867dedc..0000000
--- a/test/089-many-methods/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Do nothing - the build intentionally failed.
diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build
deleted file mode 100755
index 6b298f7..0000000
--- a/test/091-override-package-private-method/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/091-override-package-private-method/javac_post.sh b/test/091-override-package-private-method/javac_post.sh
new file mode 100755
index 0000000..d2dd7e6
--- /dev/null
+++ b/test/091-override-package-private-method/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+mkdir -p classes-ex
+mv classes/OverridePackagePrivateMethodSuper.class classes-ex
diff --git a/test/091-override-package-private-method/javac_wrapper.sh b/test/091-override-package-private-method/javac_wrapper.sh
deleted file mode 100755
index 0e2b2e1..0000000
--- a/test/091-override-package-private-method/javac_wrapper.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-$JAVAC "$@"
-mkdir -p classes-ex
-mv classes/OverridePackagePrivateMethodSuper.class classes-ex
diff --git a/test/091-override-package-private-method/run b/test/091-override-package-private-method/run
deleted file mode 100755
index d8c3c79..0000000
--- a/test/091-override-package-private-method/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Use secondary switch to add secondary dex file to class path.
-exec ${RUN} "${@}" --secondary
diff --git a/test/091-override-package-private-method/run.py b/test/091-override-package-private-method/run.py
new file mode 100644
index 0000000..2746221
--- /dev/null
+++ b/test/091-override-package-private-method/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+
+def run(ctx, args):
+ # Use secondary switch to add secondary dex file to class path.
+ ctx.default_run(args, secondary=True)
diff --git a/test/099-vmdebug/check b/test/099-vmdebug/check
deleted file mode 100755
index 3d92188..0000000
--- a/test/099-vmdebug/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip the process pids and line numbers from exact error messages.
-sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/099-vmdebug/run.py b/test/099-vmdebug/run.py
new file mode 100644
index 0000000..4492f36
--- /dev/null
+++ b/test/099-vmdebug/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip the process pids and line numbers from exact error messages.
+ ctx.run(fr"sed -i '/^.*dalvikvm\(\|32\|64\) E.*\] /d' '{args.stderr_file}'")
diff --git a/test/099-vmdebug/test-metadata.json b/test/099-vmdebug/test-metadata.json
new file mode 100644
index 0000000..2d1dd45
--- /dev/null
+++ b/test/099-vmdebug/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "run-param": {
+ "default-run": true
+ }
+}
diff --git a/test/100-reflect2/expected-stdout.txt b/test/100-reflect2/expected-stdout.txt
index a59f230..ca85392 100644
--- a/test/100-reflect2/expected-stdout.txt
+++ b/test/100-reflect2/expected-stdout.txt
@@ -31,9 +31,9 @@
30 (class java.lang.Integer)
62 (class java.lang.Long)
14 (class java.lang.Short)
-[java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
-[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfNonWhitespace(), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doRepeat(int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isBlank(), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.repeat(int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.strip(), public java.lang.String java.lang.String.stripLeading(), public java.lang.String java.lang.String.stripTrailing(), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public java.util.stream.IntStream java.lang.String.chars(), public java.util.stream.IntStream java.lang.String.codePoints(), public java.util.stream.Stream java.lang.String.lines(), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(char[],int,int,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(char[],int,int,java.lang.String,int), static void java.lang.String.checkBoundsBeginEnd(int,int,int), static void java.lang.String.checkBoundsOffCount(int,int,int), static void java.lang.String.checkIndex(int,int), void java.lang.String.getChars(char[],int)]
+[java.lang.String(byte[],byte), java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
+[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, static final boolean java.lang.String.COMPACT_STRINGS, static final byte java.lang.String.LATIN1, static final byte java.lang.String.UTF16]
+[byte java.lang.String.coder(), native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfNonWhitespace(), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfNonWhitespace(), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doRepeat(int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), private native void java.lang.String.fillBytesLatin1(byte[],int), private native void java.lang.String.fillBytesUTF16(byte[],int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.outdent(java.util.List), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isBlank(), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.Object java.lang.String.transform(java.util.function.Function), public java.lang.String java.lang.String.formatted(java.lang.Object[]), public java.lang.String java.lang.String.indent(int), public java.lang.String java.lang.String.repeat(int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.strip(), public java.lang.String java.lang.String.stripIndent(), public java.lang.String java.lang.String.stripLeading(), public java.lang.String java.lang.String.stripTrailing(), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.translateEscapes(), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public java.util.stream.IntStream java.lang.String.chars(), public java.util.stream.IntStream java.lang.String.codePoints(), public java.util.stream.Stream java.lang.String.lines(), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(byte[],byte,int,java.lang.String,int), static int java.lang.String.lastIndexOf(byte[],byte,int,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static java.lang.String java.lang.String.lambda$indent$0(java.lang.String,java.lang.String), static java.lang.String java.lang.String.lambda$indent$1(java.lang.String), static java.lang.String java.lang.String.lambda$indent$2(int,java.lang.String), static java.lang.String java.lang.String.lambda$stripIndent$3(int,java.lang.String), static void java.lang.String.checkBoundsBeginEnd(int,int,int), static void java.lang.String.checkBoundsOffCount(int,int,int), static void java.lang.String.checkIndex(int,int), static void java.lang.String.checkOffset(int,int), void java.lang.String.getBytes(byte[],int,byte), void java.lang.String.getChars(char[],int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
0
diff --git a/test/1001-app-image-regions/build b/test/1001-app-image-regions/build
deleted file mode 100755
index 16c3a5b..0000000
--- a/test/1001-app-image-regions/build
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-count=10000
-echo "LMain;" >> profile
-for i in $(seq 1 "$count"); do
- echo "LOther\$Inner${i};" >> "profile"
-done
-
-# Generate the other class.
-other_file="src/Other.java"
-echo "class Other {" >> "${other_file}"
-for i in $(seq 1 "$count"); do
- echo " static class Inner${i} { void test(){} }" >> "${other_file}"
-done
-echo "}" >> "${other_file}"
-
-./default-build "$@"
diff --git a/test/1001-app-image-regions/build.py b/test/1001-app-image-regions/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/1001-app-image-regions/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/1001-app-image-regions/generate-sources b/test/1001-app-image-regions/generate-sources
new file mode 100755
index 0000000..0fbe23f
--- /dev/null
+++ b/test/1001-app-image-regions/generate-sources
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# Copyright 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+count=10000
+echo "LMain;" >> profile
+for i in $(seq 1 "$count"); do
+ echo "LOther\$Inner${i};" >> "profile"
+done
+
+# Generate the other class.
+other_file="src/Other.java"
+echo "class Other {" >> "${other_file}"
+for i in $(seq 1 "$count"); do
+ echo " static class Inner${i} { void test(){} }" >> "${other_file}"
+done
+echo "}" >> "${other_file}"
diff --git a/test/1001-app-image-regions/run b/test/1001-app-image-regions/run
deleted file mode 100644
index 128aa2e..0000000
--- a/test/1001-app-image-regions/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/1001-app-image-regions/run.py b/test/1001-app-image-regions/run.py
new file mode 100644
index 0000000..d529700
--- /dev/null
+++ b/test/1001-app-image-regions/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/1002-notify-startup/check b/test/1002-notify-startup/check
deleted file mode 100644
index eadb559..0000000
--- a/test/1002-notify-startup/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Oat file manager will complain about duplicate dex files. Ignore.
-sed -e '/.*oat_file_manager.*/d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/1002-notify-startup/run.py b/test/1002-notify-startup/run.py
new file mode 100644
index 0000000..6e15f3a
--- /dev/null
+++ b/test/1002-notify-startup/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Oat file manager will complain about duplicate dex files. Ignore.
+ ctx.run(fr"sed -i '/.*oat_file_manager.*/d' '{args.stderr_file}'")
diff --git a/test/1003-metadata-section-strings/build b/test/1003-metadata-section-strings/build
deleted file mode 100755
index cd2cacd..0000000
--- a/test/1003-metadata-section-strings/build
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-count=5000
-
-# Generate the other class.
-other_file="src-art/Other.java"
-cat >${other_file} <<EOF
-class Other {
-static String getString(int i) {
-switch(i) {
-EOF
-
-for i in $(seq 1 "$count"); do
- echo " case ${i}: return \"string$i\";" >> "${other_file}"
-done
-
-cat >>${other_file} <<EOF
-}
-return "not found";
-}
-}
-EOF
-
-./default-build "$@"
diff --git a/test/1003-metadata-section-strings/build.py b/test/1003-metadata-section-strings/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/1003-metadata-section-strings/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/1003-metadata-section-strings/generate-sources b/test/1003-metadata-section-strings/generate-sources
new file mode 100755
index 0000000..c3e73f5
--- /dev/null
+++ b/test/1003-metadata-section-strings/generate-sources
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+count=5000
+
+# Generate the other class.
+other_file="src-art/Other.java"
+cat >${other_file} <<EOF
+class Other {
+static String getString(int i) {
+switch(i) {
+EOF
+
+for i in $(seq 1 "$count"); do
+ echo " case ${i}: return \"string$i\";" >> "${other_file}"
+done
+
+cat >>${other_file} <<EOF
+}
+return "not found";
+}
+}
+EOF
diff --git a/test/1003-metadata-section-strings/run b/test/1003-metadata-section-strings/run
deleted file mode 100644
index 9762aba..0000000
--- a/test/1003-metadata-section-strings/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile -Xcompiler-option --resolve-startup-const-strings=true
diff --git a/test/1003-metadata-section-strings/run.py b/test/1003-metadata-section-strings/run.py
new file mode 100644
index 0000000..7fc97aa
--- /dev/null
+++ b/test/1003-metadata-section-strings/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=[
+ "--compiler-filter=speed-profile",
+ "--resolve-startup-const-strings=true"
+ ])
diff --git a/test/1004-checker-volatile-ref-load/run b/test/1004-checker-volatile-ref-load/run
deleted file mode 100644
index bdaa71b..0000000
--- a/test/1004-checker-volatile-ref-load/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#! /bin/bash
-#
-# Copyright (C) 2019 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.
-
-# Limit the managed heap to 16 MiB to force more garbage collections.
-exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/1004-checker-volatile-ref-load/run.py b/test/1004-checker-volatile-ref-load/run.py
new file mode 100644
index 0000000..97dc42a
--- /dev/null
+++ b/test/1004-checker-volatile-ref-load/run.py
@@ -0,0 +1,20 @@
+#! /bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ # Limit the managed heap to 16 MiB to force more garbage collections.
+ ctx.default_run(args, runtime_option=["-Xmx16m"])
diff --git a/test/105-invoke/src/Main.java b/test/105-invoke/src/Main.java
index d5f3b62..d4e4c4e 100644
--- a/test/105-invoke/src/Main.java
+++ b/test/105-invoke/src/Main.java
@@ -71,6 +71,7 @@
static int invoke(int a) {
Main foo = new Main();
+ // clang-format off
return foo.virI_I(a) +
foo.virI_II(a, 1) +
foo.virI_III(a, 1, 2) +
@@ -84,6 +85,7 @@
statI_IIIII(a, 1, 2, 3, 4) +
statI_IIIIII(a, 1, 2, 3, 4, 5) +
foo.interfaceMethod(a);
+ // clang-format on
}
public static void main(String[] args) {
diff --git a/test/107-int-math2/src/Main.java b/test/107-int-math2/src/Main.java
index ec5678d..727d47c 100644
--- a/test/107-int-math2/src/Main.java
+++ b/test/107-int-math2/src/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+// clang-format off
+
class Main extends IntMathBase {
public static boolean mBoolean1, mBoolean2;
diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build
deleted file mode 100644
index f378df1..0000000
--- a/test/111-unresolvable-exception/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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
-
-./default-build "$@"
diff --git a/test/111-unresolvable-exception/javac_post.sh b/test/111-unresolvable-exception/javac_post.sh
new file mode 100755
index 0000000..355f792
--- /dev/null
+++ b/test/111-unresolvable-exception/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Remove class available at compile time but not at run time.
+rm classes/TestException.class
diff --git a/test/111-unresolvable-exception/javac_wrapper.sh b/test/111-unresolvable-exception/javac_wrapper.sh
deleted file mode 100755
index 9e4d2a2..0000000
--- a/test/111-unresolvable-exception/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Remove class available at compile time but not at run time.
-rm classes/TestException.class
diff --git a/test/115-native-bridge/check b/test/115-native-bridge/check
deleted file mode 100755
index e01de91..0000000
--- a/test/115-native-bridge/check
+++ /dev/null
@@ -1,27 +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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# ASAN prints a warning here.
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && sed -e '/WARNING: ASan is ignoring requested __asan_handle_no_return/,+2d' "$4" \
- | diff --strip-trailing-cr -q "$3" - >/dev/null
diff --git a/test/115-native-bridge/run b/test/115-native-bridge/run
deleted file mode 100644
index 8ce44a9..0000000
--- a/test/115-native-bridge/run
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/sh
-#
-# Copyright (C) 2012 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.
-
-ARGS=${@}
-
-BRIDGE_SO=libnativebridgetestd.so
-if echo ${ARGS} | grep -q " -O"; then
- BRIDGE_SO=libnativebridgetest.so
-fi
-
-# Use libnativebridgetest as a native bridge, start NativeBridgeMain (Main is JniTest main file).
-LIBPATH=$(echo ${ARGS} | sed -r 's/.*Djava.library.path=([^ ]*) .*/\1/')
-# Trim all but the last entry in LIBPATH, which will be nativetest[64]
-LIBPATH=${LIBPATH##*:}
-ln -sf ${LIBPATH}/$BRIDGE_SO .
-touch libarttest.so
-touch libarttestd.so
-touch libinvalid.so
-ln -sf ${LIBPATH}/libarttest.so libarttest2.so
-ln -sf ${LIBPATH}/libarttestd.so libarttestd2.so
-
-# pwd likely has /, so it's a pain to put that into a sed rule.
-LEFT=$(echo ${ARGS} | sed -r 's/-Djava.library.path.*//')
-RIGHT=$(echo ${ARGS} | sed -r 's/.*Djava.library.path[^ ]* //')
-MODARGS="${LEFT} -Djava.library.path=`pwd` ${RIGHT}"
-exec ${RUN} --runtime-option -Xforce-nb-testing --runtime-option -XX:NativeBridge=$BRIDGE_SO ${MODARGS} NativeBridgeMain
diff --git a/test/115-native-bridge/run.py b/test/115-native-bridge/run.py
new file mode 100644
index 0000000..619bde0
--- /dev/null
+++ b/test/115-native-bridge/run.py
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (C) 2012 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 re
+
+
+def run(ctx, args):
+ bridge_so = "libnativebridgetest.so" if args.O else "libnativebridgetestd.so"
+ test_dir = ctx.env.DEX_LOCATION
+
+ # Use libnativebridgetest as a native bridge, start NativeBridgeMain (Main is JniTest main file).
+ for i, opt in enumerate(args.runtime_option):
+ if opt.startswith("-Djava.library.path="):
+ libpath = opt.split(":")[-1] # last entry in libpath is nativetest[64]
+ args.runtime_option[i] = "-Djava.library.path=" + test_dir
+
+ assert libpath
+ ctx.run(f"ln -sf {libpath}/{bridge_so} {test_dir}/.")
+ ctx.run(
+ f"touch {test_dir}/libarttest.so {test_dir}/libarttestd.so {test_dir}/libinvalid.so"
+ )
+ ctx.run(f"ln -sf {libpath}/libarttest.so {test_dir}/libarttest2.so")
+ ctx.run(f"ln -sf {libpath}/libarttestd.so {test_dir}/libarttestd2.so")
+
+ ctx.default_run(
+ args,
+ runtime_option=["-Xforce-nb-testing", f"-XX:NativeBridge={bridge_so}"],
+ main="NativeBridgeMain")
+
+ # ASAN prints a warning here.
+ ctx.run(
+ fr"sed -i '/WARNING: ASan is ignoring requested __asan_handle_no_return/,+2d' '{args.stderr_file}'"
+ )
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
deleted file mode 100755
index 9063685..0000000
--- a/test/116-nodex2oat/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-flags="${@}"
-
-# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
-# flag isn't set, or complain.
-# Note: prebuild is the default.
-if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
- echo "Test 116-nodex2oat cannot run in prebuild mode."
- exit 1
-fi
-
-${RUN} ${flags}
diff --git a/test/116-nodex2oat/run.py b/test/116-nodex2oat/run.py
new file mode 100644
index 0000000..5b38316
--- /dev/null
+++ b/test/116-nodex2oat/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+
+def run(ctx, args):
+ # This test is supposed to test without oat files, so doesn't work for prebuild.
+ assert not args.prebuild
+
+ ctx.default_run(args)
diff --git a/test/116-nodex2oat/src/Main.java b/test/116-nodex2oat/src/Main.java
index 5491c49..0e675c8 100644
--- a/test/116-nodex2oat/src/Main.java
+++ b/test/116-nodex2oat/src/Main.java
@@ -15,10 +15,10 @@
*/
public class Main {
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- System.out.println("Has oat is " + hasOatFile() + ".");
- }
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ System.out.println("Has oat is " + hasOatFile() + ".");
+ }
- private native static boolean hasOatFile();
+ private native static boolean hasOatFile();
}
diff --git a/test/118-noimage-dex2oat/check b/test/118-noimage-dex2oat/check
deleted file mode 100755
index 90ffdf8..0000000
--- a/test/118-noimage-dex2oat/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip the process pids and line numbers from exact error messages.
-sed -e '/^dalvikvm.* E.*\] /d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
deleted file mode 100644
index 1b6251d..0000000
--- a/test/118-noimage-dex2oat/run
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-flags="$@"
-
-# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
-# flag isn't set, or complain.
-# Note: prebuild is the default.
-if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
- echo "Test 118-noimage-dex2oat cannot run in prebuild mode."
- exit 1
-fi
-
-# Force relocation otherwise we will just use the already created core.oat/art pair.
-# Note: relocate is the default.
-if [[ "${flags}" == *--no-relocate* ]] ; then
- echo "Test 118-noimage-dex2oat is not intended to run in no-relocate mode."
- exit 1
-fi
-
-
-# Make sure we can run without an oat file.
-echo "Run -Xnoimage-dex2oat"
-${RUN} ${flags} --runtime-option -Xnoimage-dex2oat
-return_status1=$?
-
-# Make sure we can run with the oat file.
-echo "Run -Ximage-dex2oat"
-${RUN} ${flags} --runtime-option -Ximage-dex2oat
-return_status2=$?
-
-# Make sure we can run with the default settings.
-echo "Run default"
-${RUN} ${flags}
-return_status3=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/118-noimage-dex2oat/run.py b/test/118-noimage-dex2oat/run.py
new file mode 100644
index 0000000..88efcca
--- /dev/null
+++ b/test/118-noimage-dex2oat/run.py
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+
+def run(ctx, args):
+ # This test is supposed to test without oat files, so doesn't work for prebuild.
+ assert not args.prebuild
+
+ # Force relocation otherwise we will just use the already created core.oat/art pair.
+ assert args.relocate
+
+ # Make sure we can run without an oat file.
+ ctx.echo("Run -Xnoimage-dex2oat")
+ ctx.default_run(args, runtime_option=["-Xnoimage-dex2oat"])
+
+ # Make sure we can run with the oat file.
+ ctx.echo("Run -Ximage-dex2oat")
+ ctx.default_run(args, runtime_option=["-Ximage-dex2oat"])
+
+ # Make sure we can run with the default settings.
+ ctx.echo("Run default")
+ ctx.default_run(args)
+
+ # Strip the process pids and line numbers from exact error messages.
+ ctx.run(fr"sed -i '/^dalvikvm.* E.*\] /d' '{args.stderr_file}'")
diff --git a/test/118-noimage-dex2oat/src/Main.java b/test/118-noimage-dex2oat/src/Main.java
index cc19107..f83cadc 100644
--- a/test/118-noimage-dex2oat/src/Main.java
+++ b/test/118-noimage-dex2oat/src/Main.java
@@ -18,68 +18,68 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
- boolean hasImage = hasImage();
- String instructionSet = VMRuntime.getCurrentInstructionSet();
- boolean isBootClassPathOnDisk = VMRuntime.isBootClassPathOnDisk(instructionSet);
- System.out.println(
- "Has image is " + hasImage + ", is image dex2oat enabled is "
- + isImageDex2OatEnabled() + ", is BOOTCLASSPATH on disk is "
- + isBootClassPathOnDisk + ".");
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ boolean hasImage = hasImage();
+ String instructionSet = VMRuntime.getCurrentInstructionSet();
+ boolean isBootClassPathOnDisk = VMRuntime.isBootClassPathOnDisk(instructionSet);
+ System.out.println(
+ "Has image is " + hasImage + ", is image dex2oat enabled is "
+ + isImageDex2OatEnabled() + ", is BOOTCLASSPATH on disk is "
+ + isBootClassPathOnDisk + ".");
- if (hasImage && !isImageDex2OatEnabled()) {
- throw new Error("Image with dex2oat disabled runs with an oat file");
- } else if (!hasImage && isImageDex2OatEnabled()) {
- throw new Error("Image with dex2oat enabled runs without an oat file");
- }
- if (hasImage && !isBootClassPathOnDisk) {
- throw new Error("Image with dex2oat disabled runs with an image file");
- } else if (!hasImage && isBootClassPathOnDisk) {
- throw new Error("Image with dex2oat enabled runs without an image file");
+ if (hasImage && !isImageDex2OatEnabled()) {
+ throw new Error("Image with dex2oat disabled runs with an oat file");
+ } else if (!hasImage && isImageDex2OatEnabled()) {
+ throw new Error("Image with dex2oat enabled runs without an oat file");
+ }
+ if (hasImage && !isBootClassPathOnDisk) {
+ throw new Error("Image with dex2oat disabled runs with an image file");
+ } else if (!hasImage && isBootClassPathOnDisk) {
+ throw new Error("Image with dex2oat enabled runs without an image file");
+ }
+
+ testB18485243();
}
- testB18485243();
- }
+ private native static boolean hasImage();
- private native static boolean hasImage();
+ private native static boolean isImageDex2OatEnabled();
- private native static boolean isImageDex2OatEnabled();
+ private static class VMRuntime {
+ private static final Method getCurrentInstructionSetMethod;
+ private static final Method isBootClassPathOnDiskMethod;
+ static {
+ try {
+ Class<?> c = Class.forName("dalvik.system.VMRuntime");
+ getCurrentInstructionSetMethod = c.getDeclaredMethod("getCurrentInstructionSet");
+ isBootClassPathOnDiskMethod =
+ c.getDeclaredMethod("isBootClassPathOnDisk", String.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
- private static class VMRuntime {
- private static final Method getCurrentInstructionSetMethod;
- private static final Method isBootClassPathOnDiskMethod;
- static {
- try {
- Class<?> c = Class.forName("dalvik.system.VMRuntime");
- getCurrentInstructionSetMethod = c.getDeclaredMethod("getCurrentInstructionSet");
- isBootClassPathOnDiskMethod = c.getDeclaredMethod("isBootClassPathOnDisk",
- String.class);
- } catch (Exception e) {
- throw new RuntimeException(e);
+ public static String getCurrentInstructionSet() throws Exception {
+ return (String) getCurrentInstructionSetMethod.invoke(null);
+ }
+ public static boolean isBootClassPathOnDisk(String instructionSet) throws Exception {
+ return (boolean) isBootClassPathOnDiskMethod.invoke(null, instructionSet);
}
}
- public static String getCurrentInstructionSet() throws Exception {
- return (String) getCurrentInstructionSetMethod.invoke(null);
+ private static void testB18485243() throws Exception {
+ Class<?> k = Class.forName("B18485243");
+ Object o = k.newInstance();
+ Method m = k.getDeclaredMethod("run");
+ try {
+ m.invoke(o);
+ } catch (InvocationTargetException e) {
+ Throwable actual = e.getTargetException();
+ if (!(actual instanceof IncompatibleClassChangeError)) {
+ throw new AssertionError("Expected IncompatibleClassChangeError", actual);
+ }
+ }
+ System.out.println("testB18485243 PASS");
}
- public static boolean isBootClassPathOnDisk(String instructionSet) throws Exception {
- return (boolean) isBootClassPathOnDiskMethod.invoke(null, instructionSet);
- }
- }
-
- private static void testB18485243() throws Exception {
- Class<?> k = Class.forName("B18485243");
- Object o = k.newInstance();
- Method m = k.getDeclaredMethod("run");
- try {
- m.invoke(o);
- } catch (InvocationTargetException e) {
- Throwable actual = e.getTargetException();
- if (!(actual instanceof IncompatibleClassChangeError)) {
- throw new AssertionError("Expected IncompatibleClassChangeError", actual);
- }
- }
- System.out.println("testB18485243 PASS");
- }
}
diff --git a/test/122-npe/build.py b/test/122-npe/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/122-npe/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/122-npe/test-metadata.json b/test/122-npe/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/122-npe/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build
deleted file mode 100644
index a40cbc9..0000000
--- a/test/124-missing-classes/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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
-
-./default-build "$@"
diff --git a/test/124-missing-classes/javac_post.sh b/test/124-missing-classes/javac_post.sh
new file mode 100755
index 0000000..39e26bb
--- /dev/null
+++ b/test/124-missing-classes/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Some classes are available at compile time but not at run time.
+rm 'classes/MissingClass.class' 'classes/Main$MissingInnerClass.class'
diff --git a/test/124-missing-classes/javac_wrapper.sh b/test/124-missing-classes/javac_wrapper.sh
deleted file mode 100755
index c6c234d..0000000
--- a/test/124-missing-classes/javac_wrapper.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-# Some classes are available at compile time...
-$JAVAC "$@"
-
-# ...but not at run time.
-rm 'classes/MissingClass.class' 'classes/Main$MissingInnerClass.class'
diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build
deleted file mode 100644
index c827e55..0000000
--- a/test/126-miranda-multidex/build
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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
-
-# Signal to default-build that this is a multidex test.
-mkdir src-multidex
-./default-build "$@"
diff --git a/test/126-miranda-multidex/build.py b/test/126-miranda-multidex/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/126-miranda-multidex/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/126-miranda-multidex/generate-sources b/test/126-miranda-multidex/generate-sources
new file mode 100755
index 0000000..f42ba10
--- /dev/null
+++ b/test/126-miranda-multidex/generate-sources
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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
+
+# Signal to default-build that this is a multidex test.
+mkdir src-multidex
+touch src-multidex/Empty.java
diff --git a/test/126-miranda-multidex/javac_post.sh b/test/126-miranda-multidex/javac_post.sh
new file mode 100755
index 0000000..79a9cef
--- /dev/null
+++ b/test/126-miranda-multidex/javac_post.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+if [[ "$1" == "classes2" ]]; then
+ mv classes/MirandaInterface.class classes2
+fi
diff --git a/test/126-miranda-multidex/javac_wrapper.sh b/test/126-miranda-multidex/javac_wrapper.sh
deleted file mode 100755
index 71cafa1..0000000
--- a/test/126-miranda-multidex/javac_wrapper.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-if [[ "$*" != *"classes2"* ]]; then
- # First invocation: compile src/ files.
- $JAVAC "$@"
-else
- # Second invocation: move MirandaInterface.class for placement in
- # a secondary dex file. There are no other source files for the
- # secondary DEX so no compilation required.
- mv classes/MirandaInterface.class classes2
-fi
-exit $?
diff --git a/test/126-miranda-multidex/run b/test/126-miranda-multidex/run
deleted file mode 100755
index abd63cb..0000000
--- a/test/126-miranda-multidex/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-${RUN} $@
-return_status1=$?
-
-# The problem was first exposed in a no-verify setting, as that changes the resolution path
-# taken. Make sure we also test in that environment.
-${RUN} --no-verify ${@}
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/126-miranda-multidex/run.py b/test/126-miranda-multidex/run.py
new file mode 100644
index 0000000..1896315
--- /dev/null
+++ b/test/126-miranda-multidex/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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 sys
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # The problem was first exposed in a no-verify setting, as that changes the resolution path
+ # taken. Make sure we also test in that environment.
+ ctx.default_run(args, no_verify=True)
diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build
deleted file mode 100755
index f378df1..0000000
--- a/test/127-checker-secondarydex/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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
-
-./default-build "$@"
diff --git a/test/127-checker-secondarydex/javac_post.sh b/test/127-checker-secondarydex/javac_post.sh
new file mode 100755
index 0000000..e9d5d18
--- /dev/null
+++ b/test/127-checker-secondarydex/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+mkdir classes-ex
+mv classes/Super.class classes-ex
diff --git a/test/127-checker-secondarydex/javac_wrapper.sh b/test/127-checker-secondarydex/javac_wrapper.sh
deleted file mode 100755
index 7fe6a55..0000000
--- a/test/127-checker-secondarydex/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-mkdir classes-ex
-mv classes/Super.class classes-ex
diff --git a/test/127-checker-secondarydex/run b/test/127-checker-secondarydex/run
deleted file mode 100755
index d8c3c79..0000000
--- a/test/127-checker-secondarydex/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Use secondary switch to add secondary dex file to class path.
-exec ${RUN} "${@}" --secondary
diff --git a/test/127-checker-secondarydex/run.py b/test/127-checker-secondarydex/run.py
new file mode 100644
index 0000000..2746221
--- /dev/null
+++ b/test/127-checker-secondarydex/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+
+def run(ctx, args):
+ # Use secondary switch to add secondary dex file to class path.
+ ctx.default_run(args, secondary=True)
diff --git a/test/130-hprof/run b/test/130-hprof/run
deleted file mode 100644
index 73a984e..0000000
--- a/test/130-hprof/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Currently app images aren't unloaded when dex files are unloaded.
-exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/130-hprof/run.py b/test/130-hprof/run.py
new file mode 100644
index 0000000..c3fda2f
--- /dev/null
+++ b/test/130-hprof/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ # Currently app images aren't unloaded when dex files are unloaded.
+ ctx.default_run(args, secondary_app_image=False)
diff --git a/test/133-static-invoke-super/run b/test/133-static-invoke-super/run
deleted file mode 100755
index e27a622..0000000
--- a/test/133-static-invoke-super/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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.
-
-# As this is a performance test we always use the non-debug build.
-exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/133-static-invoke-super/run.py b/test/133-static-invoke-super/run.py
new file mode 100644
index 0000000..dbb09fd
--- /dev/null
+++ b/test/133-static-invoke-super/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 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.
+
+
+def run(ctx, args):
+ # As this is a performance test we always use the non-debug build.
+ ctx.default_run(args, lib="libart.so")
diff --git a/test/1336-short-finalizer-timeout/build.py b/test/1336-short-finalizer-timeout/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/1336-short-finalizer-timeout/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/1336-short-finalizer-timeout/expected-stdout.txt b/test/1336-short-finalizer-timeout/expected-stdout.txt
index 496e0e6..802b89e 100644
--- a/test/1336-short-finalizer-timeout/expected-stdout.txt
+++ b/test/1336-short-finalizer-timeout/expected-stdout.txt
@@ -3,4 +3,3 @@
Finalizer started and snoozing...
Finalizer done snoozing.
Finalizer sleeping forever now.
-exit status: 2
diff --git a/test/1336-short-finalizer-timeout/run b/test/1336-short-finalizer-timeout/run
deleted file mode 100755
index 4a07034..0000000
--- a/test/1336-short-finalizer-timeout/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-
-# Squash the exit status and put it in expected
-./default-run --external-log-tags "$@" --runtime-option -XX:FinalizerTimeoutMs=500
-echo "exit status:" $?
diff --git a/test/1336-short-finalizer-timeout/run.py b/test/1336-short-finalizer-timeout/run.py
new file mode 100644
index 0000000..3c74a56
--- /dev/null
+++ b/test/1336-short-finalizer-timeout/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ android_log_tags="*:f",
+ runtime_option=["-XX:FinalizerTimeoutMs=500"],
+ expected_exit_code=2)
diff --git a/test/1336-short-finalizer-timeout/src/Main.java b/test/1336-short-finalizer-timeout/src/Main.java
index 1a28a64..3f024b1 100644
--- a/test/1336-short-finalizer-timeout/src/Main.java
+++ b/test/1336-short-finalizer-timeout/src/Main.java
@@ -22,7 +22,8 @@
* Test a class with a bad finalizer in an environment with a short finalizer timeout.
*
* This test is inherently flaky. It assumes that the system will schedule the finalizer daemon
- * and finalizer watchdog daemon enough to reach the timeout and throwing the fatal exception.
+ * and finalizer watchdog daemon soon and often enough to reach the timeout and throw the fatal
+ * exception before we time out here.
* Largely cloned from 030-bad-finalizer.
*/
public class Main {
@@ -49,7 +50,7 @@
snooze(9800);
// We should not get here, since it should only take 5.5 seconds for the timed out
- // fiinalizer to kill the process.
+ // finalizer to kill the process.
System.out.println("UNREACHABLE");
System.exit(0);
}
diff --git a/test/1336-short-finalizer-timeout/test-metadata.json b/test/1336-short-finalizer-timeout/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/1336-short-finalizer-timeout/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/1337-gc-coverage/check b/test/1337-gc-coverage/check
deleted file mode 100755
index 67fcafb..0000000
--- a/test/1337-gc-coverage/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Check that the string "error" isn't present
-grep -vq error "$2" \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/1337-gc-coverage/run.py b/test/1337-gc-coverage/run.py
new file mode 100644
index 0000000..9216c06
--- /dev/null
+++ b/test/1337-gc-coverage/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Check that the string "error" isn't present (delete all non-error lines)
+ ctx.run(fr"sed -i '/error/!d' '{args.stdout_file}'")
diff --git a/test/1338-gc-no-los/run b/test/1338-gc-no-los/run
deleted file mode 100755
index f3c43fb..0000000
--- a/test/1338-gc-no-los/run
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-./default-run "$@" --runtime-option -XX:LargeObjectSpace=disabled
diff --git a/test/1338-gc-no-los/run.py b/test/1338-gc-no-los/run.py
new file mode 100644
index 0000000..1c64d11
--- /dev/null
+++ b/test/1338-gc-no-los/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, runtime_option=["-XX:LargeObjectSpace=disabled"])
diff --git a/test/1339-dead-reference-safe/build.py b/test/1339-dead-reference-safe/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/1339-dead-reference-safe/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/1339-dead-reference-safe/check b/test/1339-dead-reference-safe/check
deleted file mode 100644
index e7f5f96..0000000
--- a/test/1339-dead-reference-safe/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# DeadReferenceSafe result differs for interpreted mode. A real failure
-# will produce an extra line anyway.
-
-diff --ignore-matching-lines="DeadReferenceSafe count:" -q $1 $2 \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/1339-dead-reference-safe/run.py b/test/1339-dead-reference-safe/run.py
new file mode 100644
index 0000000..6b81ed4
--- /dev/null
+++ b/test/1339-dead-reference-safe/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # DeadReferenceSafe result differs for interpreted mode. A real failure
+ # will produce an extra line anyway.
+ ctx.run(
+ fr"sed -i -E 's/^(.*DeadReferenceSafe count: )[0-9]+(.*)$/\1N\2/' '{args.stdout_file}'"
+ )
diff --git a/test/1339-dead-reference-safe/test-metadata.json b/test/1339-dead-reference-safe/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/1339-dead-reference-safe/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 4e756d2..cd5b4cf 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -28,7 +28,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include <backtrace/Backtrace.h>
+#include <unwindstack/AndroidUnwinder.h>
#include "base/file_utils.h"
#include "base/logging.h"
@@ -106,47 +106,55 @@
// Helper to look for a sequence in the stack trace.
#if __linux__
-static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
+static bool CheckStack(unwindstack::AndroidUnwinder& unwinder,
+ unwindstack::AndroidUnwinderData& data,
+ const std::vector<std::string>& seq) {
size_t cur_search_index = 0; // The currently active index in seq.
CHECK_GT(seq.size(), 0U);
- bool any_empty_name = false;
- for (size_t i = 0; i < bt->NumFrames(); i++) {
- const backtrace_frame_data_t* frame = bt->GetFrame(i);
- if (BacktraceMap::IsValid(frame->map)) {
- if (cur_search_index < seq.size()) {
- LOG(INFO) << "Got " << frame->func_name << ", looking for " << seq[cur_search_index];
- if (frame->func_name.find(seq[cur_search_index]) != std::string::npos) {
- cur_search_index++;
- }
+ bool ok = true;
+ for (const unwindstack::FrameData& frame : data.frames) {
+ if (frame.map_info == nullptr) {
+ printf("Error: No map_info for frame #%02zu\n", frame.num);
+ ok = false;
+ continue;
+ }
+ const std::string& function_name = frame.function_name;
+ if (cur_search_index < seq.size()) {
+ LOG(INFO) << "Got " << function_name << ", looking for " << seq[cur_search_index];
+ if (function_name.find(seq[cur_search_index]) != std::string::npos) {
+ cur_search_index++;
}
}
- any_empty_name |= frame->func_name.empty();
- if (frame->func_name == "main") {
+ if (function_name == "main") {
break;
}
+#if !defined(__ANDROID__) && defined(__BIONIC__ ) // host-bionic
+ // TODO(b/182810709): Unwinding is broken on host-bionic so we expect some empty frames.
+#else
+ const std::string& lib_name = frame.map_info->name();
+ if (!kIsTargetBuild && lib_name.find("libc.so") != std::string::npos) {
+ // TODO(b/254626913): Unwinding can fail for libc on host.
+ } else if (function_name.empty()) {
+ printf("Error: No function name for frame #%02zu\n", frame.num);
+ ok = false;
+ }
+#endif
}
if (cur_search_index < seq.size()) {
- printf("Cannot find %s in backtrace:\n", seq[cur_search_index].c_str());
- } else if (any_empty_name) {
-#if defined(__BIONIC__ ) && !defined(__ANDROID__)
- // TODO(b/182810709): Unwinding is broken on host-bionic so we expect some empty frames.
- return true;
-#else
- printf("Missing frames in backtrace:\n");
-#endif
- } else {
- return true;
+ printf("Error: Cannot find %s\n", seq[cur_search_index].c_str());
+ ok = false;
}
- for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
- if (BacktraceMap::IsValid(it->map)) {
- printf(" %s\n", Backtrace::FormatFrameData(&*it).c_str());
+ if (!ok) {
+ printf("Backtrace:\n");
+ for (const unwindstack::FrameData& frame : data.frames) {
+ printf(" %s\n", unwinder.FormatFrame(frame).c_str());
}
}
- return false;
+ return ok;
}
static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
@@ -166,13 +174,11 @@
#if __linux__
MutexLock mu(Thread::Current(), *GetNativeDebugInfoLock()); // Avoid races with the JIT thread.
- std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
- if (!bt->Unwind(0, nullptr)) {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
printf("Cannot unwind in process.\n");
return JNI_FALSE;
- } else if (bt->NumFrames() == 0) {
- printf("No frames for unwind in process.\n");
- return JNI_FALSE;
}
// We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
@@ -186,7 +192,7 @@
"Main.main" // The Java entry method.
};
- bool result = CheckStack(bt.get(), seq);
+ bool result = CheckStack(unwinder, data, seq);
if (!kCauseSegfault) {
return result ? JNI_TRUE : JNI_FALSE;
} else {
@@ -260,17 +266,13 @@
return JNI_FALSE;
}
- std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
bool result = true;
- if (!bt->Unwind(0, nullptr)) {
+ unwindstack::AndroidRemoteUnwinder unwinder(pid);
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
printf("Cannot unwind other process.\n");
result = false;
- } else if (bt->NumFrames() == 0) {
- printf("No frames for unwind of other process.\n");
- result = false;
- }
-
- if (result) {
+ } else {
// See comment in unwindInProcess for non-exact stack matching.
// "mini-debug-info" does not include parameters to save space.
std::vector<std::string> seq = {
@@ -280,7 +282,7 @@
"Main.main" // The Java entry method.
};
- result = CheckStack(bt.get(), seq);
+ result = CheckStack(unwinder, data, seq);
}
constexpr bool kSigQuitOnFail = true;
diff --git a/test/137-cfi/run b/test/137-cfi/run
deleted file mode 100755
index f09765f..0000000
--- a/test/137-cfi/run
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Test with full DWARF debugging information.
-# Check full signatures of methods.
-${RUN} "$@" -Xcompiler-option --generate-debug-info \
- --args --test-local --args --test-remote
-return_status1=$?
-
-# The option jitthreshold:0 ensures that if we run the test in JIT mode,
-# there will be JITed frames on the callstack (it synchronously JITs on first use).
-${RUN} "$@" -Xcompiler-option --generate-debug-info \
- --runtime-option -Xjitthreshold:0 \
- --args --test-local --args --test-remote
-return_status2=$?
-
-# Test with minimal compressed debugging information.
-# Check only method names (parameters are omitted to save space).
-# Check only remote unwinding since decompression is disabled in local unwinds (b/27391690).
-${RUN} "$@" -Xcompiler-option --generate-mini-debug-info \
- --runtime-option -Xjitthreshold:0 \
- --args --test-remote
-return_status3=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/137-cfi/run.py b/test/137-cfi/run.py
new file mode 100644
index 0000000..987d147
--- /dev/null
+++ b/test/137-cfi/run.py
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 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 sys
+
+
+def run(ctx, args):
+ # Test with full DWARF debugging information.
+ # Check full signatures of methods.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--generate-debug-info"],
+ test_args=["--test-local", "--test-remote"])
+
+ # The option jitthreshold:0 ensures that if we run the test in JIT mode,
+ # there will be JITed frames on the callstack (it synchronously JITs on first use).
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--generate-debug-info"],
+ runtime_option=["-Xjitthreshold:0"],
+ test_args=["--test-local", "--test-remote"])
+
+ # Test with minimal compressed debugging information.
+ # Check only method names (parameters are omitted to save space).
+ # Check only remote unwinding since decompression is disabled in local unwinds (b/27391690).
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--generate-mini-debug-info"],
+ runtime_option=["-Xjitthreshold:0"],
+ test_args=["--test-remote"])
diff --git a/test/138-duplicate-classes-check2/build b/test/138-duplicate-classes-check2/build
deleted file mode 100755
index 6b298f7..0000000
--- a/test/138-duplicate-classes-check2/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/138-duplicate-classes-check2/javac_post.sh b/test/138-duplicate-classes-check2/javac_post.sh
new file mode 100755
index 0000000..ce8b82f
--- /dev/null
+++ b/test/138-duplicate-classes-check2/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Remove one A.class from classes-ex
+rm -f classes-ex/A.class
diff --git a/test/138-duplicate-classes-check2/javac_wrapper.sh b/test/138-duplicate-classes-check2/javac_wrapper.sh
deleted file mode 100755
index 68c352f..0000000
--- a/test/138-duplicate-classes-check2/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Remove one A.class from classes-ex
-rm -f classes-ex/A.class
diff --git a/test/139-register-natives/check b/test/139-register-natives/check
deleted file mode 100755
index f9b35f6..0000000
--- a/test/139-register-natives/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip any JNI registration error messages
-sed -e '/jni_entrypoints/d' -e '/jni_internal.cc/d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/139-register-natives/run.py b/test/139-register-natives/run.py
new file mode 100644
index 0000000..a4b0436
--- /dev/null
+++ b/test/139-register-natives/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip any JNI registration error messages
+ ctx.run(
+ fr"sed -i -E '/(jni_entrypoints|jni_internal.cc)/d' '{args.stderr_file}'")
diff --git a/test/141-class-unload/profile b/test/141-class-unload/profile
new file mode 100644
index 0000000..709f6ea
--- /dev/null
+++ b/test/141-class-unload/profile
@@ -0,0 +1,3 @@
+LMain;
+LIface;
+LImpl2;
diff --git a/test/141-class-unload/run b/test/141-class-unload/run
deleted file mode 100644
index 73a984e..0000000
--- a/test/141-class-unload/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Currently app images aren't unloaded when dex files are unloaded.
-exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/141-class-unload/run.py b/test/141-class-unload/run.py
new file mode 100644
index 0000000..691d209
--- /dev/null
+++ b/test/141-class-unload/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ # Currently app images aren't unloaded when dex files are unloaded.
+ ctx.default_run(args, profile=True, secondary_app_image=False)
diff --git a/test/141-class-unload/src-ex/Impl.java b/test/141-class-unload/src-ex/Impl.java
new file mode 100644
index 0000000..978b3de
--- /dev/null
+++ b/test/141-class-unload/src-ex/Impl.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Impl implements Iface {
+}
diff --git a/test/141-class-unload/src/Iface.java b/test/141-class-unload/src/Iface.java
new file mode 100644
index 0000000..37b513f
--- /dev/null
+++ b/test/141-class-unload/src/Iface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public interface Iface {
+ default void invokeRun(Runnable r) {
+ r.run();
+ }
+}
diff --git a/test/141-class-unload/src/Impl2.java b/test/141-class-unload/src/Impl2.java
new file mode 100644
index 0000000..8fd77e5
--- /dev/null
+++ b/test/141-class-unload/src/Impl2.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Impl2 implements Iface {
+}
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 3cfe006..716f055 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -20,6 +20,8 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.function.Consumer;
public class Main {
static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar";
@@ -49,6 +51,14 @@
testOatFilesUnloaded(getPid());
// Test that objects keep class loader live for sticky GC.
testStickyUnload(constructor);
+ // Test that copied methods recorded in a stack trace prevents unloading.
+ testCopiedMethodInStackTrace(constructor);
+ // Test that code preventing unloading holder classes of copied methods recorded in
+ // a stack trace does not crash when processing a copied method in the boot class path.
+ testCopiedBcpMethodInStackTrace();
+ // Test that code preventing unloading holder classes of copied methods recorded in
+ // a stack trace does not crash when processing a copied method in an app image.
+ testCopiedAppImageMethodInStackTrace();
} catch (Exception e) {
e.printStackTrace(System.out);
}
@@ -103,13 +113,12 @@
System.out.println(klass2.get());
}
- private static void testUnloadLoader(Constructor<?> constructor)
- throws Exception {
- WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
- // No strong references to class loader, should get unloaded.
- doUnloading();
- // If the weak reference is cleared, then it was unloaded.
- System.out.println(loader.get());
+ private static void testUnloadLoader(Constructor<?> constructor) throws Exception {
+ WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
+ // No strong references to class loader, should get unloaded.
+ doUnloading();
+ // If the weak reference is cleared, then it was unloaded.
+ System.out.println(loader.get());
}
private static void testStackTrace(Constructor<?> constructor) throws Exception {
@@ -138,33 +147,34 @@
}
static class Pair {
- public Pair(Object o, ClassLoader l) {
- object = o;
- classLoader = new WeakReference<ClassLoader>(l);
- }
+ public Pair(Object o, ClassLoader l) {
+ object = o;
+ classLoader = new WeakReference<ClassLoader>(l);
+ }
- public Object object;
- public WeakReference<ClassLoader> classLoader;
+ public Object object;
+ public WeakReference<ClassLoader> classLoader;
}
- private static Pair testNoUnloadInstanceHelper(Constructor<?> constructor) throws Exception {
+ // Make the method not inline-able to prevent the compiler optimizing away the allocation.
+ private static Pair $noinline$testNoUnloadInstanceHelper(Constructor<?> constructor)
+ throws Exception {
ClassLoader loader = (ClassLoader) constructor.newInstance(
- DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Object o = testNoUnloadHelper(loader);
return new Pair(o, loader);
}
private static void testNoUnloadInstance(Constructor<?> constructor) throws Exception {
- Pair p = testNoUnloadInstanceHelper(constructor);
+ Pair p = $noinline$testNoUnloadInstanceHelper(constructor);
doUnloading();
- // If the class loader was unloded too early due to races, just pass the test.
boolean isNull = p.classLoader.get() == null;
System.out.println("loader null " + isNull);
}
private static Class<?> setUpUnloadClass(Constructor<?> constructor) throws Exception {
ClassLoader loader = (ClassLoader) constructor.newInstance(
- DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Class<?> intHolder = loader.loadClass("IntHolder");
Method getValue = intHolder.getDeclaredMethod("getValue");
Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
@@ -201,6 +211,75 @@
System.out.println("Too small " + (s.length() < 1000));
}
+ private static void assertStackTraceContains(Throwable t, String className, String methodName) {
+ boolean found = false;
+ for (StackTraceElement e : t.getStackTrace()) {
+ if (className.equals(e.getClassName()) && methodName.equals(e.getMethodName())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new Error("Did not find " + className + "." + methodName);
+ }
+ }
+
+ private static void testCopiedMethodInStackTrace(Constructor<?> constructor) throws Exception {
+ Throwable t = $noinline$createStackTraceWithCopiedMethod(constructor);
+ doUnloading();
+ assertStackTraceContains(t, "Iface", "invokeRun");
+ }
+
+ private static Throwable $noinline$createStackTraceWithCopiedMethod(Constructor<?> constructor)
+ throws Exception {
+ ClassLoader loader = (ClassLoader) constructor.newInstance(
+ DEX_FILE, LIBRARY_SEARCH_PATH, Main.class.getClassLoader());
+ Iface impl = (Iface) loader.loadClass("Impl").newInstance();
+ Runnable throwingRunnable = new Runnable() {
+ public void run() {
+ throw new Error();
+ }
+ };
+ try {
+ impl.invokeRun(throwingRunnable);
+ System.out.println("UNREACHABLE");
+ return null;
+ } catch (Error expected) {
+ return expected;
+ }
+ }
+
+ private static void testCopiedBcpMethodInStackTrace() {
+ Consumer<Object> consumer = new Consumer<Object>() {
+ public void accept(Object o) {
+ throw new Error();
+ }
+ };
+ Error err = null;
+ try {
+ Arrays.asList(new Object[] { new Object() }).iterator().forEachRemaining(consumer);
+ } catch (Error expected) {
+ err = expected;
+ }
+ assertStackTraceContains(err, "Main", "testCopiedBcpMethodInStackTrace");
+ }
+
+ private static void testCopiedAppImageMethodInStackTrace() throws Exception {
+ Iface limpl = (Iface) Class.forName("Impl2").newInstance();
+ Runnable throwingRunnable = new Runnable() {
+ public void run() {
+ throw new Error();
+ }
+ };
+ Error err = null;
+ try {
+ limpl.invokeRun(throwingRunnable);
+ } catch (Error expected) {
+ err = expected;
+ }
+ assertStackTraceContains(err, "Main", "testCopiedAppImageMethodInStackTrace");
+ }
+
private static WeakReference<Class> setUpUnloadClassWeak(Constructor<?> constructor)
throws Exception {
return new WeakReference<Class>(setUpUnloadClass(constructor));
@@ -241,7 +320,7 @@
}
private static int getPid() throws Exception {
- return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
+ return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
}
public static native void stopJit();
diff --git a/test/143-string-value/check b/test/143-string-value/check
deleted file mode 100755
index 0691e88..0000000
--- a/test/143-string-value/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip error log messages.
-sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/143-string-value/run.py b/test/143-string-value/run.py
new file mode 100644
index 0000000..5f32b06
--- /dev/null
+++ b/test/143-string-value/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip error log messages.
+ ctx.run(fr"sed -i '/^.*dalvikvm\(\|32\|64\) E.*\] /d' '{args.stderr_file}'")
diff --git a/test/143-string-value/test-metadata.json b/test/143-string-value/test-metadata.json
new file mode 100644
index 0000000..2d1dd45
--- /dev/null
+++ b/test/143-string-value/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "run-param": {
+ "default-run": true
+ }
+}
diff --git a/test/144-static-field-sigquit/Android.bp b/test/144-static-field-sigquit/Android.bp
index 0c21c07..1febd32 100644
--- a/test/144-static-field-sigquit/Android.bp
+++ b/test/144-static-field-sigquit/Android.bp
@@ -15,7 +15,7 @@
java_test {
name: "art-run-test-144-static-field-sigquit",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-144-static-field-sigquit-expected-stdout",
diff --git a/test/144-static-field-sigquit/run.py b/test/144-static-field-sigquit/run.py
new file mode 100644
index 0000000..c4268ce
--- /dev/null
+++ b/test/144-static-field-sigquit/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:w")
diff --git a/test/146-bad-interface/check b/test/146-bad-interface/check
deleted file mode 100644
index eadb559..0000000
--- a/test/146-bad-interface/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Oat file manager will complain about duplicate dex files. Ignore.
-sed -e '/.*oat_file_manager.*/d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/146-bad-interface/run b/test/146-bad-interface/run
deleted file mode 100755
index 2b4bbb0..0000000
--- a/test/146-bad-interface/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Use the `--secondary-class-loader-context` switch to compile the secondary dex
-# file with the right class loader context. Do not use `--secondary` as we're
-# loading the *-ex.jar file in a separate class loader.
-exec ${RUN} "${@}" \
- --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]"
diff --git a/test/146-bad-interface/run.py b/test/146-bad-interface/run.py
new file mode 100644
index 0000000..d47bde2
--- /dev/null
+++ b/test/146-bad-interface/run.py
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Use the `--secondary-class-loader-context` switch to compile the secondary dex
+ # file with the right class loader context. Do not use `--secondary` as we're
+ # loading the *-ex.jar file in a separate class loader.
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(args, secondary_class_loader_context=pcl)
+
+ # Oat file manager will complain about duplicate dex files. Ignore.
+ ctx.run(fr"sed -i '/.*oat_file_manager.*/d' '{args.stderr_file}'")
diff --git a/test/148-multithread-gc-annotations/check b/test/148-multithread-gc-annotations/check
deleted file mode 100755
index 67fcafb..0000000
--- a/test/148-multithread-gc-annotations/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Check that the string "error" isn't present
-grep -vq error "$2" \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/148-multithread-gc-annotations/run.py b/test/148-multithread-gc-annotations/run.py
new file mode 100644
index 0000000..9216c06
--- /dev/null
+++ b/test/148-multithread-gc-annotations/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Check that the string "error" isn't present (delete all non-error lines)
+ ctx.run(fr"sed -i '/error/!d' '{args.stdout_file}'")
diff --git a/test/149-suspend-all-stress/check b/test/149-suspend-all-stress/check
deleted file mode 100755
index 0386d58..0000000
--- a/test/149-suspend-all-stress/check
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Only compare the last line.
-tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
-
diff --git a/test/149-suspend-all-stress/run.py b/test/149-suspend-all-stress/run.py
new file mode 100644
index 0000000..f6f670e
--- /dev/null
+++ b/test/149-suspend-all-stress/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Only compare the last line.
+ ctx.run(fr"tail -n 1 '{args.stdout_file}' > temp-stdout.txt")
+ ctx.run(fr"mv temp-stdout.txt '{args.stdout_file}'")
diff --git a/test/151-OpenFileLimit/Android.bp b/test/151-OpenFileLimit/Android.bp
index 946be83..6b2a8c9 100644
--- a/test/151-OpenFileLimit/Android.bp
+++ b/test/151-OpenFileLimit/Android.bp
@@ -15,7 +15,7 @@
java_test {
name: "art-run-test-151-OpenFileLimit",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-151-OpenFileLimit-expected-stdout",
diff --git a/test/151-OpenFileLimit/expected-stdout.txt b/test/151-OpenFileLimit/expected-stdout.txt
index 6bc45ef..18903be 100644
--- a/test/151-OpenFileLimit/expected-stdout.txt
+++ b/test/151-OpenFileLimit/expected-stdout.txt
@@ -1,3 +1,4 @@
+JNI_OnLoad called
Message includes "Too many open files"
thread run.
done.
diff --git a/test/151-OpenFileLimit/run b/test/151-OpenFileLimit/run
deleted file mode 100755
index 6faeb0d..0000000
--- a/test/151-OpenFileLimit/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Filter out expected error messages, which happen on device.
-export ANDROID_LOG_TAGS='*:f'
-
-flags="$@"
-
-# Reduce the file descriptor limit so the test will reach the limit sooner.
-ulimit -n 512
-${RUN} --external-log-tags ${flags}
diff --git a/test/151-OpenFileLimit/run.py b/test/151-OpenFileLimit/run.py
new file mode 100644
index 0000000..65d34ed
--- /dev/null
+++ b/test/151-OpenFileLimit/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import resource
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f")
diff --git a/test/151-OpenFileLimit/src/Main.java b/test/151-OpenFileLimit/src/Main.java
index 9b16090..561c17a 100644
--- a/test/151-OpenFileLimit/src/Main.java
+++ b/test/151-OpenFileLimit/src/Main.java
@@ -24,6 +24,9 @@
private static final String TEMP_FILE_NAME_SUFFIX = ".txt";
public static void main(String[] args) throws IOException {
+ System.loadLibrary(args[0]);
+
+ setRlimitNoFile(512);
// Exhaust the number of open file descriptors.
List<File> files = new ArrayList<File>();
@@ -79,4 +82,6 @@
}
}
}
+
+ public static native void setRlimitNoFile(int value);
}
diff --git a/test/157-void-class/run b/test/157-void-class/run
deleted file mode 100755
index 8c6159f..0000000
--- a/test/157-void-class/run
+++ /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.
-
-# Let the test build its own core image with --no-image and use verify,
-# so that the compiler does not try to initialize classes. This leaves the
-# java.lang.Void compile-time verified but uninitialized.
-./default-run "$@" --no-image \
- --runtime-option -Ximage-compiler-option \
- --runtime-option --compiler-filter=verify
diff --git a/test/157-void-class/run.py b/test/157-void-class/run.py
new file mode 100644
index 0000000..c0cc3e9
--- /dev/null
+++ b/test/157-void-class/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Let the test build its own core image with --no-image and use verify,
+ # so that the compiler does not try to initialize classes. This leaves the
+ # java.lang.Void compile-time verified but uninitialized.
+ ctx.default_run(
+ args,
+ image=False,
+ runtime_option=["-Ximage-compiler-option", "--compiler-filter=verify"])
diff --git a/test/158-app-image-class-table/run b/test/158-app-image-class-table/run
deleted file mode 100644
index 146e180..0000000
--- a/test/158-app-image-class-table/run
+++ /dev/null
@@ -1,17 +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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/158-app-image-class-table/run.py b/test/158-app-image-class-table/run.py
new file mode 100644
index 0000000..7678899
--- /dev/null
+++ b/test/158-app-image-class-table/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/159-app-image-fields/run b/test/159-app-image-fields/run
deleted file mode 100644
index 74facb0..0000000
--- a/test/159-app-image-fields/run
+++ /dev/null
@@ -1,21 +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.
-
-# Use a profile to put specific classes in the app image.
-# Also run the compiler with -j1 to ensure specific class verification order.
-# And limit the managed heap to 16 MiB to speed up GCs.
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
- -Xcompiler-option -j1 --runtime-option -Xmx16m
diff --git a/test/159-app-image-fields/run.py b/test/159-app-image-fields/run.py
new file mode 100644
index 0000000..ae99f54
--- /dev/null
+++ b/test/159-app-image-fields/run.py
@@ -0,0 +1,26 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image.
+ # Also run the compiler with -j1 to ensure specific class verification order.
+ # And limit the managed heap to 16 MiB to speed up GCs.
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=["--compiler-filter=speed-profile", "-j1"],
+ runtime_option=["-Xmx16m"])
diff --git a/test/159-app-image-fields/src/Main.java b/test/159-app-image-fields/src/Main.java
index e7db1e1..d238f77 100644
--- a/test/159-app-image-fields/src/Main.java
+++ b/test/159-app-image-fields/src/Main.java
@@ -115,6 +115,7 @@
// and the verification of Main is last and fills the DexCache with Derived.value.
//
class Fields {
+ // clang-format off
public static int clobberDexCache() {
return 0
+ testField0000
diff --git a/test/160-read-barrier-stress/build b/test/160-read-barrier-stress/build
deleted file mode 100755
index 90b6b95..0000000
--- a/test/160-read-barrier-stress/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-./default-build "$@" --experimental var-handles
diff --git a/test/160-read-barrier-stress/build.py b/test/160-read-barrier-stress/build.py
new file mode 100644
index 0000000..b65e088
--- /dev/null
+++ b/test/160-read-barrier-stress/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build(api_level="var-handles")
diff --git a/test/160-read-barrier-stress/run b/test/160-read-barrier-stress/run
deleted file mode 100644
index ab82229..0000000
--- a/test/160-read-barrier-stress/run
+++ /dev/null
@@ -1,18 +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.
-
-# Limit the Java heap to 16MiB to force more GCs.
-exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/160-read-barrier-stress/run.py b/test/160-read-barrier-stress/run.py
new file mode 100644
index 0000000..fb006ea
--- /dev/null
+++ b/test/160-read-barrier-stress/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Limit the Java heap to 16MiB to force more GCs.
+ ctx.default_run(args, runtime_option=["-Xmx16m"])
diff --git a/test/160-read-barrier-stress/test-metadata.json b/test/160-read-barrier-stress/test-metadata.json
new file mode 100644
index 0000000..b7e262c
--- /dev/null
+++ b/test/160-read-barrier-stress/test-metadata.json
@@ -0,0 +1,6 @@
+{
+ "build-param": {
+ "experimental": "var-handles",
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/161-final-abstract-class/build.py b/test/161-final-abstract-class/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/161-final-abstract-class/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/161-final-abstract-class/build b/test/161-final-abstract-class/generate-sources
old mode 100644
new mode 100755
similarity index 100%
rename from test/161-final-abstract-class/build
rename to test/161-final-abstract-class/generate-sources
diff --git a/test/163-app-image-methods/run b/test/163-app-image-methods/run
deleted file mode 100644
index 74facb0..0000000
--- a/test/163-app-image-methods/run
+++ /dev/null
@@ -1,21 +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.
-
-# Use a profile to put specific classes in the app image.
-# Also run the compiler with -j1 to ensure specific class verification order.
-# And limit the managed heap to 16 MiB to speed up GCs.
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
- -Xcompiler-option -j1 --runtime-option -Xmx16m
diff --git a/test/163-app-image-methods/run.py b/test/163-app-image-methods/run.py
new file mode 100644
index 0000000..ae99f54
--- /dev/null
+++ b/test/163-app-image-methods/run.py
@@ -0,0 +1,26 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image.
+ # Also run the compiler with -j1 to ensure specific class verification order.
+ # And limit the managed heap to 16 MiB to speed up GCs.
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=["--compiler-filter=speed-profile", "-j1"],
+ runtime_option=["-Xmx16m"])
diff --git a/test/164-resolution-trampoline-dex-cache/run b/test/164-resolution-trampoline-dex-cache/run
deleted file mode 100644
index 33a6f01..0000000
--- a/test/164-resolution-trampoline-dex-cache/run
+++ /dev/null
@@ -1,21 +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.
-
-# Make sure we compile the required method using speed-profile compiler filter.
-# Enable JIT through runtime option to avoid the compiler filter set by --jit.
-exec ${RUN} "${@}" \
- -Xcompiler-option --compiler-filter=speed-profile --profile \
- --runtime-option -Xusejit:true
diff --git a/test/164-resolution-trampoline-dex-cache/run.py b/test/164-resolution-trampoline-dex-cache/run.py
new file mode 100644
index 0000000..6e7b777
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Make sure we compile the required method using speed-profile compiler filter.
+ # Enable JIT through runtime option to avoid the compiler filter set by --jit.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--compiler-filter=speed-profile"],
+ profile=True,
+ runtime_option=["-Xusejit:true"])
diff --git a/test/165-lock-owner-proxy/run b/test/165-lock-owner-proxy/run
deleted file mode 100644
index 9365411..0000000
--- a/test/165-lock-owner-proxy/run
+++ /dev/null
@@ -1,18 +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.
-
-# Use a smaller heap so it's easier to potentially fill up.
-exec ${RUN} $@ --runtime-option -Xmx2m
diff --git a/test/165-lock-owner-proxy/run.py b/test/165-lock-owner-proxy/run.py
new file mode 100644
index 0000000..266f747
--- /dev/null
+++ b/test/165-lock-owner-proxy/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to potentially fill up.
+ ctx.default_run(args, runtime_option=["-Xmx2m"])
diff --git a/test/165-lock-owner-proxy/src/Main.java b/test/165-lock-owner-proxy/src/Main.java
index fff8e58..99a6d42 100644
--- a/test/165-lock-owner-proxy/src/Main.java
+++ b/test/165-lock-owner-proxy/src/Main.java
@@ -18,6 +18,11 @@
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+// Note that this is run with a tiny heap.
+// See b/260228356 for some discussion. It's unclear if and how this reliably forces an
+// OOME. For now, we're keeping this around because it appears to have detected some bugs
+// in the past. It may need revisiting.
+
public class Main {
static final int numberOfThreads = 5;
static final int totalOperations = 10000;
@@ -27,9 +32,16 @@
static volatile boolean finish = false;
public static void main(String[] args) throws Exception {
+ // Wait for system daemons to start, so that we minimize the chances of
+ // a system daemon still starting up if/when we run out of memory.
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpected interrupt:" + e);
+ }
+
inf = (SimpleInterface)Proxy.newProxyInstance(SimpleInterface.class.getClassLoader(),
new Class[] { SimpleInterface.class }, new EmptyInvocationHandler());
-
Thread garbageThread = new Thread(new GarbageRunner());
garbageThread.start();
@@ -111,6 +123,7 @@
try {
Thread.sleep(10);
} catch (Exception e) {
+ System.out.println("Unexpected exception in sleep():" + e);
}
}
}
diff --git a/test/166-bad-interface-super/build b/test/166-bad-interface-super/build
deleted file mode 100644
index bba6184..0000000
--- a/test/166-bad-interface-super/build
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Use the jasmin sources for JVM, otherwise the smali sources.
-extra_arg="--no-jasmin"
-
-for arg in "$@"; do
- if [[ "$arg" == "--jvm" ]]; then
- extra_arg="--no-smali"
- break
- fi
-done
-
-./default-build "$@" "$extra_arg"
diff --git a/test/166-bad-interface-super/build.py b/test/166-bad-interface-super/build.py
new file mode 100644
index 0000000..b2ff2d7
--- /dev/null
+++ b/test/166-bad-interface-super/build.py
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ # Use the jasmin sources for JVM, otherwise the smali sources.
+ if ctx.jvm:
+ ctx.default_build(use_smali=False)
+ else:
+ ctx.default_build(use_jasmin=False)
diff --git a/test/167-visit-locks/run b/test/167-visit-locks/run
deleted file mode 100644
index 9365411..0000000
--- a/test/167-visit-locks/run
+++ /dev/null
@@ -1,18 +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.
-
-# Use a smaller heap so it's easier to potentially fill up.
-exec ${RUN} $@ --runtime-option -Xmx2m
diff --git a/test/167-visit-locks/run.py b/test/167-visit-locks/run.py
new file mode 100644
index 0000000..266f747
--- /dev/null
+++ b/test/167-visit-locks/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to potentially fill up.
+ ctx.default_run(args, runtime_option=["-Xmx2m"])
diff --git a/test/168-vmstack-annotated/run b/test/168-vmstack-annotated/run
deleted file mode 100644
index 9365411..0000000
--- a/test/168-vmstack-annotated/run
+++ /dev/null
@@ -1,18 +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.
-
-# Use a smaller heap so it's easier to potentially fill up.
-exec ${RUN} $@ --runtime-option -Xmx2m
diff --git a/test/168-vmstack-annotated/run.py b/test/168-vmstack-annotated/run.py
new file mode 100644
index 0000000..266f747
--- /dev/null
+++ b/test/168-vmstack-annotated/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to potentially fill up.
+ ctx.default_run(args, runtime_option=["-Xmx2m"])
diff --git a/test/168-vmstack-annotated/src/Main.java b/test/168-vmstack-annotated/src/Main.java
index 8234f94..5056c42 100644
--- a/test/168-vmstack-annotated/src/Main.java
+++ b/test/168-vmstack-annotated/src/Main.java
@@ -22,6 +22,7 @@
import java.util.Map;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ForkJoinPool;
public class Main {
@@ -89,6 +90,9 @@
}
public static void main(String[] args) throws Exception {
+ // Eagerly initialize ForkJoinPool as we've seen the class initialization code interfere
+ // with the logic of the test when waiting for threads to be non-runnable.
+ Class.forName(ForkJoinPool.class.getName(), true, ForkJoinPool.class.getClassLoader());
try {
testCluster1();
} catch (Exception e) {
diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check
deleted file mode 100755
index 228bcb5..0000000
--- a/test/172-app-image-twice/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Remove all lines not containing "passed".
-grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run
deleted file mode 100644
index 2987b4b..0000000
--- a/test/172-app-image-twice/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Build an app image with TestClass (specified by profile).
-
-${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/172-app-image-twice/run.py b/test/172-app-image-twice/run.py
new file mode 100644
index 0000000..6b53067
--- /dev/null
+++ b/test/172-app-image-twice/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Build an app image with TestClass (specified by profile).
+
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
+
+ # Remove all lines not containing "passed".
+ ctx.run(fr"sed -i '/^passed/!d' '{args.stdout_file}'")
diff --git a/test/173-missing-field-type/smali/BadField.smali b/test/173-missing-field-type/smali/BadField.smali
index 7593983..1734034 100644
--- a/test/173-missing-field-type/smali/BadField.smali
+++ b/test/173-missing-field-type/smali/BadField.smali
@@ -41,6 +41,18 @@
return-void
.end method
+.method public static storeStatic(Ljava/lang/Object;)V
+ .registers 1
+ sput-object p0, LBadField;->widget:LWidget;
+ return-void
+.end method
+
+.method public static storeInstance(LBadField;Ljava/lang/Object;)V
+ .registers 2
+ iput-object p1, p0, LBadField;->iwidget:LWidget;
+ return-void
+.end method
+
.method public static storeInstanceObject()V
.registers 2
new-instance v1, LBadField;
diff --git a/test/173-missing-field-type/src-art/Main.java b/test/173-missing-field-type/src-art/Main.java
index 35dbbea..8c5a741 100644
--- a/test/173-missing-field-type/src-art/Main.java
+++ b/test/173-missing-field-type/src-art/Main.java
@@ -15,6 +15,7 @@
*/
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Throwable {
@@ -24,10 +25,16 @@
// Storing null is OK.
c.getMethod("storeStaticNull").invoke(null);
c.getMethod("storeInstanceNull").invoke(null);
+ c.getMethod("storeStatic", Object.class).invoke(null, new Object[]{ null });
+ c.getMethod("storeInstance", c, Object.class).invoke(
+ null, new Object[]{ c.newInstance(), null });
// Storing anything else should throw an exception.
- testStoreObject(c, "storeStaticObject");
- testStoreObject(c, "storeInstanceObject");
+ testStoreObject(c.getMethod("storeStaticObject"));
+ testStoreObject(c.getMethod("storeInstanceObject"));
+ testStoreObject(c.getMethod("storeStatic", Object.class), new Object());
+ testStoreObject(
+ c.getMethod("storeInstance", c, Object.class), c.newInstance(), new Object());
// Loading is OK.
c = Class.forName("BadFieldGet");
@@ -38,9 +45,9 @@
c.getMethod(methodName).invoke(null);
}
- public static void testStoreObject(Class<?> c, String methodName) throws Throwable {
+ public static void testStoreObject(Method method, Object... arguments) throws Throwable {
try {
- c.getMethod(methodName).invoke(null);
+ method.invoke(null, arguments);
throw new Error("Expected NoClassDefFoundError");
} catch (InvocationTargetException expected) {
Throwable e = expected.getCause();
diff --git a/test/176-app-image-string/run b/test/176-app-image-string/run
deleted file mode 100644
index 52d2b5f..0000000
--- a/test/176-app-image-string/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-exec ${RUN} $@ --profile
diff --git a/test/176-app-image-string/run.py b/test/176-app-image-string/run.py
new file mode 100644
index 0000000..0f3a0ea
--- /dev/null
+++ b/test/176-app-image-string/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, profile=True)
diff --git a/test/178-app-image-native-method/build.py b/test/178-app-image-native-method/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/178-app-image-native-method/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/178-app-image-native-method/check b/test/178-app-image-native-method/check
deleted file mode 100755
index 45d3654..0000000
--- a/test/178-app-image-native-method/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Filter out error messages for missing native methods.
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && grep -v 'No implementation found for ' "$4" | diff -q "$3" - >/dev/null
diff --git a/test/178-app-image-native-method/run b/test/178-app-image-native-method/run
deleted file mode 100644
index 7cd0d57..0000000
--- a/test/178-app-image-native-method/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-# Use a profile to put specific classes in the app image. Increase the large
-# method threshold to compile Main.$noinline$opt$testCriticalSignatures().
-${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
- -Xcompiler-option --large-method-max=2000
-return_status1=$?
-
-# Also run with the verify filter to avoid compiling JNI stubs.
-${RUN} ${@} --profile -Xcompiler-option --compiler-filter=verify
-return_status2=$?
-
-(exit ${return_status1}) && (exit ${return_status2})
diff --git a/test/178-app-image-native-method/run.py b/test/178-app-image-native-method/run.py
new file mode 100644
index 0000000..5f8c507
--- /dev/null
+++ b/test/178-app-image-native-method/run.py
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image. Increase the large
+ # method threshold to compile Main.$noinline$opt$testCriticalSignatures().
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=[
+ "--compiler-filter=speed-profile", "--large-method-max=2000"
+ ])
+
+ # Also run with the verify filter to avoid compiling JNI stubs.
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=verify"])
+
+ # Filter out error messages for missing native methods.
+ ctx.run(fr"sed -i '/No implementation found for/d' '{args.stderr_file}'")
diff --git a/test/178-app-image-native-method/src/Main.java b/test/178-app-image-native-method/src/Main.java
index 294ad47..877efa4 100644
--- a/test/178-app-image-native-method/src/Main.java
+++ b/test/178-app-image-native-method/src/Main.java
@@ -17,6 +17,8 @@
import dalvik.annotation.optimization.FastNative;
import dalvik.annotation.optimization.CriticalNative;
+// clang-format off
+
public class Main {
public static void main(String[] args) throws Exception {
diff --git a/test/178-app-image-native-method/test-metadata.json b/test/178-app-image-native-method/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/178-app-image-native-method/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/180-native-default-method/build b/test/180-native-default-method/build
deleted file mode 100644
index b6b604f..0000000
--- a/test/180-native-default-method/build
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@"
-
-if [[ $@ != *"--jvm"* ]]; then
- # Change the generated dex file to have a v35 magic number if it is version 38
- if test -f classes.dex && head -c 7 classes.dex | grep -q 038; then
- # place ascii value '035' into the classes.dex file starting at byte 4.
- printf '035' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
- rm -f $TEST_NAME.jar
- ${SOONG_ZIP} -o $TEST_NAME.jar -f classes.dex
- else
- echo Unexpected dex verison
- exit 1
- fi
-fi
diff --git a/test/180-native-default-method/build.py b/test/180-native-default-method/build.py
new file mode 100644
index 0000000..62aac82
--- /dev/null
+++ b/test/180-native-default-method/build.py
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2022 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.
+
+def build(ctx):
+ ctx.default_build()
+ if ctx.jvm:
+ return
+ # Change the generated dex file to have a v35 magic number if it is version 38
+ with open(ctx.test_dir / "classes.dex", "rb+") as f:
+ assert f.read(8) == b"dex\n038\x00"
+ f.seek(0)
+ f.write(b"dex\n035\x00")
+ (ctx.test_dir / "180-native-default-method.jar").unlink()
+ ctx.soong_zip([
+ "-o", ctx.test_dir / "180-native-default-method.jar", "-j",
+ "-f", ctx.test_dir / "classes.dex"
+ ])
diff --git a/test/181-default-methods/build b/test/181-default-methods/build
deleted file mode 100644
index 9cd5738..0000000
--- a/test/181-default-methods/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/181-default-methods/build.py b/test/181-default-methods/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/181-default-methods/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/182-method-linking/Android.bp b/test/182-method-linking/Android.bp
new file mode 100644
index 0000000..cee24a9
--- /dev/null
+++ b/test/182-method-linking/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `182-method-linking`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-182-method-linking-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-182-method-linking",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-182-method-linking-src"
+ ],
+ data: [
+ ":art-run-test-182-method-linking-expected-stdout",
+ ":art-run-test-182-method-linking-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-182-method-linking-expected-stdout",
+ out: ["art-run-test-182-method-linking-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-182-method-linking-expected-stderr",
+ out: ["art-run-test-182-method-linking-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/182-method-linking/src/Main.java b/test/182-method-linking/src/Main.java
index 3902956..638ff65 100644
--- a/test/182-method-linking/src/Main.java
+++ b/test/182-method-linking/src/Main.java
@@ -33,12 +33,6 @@
public class Main {
public static void main(String args[]) {
- try {
- Class.forName("dalvik.system.PathClassLoader");
- } catch (ClassNotFoundException e) {
- usingRI = true;
- }
-
// A single method signature can result in multiple vtable entries
// when package-private methods from different packages are involved.
// All classes here define the method `void foo()` but classes
@@ -120,7 +114,6 @@
CXI1 cxi1 = new CXI1();
I1.callI1Foo(cxi1);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg1.I1.foo on pkg1.CXI1");
System.out.println("Caught IllegalAccessError");
}
@@ -128,7 +121,6 @@
CXI2 cxi2 = new CXI2();
I2.callI2Foo(cxi2);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg2.I2.foo on pkg1.CXI2");
System.out.println("Caught IllegalAccessError");
}
@@ -136,7 +128,6 @@
DXI1 dxi1 = new DXI1();
I1.callI1Foo(dxi1);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg1.I1.foo on pkg2.DXI1");
System.out.println("Caught IllegalAccessError");
}
@@ -144,17 +135,7 @@
DXI2 dxi2 = new DXI2();
I2.callI2Foo(dxi2);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg2.I2.foo on pkg2.DXI2");
System.out.println("Caught IllegalAccessError");
}
}
-
- private static void printOnDalvik(String line) {
- if (!usingRI) {
- // FIXME: Delay IAE until calling the method. Bug: 211854716
- System.out.println(line);
- }
- }
-
- private static boolean usingRI = false;
}
diff --git a/test/1900-track-alloc/run b/test/1900-track-alloc/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1900-track-alloc/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1900-track-alloc/run.py b/test/1900-track-alloc/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1900-track-alloc/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1901-get-bytecodes/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/run.py b/test/1901-get-bytecodes/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1901-get-bytecodes/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1902-suspend/run b/test/1902-suspend/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1902-suspend/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1902-suspend/run.py b/test/1902-suspend/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1902-suspend/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1903-suspend-self/run b/test/1903-suspend-self/run
deleted file mode 100755
index 97f077b..0000000
--- a/test/1903-suspend-self/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
-
-return_status1=$?
-
-#Also do one run with --no-image to have one test coverage with no-image.
-./default-run "$@" --jvmti --no-image
-
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/1903-suspend-self/run.py b/test/1903-suspend-self/run.py
new file mode 100644
index 0000000..6f8d830
--- /dev/null
+++ b/test/1903-suspend-self/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ #Also do one run with --no-image to have one test coverage with no-image.
+ ctx.default_run(args, jvmti=True, image=False)
diff --git a/test/1904-double-suspend/run b/test/1904-double-suspend/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1904-double-suspend/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1904-double-suspend/run.py b/test/1904-double-suspend/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1904-double-suspend/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1905-suspend-native/run b/test/1905-suspend-native/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1905-suspend-native/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1905-suspend-native/run.py b/test/1905-suspend-native/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1905-suspend-native/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1906-suspend-list-me-first/run b/test/1906-suspend-list-me-first/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1906-suspend-list-me-first/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1906-suspend-list-me-first/run.py b/test/1906-suspend-list-me-first/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1906-suspend-list-me-first/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1907-suspend-list-self-twice/run b/test/1907-suspend-list-self-twice/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1907-suspend-list-self-twice/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1907-suspend-list-self-twice/run.py b/test/1907-suspend-list-self-twice/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1908-suspend-native-resume-self/run b/test/1908-suspend-native-resume-self/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1908-suspend-native-resume-self/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1908-suspend-native-resume-self/run.py b/test/1908-suspend-native-resume-self/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1909-per-agent-tls/run b/test/1909-per-agent-tls/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1909-per-agent-tls/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1909-per-agent-tls/run.py b/test/1909-per-agent-tls/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1909-per-agent-tls/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1910-transform-with-default/run b/test/1910-transform-with-default/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1910-transform-with-default/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1910-transform-with-default/run.py b/test/1910-transform-with-default/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1910-transform-with-default/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1911-get-local-var-table/run b/test/1911-get-local-var-table/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1911-get-local-var-table/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1911-get-local-var-table/run.py b/test/1911-get-local-var-table/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1911-get-local-var-table/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1912-get-set-local-primitive/run b/test/1912-get-set-local-primitive/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1912-get-set-local-primitive/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1912-get-set-local-primitive/run.py b/test/1912-get-set-local-primitive/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1912-get-set-local-primitive/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1913-get-set-local-objects/run b/test/1913-get-set-local-objects/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1913-get-set-local-objects/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1913-get-set-local-objects/run.py b/test/1913-get-set-local-objects/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1913-get-set-local-objects/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1914-get-local-instance/run b/test/1914-get-local-instance/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1914-get-local-instance/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1914-get-local-instance/run.py b/test/1914-get-local-instance/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1914-get-local-instance/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1915-get-set-local-current-thread/run b/test/1915-get-set-local-current-thread/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1915-get-set-local-current-thread/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1915-get-set-local-current-thread/run.py b/test/1915-get-set-local-current-thread/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1915-get-set-local-current-thread/src/Main.java b/test/1915-get-set-local-current-thread/src/Main.java
index 47e6767..5ca6f09 100644
--- a/test/1915-get-set-local-current-thread/src/Main.java
+++ b/test/1915-get-set-local-current-thread/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1915.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1915.run();
+ }
}
diff --git a/test/1915-get-set-local-current-thread/src/art/Test1915.java b/test/1915-get-set-local-current-thread/src/art/Test1915.java
index a99a487..8398afa 100644
--- a/test/1915-get-set-local-current-thread/src/art/Test1915.java
+++ b/test/1915-get-set-local-current-thread/src/art/Test1915.java
@@ -31,75 +31,75 @@
import java.util.function.Consumer;
public class Test1915 {
- public static final int SET_VALUE = 1337;
- public static final String TARGET_VAR = "TARGET";
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
- public static void reportValue(Object val) {
- System.out.println("\tValue is '" + val + "'");
- }
- public static interface ThrowRunnable {
- public void run() throws Exception;
- }
-
- public static void IntMethod(ThrowRunnable safepoint) throws Exception {
- int TARGET = 42;
- safepoint.run();
- reportValue(TARGET);
- }
-
- public static void run() throws Exception {
- Locals.EnableLocalVariableAccess();
- final Method target = Test1915.class.getDeclaredMethod("IntMethod", ThrowRunnable.class);
- // Get Variable.
- System.out.println("GetLocalInt on current thread!");
- IntMethod(() -> {
- StackTrace.StackFrameData frame = FindStackFrame(target);
- int depth = FindExpectedFrameDepth(frame);
- int slot = FindSlot(frame);
- int value = Locals.GetLocalVariableInt(Thread.currentThread(), depth, slot);
- System.out.println("From GetLocalInt(), value is " + value);
- });
- // Set Variable.
- System.out.println("SetLocalInt on current thread!");
- IntMethod(() -> {
- StackTrace.StackFrameData frame = FindStackFrame(target);
- int depth = FindExpectedFrameDepth(frame);
- int slot = FindSlot(frame);
- Locals.SetLocalVariableInt(Thread.currentThread(), depth, slot, SET_VALUE);
- });
- }
-
- public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
- long loc = frame.current_location;
- for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
- if (var.start_location <= loc &&
- var.length + var.start_location > loc &&
- var.name.equals(TARGET_VAR)) {
- return var.slot;
- }
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
}
- throw new Error(
- "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
- }
-
- public static int FindExpectedFrameDepth(StackTrace.StackFrameData frame) throws Exception {
- // Adjust the 'frame' depth since it is modified by:
- // +1 for Get/SetLocalVariableInt in future.
- // -1 for FindStackFrame
- // -1 for GetStackTrace
- // -1 for GetStackTraceNative
- // ------------------------------
- // -2
- return frame.depth - 2;
- }
-
- private static StackTrace.StackFrameData FindStackFrame(Method target) {
- for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
- if (frame.method.equals(target)) {
- return frame;
- }
+ public static interface ThrowRunnable {
+ public void run() throws Exception;
}
- throw new Error("Unable to find stack frame in method " + target);
- }
+
+ public static void IntMethod(ThrowRunnable safepoint) throws Exception {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final Method target = Test1915.class.getDeclaredMethod("IntMethod", ThrowRunnable.class);
+ // Get Variable.
+ System.out.println("GetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(Thread.currentThread(), depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ });
+ // Set Variable.
+ System.out.println("SetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ Locals.SetLocalVariableInt(Thread.currentThread(), depth, slot, SET_VALUE);
+ });
+ }
+
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+ }
+
+ public static int FindExpectedFrameDepth(StackTrace.StackFrameData frame) throws Exception {
+ // Adjust the 'frame' depth since it is modified by:
+ // +1 for Get/SetLocalVariableInt in future.
+ // -1 for FindStackFrame
+ // -1 for GetStackTrace
+ // -1 for GetStackTraceNative
+ // ------------------------------
+ // -2
+ return frame.depth - 2;
+ }
+
+ private static StackTrace.StackFrameData FindStackFrame(Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target);
+ }
}
diff --git a/test/1916-get-set-current-frame/run b/test/1916-get-set-current-frame/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1916-get-set-current-frame/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1916-get-set-current-frame/run.py b/test/1916-get-set-current-frame/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1916-get-set-current-frame/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1916-get-set-current-frame/src/Main.java b/test/1916-get-set-current-frame/src/Main.java
index 7d0cd21..2ecf0ca 100644
--- a/test/1916-get-set-current-frame/src/Main.java
+++ b/test/1916-get-set-current-frame/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1916.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1916.run();
+ }
}
diff --git a/test/1916-get-set-current-frame/src/art/Test1916.java b/test/1916-get-set-current-frame/src/art/Test1916.java
index 3e5bce2..6fe4ba2 100644
--- a/test/1916-get-set-current-frame/src/art/Test1916.java
+++ b/test/1916-get-set-current-frame/src/art/Test1916.java
@@ -31,118 +31,118 @@
import java.util.function.Consumer;
public class Test1916 {
- public static final int SET_VALUE = 1337;
- public static final String TARGET_VAR = "TARGET";
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
- public static void reportValue(Object val) {
- System.out.println("\tValue is '" + val + "'");
- }
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
+ }
- public static class IntRunner implements Runnable {
- private volatile boolean continueBusyLoop;
- private volatile boolean inBusyLoop;
- public IntRunner() {
- this.continueBusyLoop = true;
- this.inBusyLoop = false;
+ public static class IntRunner implements Runnable {
+ private volatile boolean continueBusyLoop;
+ private volatile boolean inBusyLoop;
+ public IntRunner() {
+ this.continueBusyLoop = true;
+ this.inBusyLoop = false;
+ }
+ public void run() {
+ int TARGET = 42;
+ // We will suspend the thread during this loop.
+ while (continueBusyLoop) {
+ inBusyLoop = true;
+ }
+ reportValue(TARGET);
+ }
+ public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
+ public void finish() { continueBusyLoop = false; }
}
- public void run() {
- int TARGET = 42;
- // We will suspend the thread during this loop.
- while (continueBusyLoop) {
- inBusyLoop = true;
- }
- reportValue(TARGET);
- }
- public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
- public void finish() { continueBusyLoop = false; }
- }
- public static void run() throws Exception {
- Locals.EnableLocalVariableAccess();
- runGet();
- runSet();
- }
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ runGet();
+ runSet();
+ }
- public static void runGet() throws Exception {
- Method target = IntRunner.class.getDeclaredMethod("run");
- // Get Int
- IntRunner int_runner = new IntRunner();
- Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
- target_get.start();
- int_runner.waitForBusyLoopStart();
- try {
- Suspension.suspend(target_get);
- } catch (Exception e) {
- System.out.println("FAIL: got " + e);
- e.printStackTrace();
- int_runner.finish();
- target_get.join();
- return;
+ public static void runGet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Get Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
+ target_get.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_get);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_get.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(target_get, depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ } finally {
+ Suspension.resume(target_get);
+ int_runner.finish();
+ target_get.join();
+ }
}
- try {
- StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
- int depth = frame.depth;
- if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
- int slot = FindSlot(frame);
- int value = Locals.GetLocalVariableInt(target_get, depth, slot);
- System.out.println("From GetLocalInt(), value is " + value);
- } finally {
- Suspension.resume(target_get);
- int_runner.finish();
- target_get.join();
- }
- }
- public static void runSet() throws Exception {
- Method target = IntRunner.class.getDeclaredMethod("run");
- // Set Int
- IntRunner int_runner = new IntRunner();
- Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
- target_set.start();
- int_runner.waitForBusyLoopStart();
- try {
- Suspension.suspend(target_set);
- } catch (Exception e) {
- System.out.println("FAIL: got " + e);
- e.printStackTrace();
- int_runner.finish();
- target_set.join();
- return;
+ public static void runSet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Set Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
+ target_set.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_set);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_set.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ System.out.println("Setting TARGET to " + SET_VALUE);
+ Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
+ } finally {
+ Suspension.resume(target_set);
+ int_runner.finish();
+ target_set.join();
+ }
}
- try {
- StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
- int depth = frame.depth;
- if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
- int slot = FindSlot(frame);
- System.out.println("Setting TARGET to " + SET_VALUE);
- Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
- } finally {
- Suspension.resume(target_set);
- int_runner.finish();
- target_set.join();
- }
- }
- public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
- long loc = frame.current_location;
- for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
- if (var.start_location <= loc &&
- var.length + var.start_location > loc &&
- var.name.equals(TARGET_VAR)) {
- return var.slot;
- }
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
}
- throw new Error(
- "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
- }
- private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
- for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
- if (frame.method.equals(target)) {
- return frame;
- }
+ private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
}
- throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
- }
}
diff --git a/test/1917-get-stack-frame/run b/test/1917-get-stack-frame/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1917-get-stack-frame/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1917-get-stack-frame/run.py b/test/1917-get-stack-frame/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1917-get-stack-frame/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1917-get-stack-frame/src/Main.java b/test/1917-get-stack-frame/src/Main.java
index c055a5c..c6f0853 100644
--- a/test/1917-get-stack-frame/src/Main.java
+++ b/test/1917-get-stack-frame/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1917.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1917.run();
+ }
}
diff --git a/test/1917-get-stack-frame/src/art/Test1917.java b/test/1917-get-stack-frame/src/art/Test1917.java
index 75af43b..47b06cf 100644
--- a/test/1917-get-stack-frame/src/art/Test1917.java
+++ b/test/1917-get-stack-frame/src/art/Test1917.java
@@ -34,124 +34,124 @@
import java.util.function.Consumer;
public class Test1917 {
- public final static boolean TEST_PRINT_ALL = false;
+ public final static boolean TEST_PRINT_ALL = false;
- public static class ThreadPauser implements Runnable {
- public Semaphore sem_wakeup_main = new Semaphore(0);
- public Semaphore sem_wait = new Semaphore(0);
+ public static class ThreadPauser implements Runnable {
+ public Semaphore sem_wakeup_main = new Semaphore(0);
+ public Semaphore sem_wait = new Semaphore(0);
- public void run() {
- try {
- sem_wakeup_main.release();
- sem_wait.acquire();
- } catch (Exception e) {
- throw new Error("Error with semaphores!", e);
- }
- }
-
- public void waitForOtherThreadToPause() throws Exception {
- sem_wakeup_main.acquire();
- while (!sem_wait.hasQueuedThreads()) {}
- }
-
- public void wakeupOtherThread() throws Exception {
- sem_wait.release();
- }
- }
-
- public static class StackTraceGenerator implements Runnable {
- private final Thread thr;
- private final Consumer<StackTrace.StackFrameData> con;
- public StackTraceGenerator(Thread thr, Consumer<StackTrace.StackFrameData> con) {
- this.thr = thr;
- this.con = con;
- }
-
- public StackTraceGenerator(Consumer<StackTrace.StackFrameData> con) {
- this(null, con);
- }
-
- public Thread getThread() {
- if (thr == null) {
- return Thread.currentThread();
- } else {
- return thr;
- }
- }
- public void run() {
- for (StackTrace.StackFrameData s : StackTrace.GetStackTrace(getThread())) {
- con.accept(s);
- }
- }
- }
-
- public static class RecurCount implements Runnable {
- private final int cnt;
- private final Runnable then;
- public RecurCount(int cnt, Runnable then) {
- this.cnt = cnt;
- this.then = then;
- }
-
- public void run() {
- doRecur(0);
- }
-
- public void doRecur(int n) {
- if (n < cnt) {
- doRecur(n + 1);
- } else {
- then.run();
- }
- }
- }
-
- public static Consumer<StackTrace.StackFrameData> makePrintStackFramesConsumer()
- throws Exception {
- final Method end_method = Test1917.class.getDeclaredMethod("run");
- return new Consumer<StackTrace.StackFrameData>() {
- public void accept(StackTrace.StackFrameData data) {
- if (TEST_PRINT_ALL) {
- System.out.println(data);
- } else {
- Package p = data.method.getDeclaringClass().getPackage();
- // Filter out anything to do with the testing harness.
- if (p != null && p.equals(Test1917.class.getPackage())) {
- System.out.printf("'%s' line: %d\n",
- data.method,
- Breakpoint.locationToLine(data.method, data.current_location));
- } else if (data.method.getDeclaringClass().equals(Semaphore.class)) {
- System.out.printf("'%s' line: <NOT-DETERMINISTIC>\n", data.method);
- }
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
}
- }
- };
- }
- public static void run() throws Exception {
- System.out.println("Recurring 5 times");
- new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())).run();
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ while (!sem_wait.hasQueuedThreads()) {}
+ }
- System.out.println("Recurring 5 times on another thread");
- Thread thr = new Thread(
- Thread.currentThread().getThreadGroup(),
- new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())),
- "Recurring Thread 1",
- 10*1000000 /* 10 mb*/);
- thr.start();
- thr.join();
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
- System.out.println("Recurring 5 times on another thread. Stack trace from main thread!");
- ThreadPauser pause = new ThreadPauser();
- Thread thr2 = new Thread(
- Thread.currentThread().getThreadGroup(),
- new RecurCount(5, pause),
- "Recurring Thread 2",
- 10*1000000 /* 10 mb*/);
- thr2.start();
- pause.waitForOtherThreadToPause();
- new StackTraceGenerator(thr2, makePrintStackFramesConsumer()).run();
- pause.wakeupOtherThread();
- thr2.join();
- }
+ public static class StackTraceGenerator implements Runnable {
+ private final Thread thr;
+ private final Consumer<StackTrace.StackFrameData> con;
+ public StackTraceGenerator(Thread thr, Consumer<StackTrace.StackFrameData> con) {
+ this.thr = thr;
+ this.con = con;
+ }
+
+ public StackTraceGenerator(Consumer<StackTrace.StackFrameData> con) {
+ this(null, con);
+ }
+
+ public Thread getThread() {
+ if (thr == null) {
+ return Thread.currentThread();
+ } else {
+ return thr;
+ }
+ }
+ public void run() {
+ for (StackTrace.StackFrameData s : StackTrace.GetStackTrace(getThread())) {
+ con.accept(s);
+ }
+ }
+ }
+
+ public static class RecurCount implements Runnable {
+ private final int cnt;
+ private final Runnable then;
+ public RecurCount(int cnt, Runnable then) {
+ this.cnt = cnt;
+ this.then = then;
+ }
+
+ public void run() {
+ doRecur(0);
+ }
+
+ public void doRecur(int n) {
+ if (n < cnt) {
+ doRecur(n + 1);
+ } else {
+ then.run();
+ }
+ }
+ }
+
+ public static Consumer<StackTrace.StackFrameData> makePrintStackFramesConsumer()
+ throws Exception {
+ final Method end_method = Test1917.class.getDeclaredMethod("run");
+ return new Consumer<StackTrace.StackFrameData>() {
+ public void accept(StackTrace.StackFrameData data) {
+ if (TEST_PRINT_ALL) {
+ System.out.println(data);
+ } else {
+ Package p = data.method.getDeclaringClass().getPackage();
+ // Filter out anything to do with the testing harness.
+ if (p != null && p.equals(Test1917.class.getPackage())) {
+ System.out.printf("'%s' line: %d\n",
+ data.method,
+ Breakpoint.locationToLine(data.method, data.current_location));
+ } else if (data.method.getDeclaringClass().equals(Semaphore.class)) {
+ System.out.printf("'%s' line: <NOT-DETERMINISTIC>\n", data.method);
+ }
+ }
+ }
+ };
+ }
+
+ public static void run() throws Exception {
+ System.out.println("Recurring 5 times");
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())).run();
+
+ System.out.println("Recurring 5 times on another thread");
+ Thread thr = new Thread(
+ Thread.currentThread().getThreadGroup(),
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())),
+ "Recurring Thread 1",
+ 10*1000000 /* 10 mb*/);
+ thr.start();
+ thr.join();
+
+ System.out.println("Recurring 5 times on another thread. Stack trace from main thread!");
+ ThreadPauser pause = new ThreadPauser();
+ Thread thr2 = new Thread(
+ Thread.currentThread().getThreadGroup(),
+ new RecurCount(5, pause),
+ "Recurring Thread 2",
+ 10*1000000 /* 10 mb*/);
+ thr2.start();
+ pause.waitForOtherThreadToPause();
+ new StackTraceGenerator(thr2, makePrintStackFramesConsumer()).run();
+ pause.wakeupOtherThread();
+ thr2.join();
+ }
}
diff --git a/test/1919-vminit-thread-start-timing/run b/test/1919-vminit-thread-start-timing/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1919-vminit-thread-start-timing/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1919-vminit-thread-start-timing/run.py b/test/1919-vminit-thread-start-timing/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1919-vminit-thread-start-timing/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1919-vminit-thread-start-timing/src/Main.java b/test/1919-vminit-thread-start-timing/src/Main.java
index 41baf24..32a45c5 100644
--- a/test/1919-vminit-thread-start-timing/src/Main.java
+++ b/test/1919-vminit-thread-start-timing/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1919.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1919.run();
+ }
}
diff --git a/test/1919-vminit-thread-start-timing/src/art/Test1919.java b/test/1919-vminit-thread-start-timing/src/art/Test1919.java
index 26ff49f..f146ae6 100644
--- a/test/1919-vminit-thread-start-timing/src/art/Test1919.java
+++ b/test/1919-vminit-thread-start-timing/src/art/Test1919.java
@@ -17,49 +17,49 @@
package art;
public class Test1919 {
- public static final boolean PRINT_ALL_THREADS = false;
+ public static final boolean PRINT_ALL_THREADS = false;
- public static void run() throws Exception {
- Thread testing_thread = getTestingThread();
- // TODO(b/124284724): For unknown reasons the testing thread will sometimes SEGV after the test
- // has otherwise completed successfully. This has only been observed on the release version of
- // art (libart.so) and I haven't had any luck reproing it. I assume it has something to do with
- // racing between the DetachCurrentThread and shutdown but I'm not sure. Since the runtime
- // normally never shuts down anyway for now I'll just ensure everything gets cleaned up early to
- // prevent the problem from showing up.
- testing_thread.join();
- for (Event e : getEvents()) {
- if (e.thr != null) {
- if (PRINT_ALL_THREADS ||
- e.thr.equals(Thread.currentThread()) ||
- e.thr.equals(testing_thread)) {
- System.out.println(e.name + ": " + e.thr.getName());
+ public static void run() throws Exception {
+ Thread testing_thread = getTestingThread();
+ // TODO(b/124284724): For unknown reasons the testing thread will sometimes SEGV after the
+ // test has otherwise completed successfully. This has only been observed on the release
+ // version of art (libart.so) and I haven't had any luck reproing it. I assume it has
+ // something to do with racing between the DetachCurrentThread and shutdown but I'm not
+ // sure. Since the runtime normally never shuts down anyway for now I'll just ensure
+ // everything gets cleaned up early to prevent the problem from showing up.
+ testing_thread.join();
+ for (Event e : getEvents()) {
+ if (e.thr != null) {
+ if (PRINT_ALL_THREADS ||
+ e.thr.equals(Thread.currentThread()) ||
+ e.thr.equals(testing_thread)) {
+ System.out.println(e.name + ": " + e.thr.getName());
+ }
+ }
}
- }
}
- }
- static class Event {
- public final String name;
- public final Thread thr;
- public Event(String name, Thread thr) {
- this.name = name;
- this.thr = thr;
+ static class Event {
+ public final String name;
+ public final Thread thr;
+ public Event(String name, Thread thr) {
+ this.name = name;
+ this.thr = thr;
+ }
}
- }
- public static Event[] getEvents() {
- String[] ns = getEventNames();
- Thread[] ts = getEventThreads();
- Event[] es = new Event[Math.min(ns.length, ts.length)];
- for (int i = 0; i < es.length; i++) {
- es[i] = new Event(ns[i], ts[i]);
+ public static Event[] getEvents() {
+ String[] ns = getEventNames();
+ Thread[] ts = getEventThreads();
+ Event[] es = new Event[Math.min(ns.length, ts.length)];
+ for (int i = 0; i < es.length; i++) {
+ es[i] = new Event(ns[i], ts[i]);
+ }
+ return es;
}
- return es;
- }
- public static native String[] getEventNames();
- public static native Thread[] getEventThreads();
+ public static native String[] getEventNames();
+ public static native Thread[] getEventThreads();
- public static native Thread getTestingThread();
+ public static native Thread getTestingThread();
}
diff --git a/test/1920-suspend-native-monitor/run b/test/1920-suspend-native-monitor/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1920-suspend-native-monitor/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1920-suspend-native-monitor/run.py b/test/1920-suspend-native-monitor/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1920-suspend-native-monitor/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1921-suspend-native-recursive-monitor/run b/test/1921-suspend-native-recursive-monitor/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1921-suspend-native-recursive-monitor/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1921-suspend-native-recursive-monitor/run.py b/test/1921-suspend-native-recursive-monitor/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1921-suspend-native-recursive-monitor/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1922-owned-monitors-info/run b/test/1922-owned-monitors-info/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1922-owned-monitors-info/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1922-owned-monitors-info/run.py b/test/1922-owned-monitors-info/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1922-owned-monitors-info/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1923-frame-pop/run b/test/1923-frame-pop/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1923-frame-pop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1923-frame-pop/run.py b/test/1923-frame-pop/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1923-frame-pop/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1924-frame-pop-toggle/run b/test/1924-frame-pop-toggle/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1924-frame-pop-toggle/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1924-frame-pop-toggle/run.py b/test/1924-frame-pop-toggle/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1924-frame-pop-toggle/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1925-self-frame-pop/run b/test/1925-self-frame-pop/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1925-self-frame-pop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1925-self-frame-pop/run.py b/test/1925-self-frame-pop/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1925-self-frame-pop/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1926-missed-frame-pop/run b/test/1926-missed-frame-pop/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1926-missed-frame-pop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1926-missed-frame-pop/run.py b/test/1926-missed-frame-pop/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1926-missed-frame-pop/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1927-exception-event/run b/test/1927-exception-event/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1927-exception-event/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1927-exception-event/run.py b/test/1927-exception-event/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1927-exception-event/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1928-exception-event-exception/run b/test/1928-exception-event-exception/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1928-exception-event-exception/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1928-exception-event-exception/run.py b/test/1928-exception-event-exception/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1928-exception-event-exception/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1929-exception-catch-exception/run b/test/1929-exception-catch-exception/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1929-exception-catch-exception/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1929-exception-catch-exception/run.py b/test/1929-exception-catch-exception/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1929-exception-catch-exception/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1930-monitor-info/run b/test/1930-monitor-info/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1930-monitor-info/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1930-monitor-info/run.py b/test/1930-monitor-info/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1930-monitor-info/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1931-monitor-events/check b/test/1931-monitor-events/check
deleted file mode 100644
index 3f799cc..0000000
--- a/test/1931-monitor-events/check
+++ /dev/null
@@ -1,28 +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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Art sends events for park/unpark, and the RI doesn't. Remove it from the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1931-monitor-events/expected-stdout.jvm.txt b/test/1931-monitor-events/expected-stdout.jvm.txt
new file mode 100644
index 0000000..e26d459
--- /dev/null
+++ b/test/1931-monitor-events/expected-stdout.jvm.txt
@@ -0,0 +1,30 @@
+Testing contended locking.
+Locker thread 1 for NamedLock[Lock testLock] contended-LOCKING NamedLock[Lock testLock]
+Locker thread 1 for NamedLock[Lock testLock] LOCKED NamedLock[Lock testLock]
+Testing park.
+Testing monitor wait.
+Locker thread 2 for NamedLock[Lock testWait] start-monitor-wait NamedLock[Lock testWait] timeout: 0
+Locker thread 2 for NamedLock[Lock testWait] monitor-waited NamedLock[Lock testWait] timed_out: false
+Testing monitor timed wait.
+Locker thread 4 for NamedLock[Lock testTimedWait] start-monitor-wait NamedLock[Lock testTimedWait] timeout: 3600000
+Locker thread 4 for NamedLock[Lock testTimedWait] monitor-waited NamedLock[Lock testTimedWait] timed_out: false
+Testing monitor timed with timeout.
+Waiting for 10 seconds.
+Locker thread 6 for NamedLock[Lock testTimedWaitTimeout] start-monitor-wait NamedLock[Lock testTimedWaitTimeout] timeout: 10000
+Locker thread 6 for NamedLock[Lock testTimedWaitTimeout] monitor-waited NamedLock[Lock testTimedWaitTimeout] timed_out: true
+Wait finished with timeout.
+Waiting on an unlocked monitor.
+Unlocked wait thread: start-monitor-wait NamedLock[Lock testUnlockedWait] timeout: 0
+Caught exception: java.lang.reflect.InvocationTargetException
+ Caused by: class java.lang.IllegalMonitorStateException
+Waiting with an illegal argument (negative timeout)
+Locker thread 7 for NamedLock[Lock testIllegalWait] start-monitor-wait NamedLock[Lock testIllegalWait] timeout: -100
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: Got an error while performing action TIMED_WAIT
+ Caused by: class java.lang.IllegalArgumentException
+Interrupt a monitor being waited on.
+Locker thread 8 for NamedLock[Lock testInteruptWait] start-monitor-wait NamedLock[Lock testInteruptWait] timeout: 0
+Locker thread 8 for NamedLock[Lock testInteruptWait] monitor-waited NamedLock[Lock testInteruptWait] timed_out: false
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: Got an error while performing action WAIT
+ Caused by: class java.lang.InterruptedException
diff --git a/test/1931-monitor-events/jvm-expected.patch b/test/1931-monitor-events/jvm-expected.patch
deleted file mode 100644
index 7595b14..0000000
--- a/test/1931-monitor-events/jvm-expected.patch
+++ /dev/null
@@ -1,3 +0,0 @@
-5,6d4
-< ParkThread start-monitor-wait NamedLock[Parking blocker object] timeout: 1
-< ParkThread monitor-waited NamedLock[Parking blocker object] timed_out: true
diff --git a/test/1931-monitor-events/run b/test/1931-monitor-events/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1931-monitor-events/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1931-monitor-events/run.py b/test/1931-monitor-events/run.py
new file mode 100644
index 0000000..6c5d4ad
--- /dev/null
+++ b/test/1931-monitor-events/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # Art sends events for park/unpark, and the RI doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1932-monitor-events-misc/check b/test/1932-monitor-events-misc/check
deleted file mode 100644
index 0fe4429..0000000
--- a/test/1932-monitor-events-misc/check
+++ /dev/null
@@ -1,28 +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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI sends an extra event that art doesn't. Add it to the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1932-monitor-events-misc/expected-stdout.jvm.txt b/test/1932-monitor-events-misc/expected-stdout.jvm.txt
new file mode 100644
index 0000000..b2b051e
--- /dev/null
+++ b/test/1932-monitor-events-misc/expected-stdout.jvm.txt
@@ -0,0 +1,105 @@
+Testing contended locking where lock is released before callback ends.
+Locker thread 1 for NamedLock[Lock testLockUncontend] contended-LOCKING NamedLock[Lock testLockUncontend]
+Releasing NamedLock[Lock testLockUncontend] during monitorEnter event.
+Locker thread 1 for NamedLock[Lock testLockUncontend] LOCKED NamedLock[Lock testLockUncontend]
+Testing throwing exceptions in monitor_enter
+Locker thread 3 for NamedLock[Lock testLockThrowEnter] contended-LOCKING NamedLock[Lock testLockThrowEnter]
+Throwing exception in MonitorEnter
+Locker thread 3 for NamedLock[Lock testLockThrowEnter] LOCKED NamedLock[Lock testLockThrowEnter]
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorEnter of NamedLock[Lock testLockThrowEnter]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testLockThrowEnter], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exceptions in monitor_entered
+Locker thread 5 for NamedLock[Lock testLockThrowEntered] contended-LOCKING NamedLock[Lock testLockThrowEntered]
+Locker thread 5 for NamedLock[Lock testLockThrowEntered] LOCKED NamedLock[Lock testLockThrowEntered]
+Throwing exception in MonitorEntered
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorEntered of NamedLock[Lock testLockThrowEntered]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testLockThrowEntered], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exceptions in both monitorEnter & MonitorEntered
+Locker thread 7 for NamedLock[Lock testLockThrowBoth] contended-LOCKING NamedLock[Lock testLockThrowBoth]
+Throwing exception in MonitorEnter
+Locker thread 7 for NamedLock[Lock testLockThrowBoth] LOCKED NamedLock[Lock testLockThrowBoth]
+Throwing exception in MonitorEntered
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorEntered of NamedLock[Lock testLockThrowBoth]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testLockThrowBoth], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWait event
+Locker thread 8 for NamedLock[Lock testThrowWait] start-monitor-wait NamedLock[Lock testThrowWait] timeout: 0
+Throwing exception in MonitorWait
+Locker thread 8 for NamedLock[Lock testThrowWait] monitor-waited NamedLock[Lock testThrowWait] timed_out: false
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during MonitorWait of NamedLock[Lock testThrowWait]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWait event with illegal aruments
+Locker thread 9 for NamedLock[Lock testThrowIllegalWait] start-monitor-wait NamedLock[Lock testThrowIllegalWait] timeout: -100000
+Throwing exception in MonitorWait timeout = -100000
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWait of NamedLock[Lock testThrowIllegalWait]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowIllegalWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWaited event
+Locker thread 10 for NamedLock[Lock testThrowWaited] start-monitor-wait NamedLock[Lock testThrowWaited] timeout: 0
+Locker thread 10 for NamedLock[Lock testThrowWaited] monitor-waited NamedLock[Lock testThrowWaited] timed_out: false
+Throwing exception in MonitorWaited
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWaited of NamedLock[Lock testThrowWaited]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWaited], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWaited event caused by timeout
+Locker thread 12 for NamedLock[Lock testThrowWaitedTimeout] start-monitor-wait NamedLock[Lock testThrowWaitedTimeout] timeout: 5000
+Locker thread 12 for NamedLock[Lock testThrowWaitedTimeout] monitor-waited NamedLock[Lock testThrowWaitedTimeout] timed_out: true
+Throwing exception in MonitorWaited
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWaited of NamedLock[Lock testThrowWaitedTimeout]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWaitedTimeout], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWaited event caused by interrupt
+Locker thread 13 for NamedLock[Lock testThrowWaitedInterrupt] start-monitor-wait NamedLock[Lock testThrowWaitedInterrupt] timeout: 0
+Locker thread 13 for NamedLock[Lock testThrowWaitedInterrupt] monitor-waited NamedLock[Lock testThrowWaitedInterrupt] timed_out: false
+Throwing exception in MonitorWaited
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWaited of NamedLock[Lock testThrowWaitedInterrupt]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWaitedInterrupt], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing ObjectMonitorInfo inside of events
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] contended-LOCKING NamedLock[Lock testMonitorInfoInEvents]
+Monitor usage in MonitorEnter: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: Locker thread 14 for NamedLock[Lock testMonitorInfoInEvents], entryCount: 1, waiters: [], notify_waiters: [] }
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] LOCKED NamedLock[Lock testMonitorInfoInEvents]
+Monitor usage in MonitorEntered: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents], entryCount: 1, waiters: [], notify_waiters: [] }
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] start-monitor-wait NamedLock[Lock testMonitorInfoInEvents] timeout: 0
+Monitor usage in MonitorWait: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents], entryCount: 1, waiters: [], notify_waiters: [] }
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] monitor-waited NamedLock[Lock testMonitorInfoInEvents] timed_out: false
+Monitor usage in MonitorWaited: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing that the monitor can be stolen during the MonitorWaited event.
+Locker thread 17 for NamedLock[test testWaitEnterInterleaving] start-monitor-wait NamedLock[test testWaitEnterInterleaving] timeout: 0
+Locker thread 17 for NamedLock[test testWaitEnterInterleaving] monitor-waited NamedLock[test testWaitEnterInterleaving] timed_out: false
+locking controller3 in controller2 MonitorWaited event
+Controller3 now holds the lock the monitor wait will try to re-acquire
+Testing that we can lock and release the monitor in the MonitorWait event
+Locker thread 20 for NamedLock[test testWaitMonitorEnter] start-monitor-wait NamedLock[test testWaitMonitorEnter] timeout: 0
+In wait monitor usage: MonitorUsage{ monitor: NamedLock[test testWaitMonitorEnter], owner: Locker thread 20 for NamedLock[test testWaitMonitorEnter], entryCount: 1, waiters: [], notify_waiters: [] }
+In wait monitor usage sync: MonitorUsage{ monitor: NamedLock[test testWaitMonitorEnter], owner: Locker thread 20 for NamedLock[test testWaitMonitorEnter], entryCount: 2, waiters: [], notify_waiters: [] }
+Locker thread 20 for NamedLock[test testWaitMonitorEnter] monitor-waited NamedLock[test testWaitMonitorEnter] timed_out: false
+Testing that we can lock and release the monitor in the MonitorWaited event
+Locker thread 22 for NamedLock[test testWaitedMonitorEnter] start-monitor-wait NamedLock[test testWaitedMonitorEnter] timeout: 0
+Locker thread 22 for NamedLock[test testWaitedMonitorEnter] monitor-waited NamedLock[test testWaitedMonitorEnter] timed_out: false
+In waited monitor usage: MonitorUsage{ monitor: NamedLock[test testWaitedMonitorEnter], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+In waited monitor usage sync: MonitorUsage{ monitor: NamedLock[test testWaitedMonitorEnter], owner: Locker thread 22 for NamedLock[test testWaitedMonitorEnter], entryCount: 1, waiters: [], notify_waiters: [] }
+Testing we can perform recursive lock in MonitorEntered
+Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock] contended-LOCKING NamedLock[test testRecursiveMontiorEnteredLock]
+Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock] LOCKED NamedLock[test testRecursiveMontiorEnteredLock]
+In MonitorEntered usage: MonitorUsage{ monitor: NamedLock[test testRecursiveMontiorEnteredLock], owner: Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock], entryCount: 1, waiters: [], notify_waiters: [] }
+In MonitorEntered sync: MonitorUsage{ monitor: NamedLock[test testRecursiveMontiorEnteredLock], owner: Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock], entryCount: 2, waiters: [], notify_waiters: [] }
+Testing the lock state if MonitorEnter throws in a native method
+NativeLockStateThrowEnter thread contended-LOCKING NamedLock[test testNativeLockStateThrowEnter]
+Unlocking controller1 in MonitorEnter
+Throwing exception in MonitorEnter
+NativeLockStateThrowEnter thread LOCKED NamedLock[test testNativeLockStateThrowEnter]
+MonitorEnter returned: -1
+Lock state is: MonitorUsage{ monitor: NamedLock[test testNativeLockStateThrowEnter], owner: NativeLockStateThrowEnter thread, entryCount: 1, waiters: [], notify_waiters: [] }
+Caught exception: art.Monitors$TestException: throwing exception during monitorEnter of NamedLock[test testNativeLockStateThrowEnter]
+Testing the lock state if MonitorEntered throws in a native method
+NativeLockStateThrowEntered thread contended-LOCKING NamedLock[test testNativeLockStateThrowEntered]
+Unlocking controller1 in MonitorEnter
+NativeLockStateThrowEntered thread LOCKED NamedLock[test testNativeLockStateThrowEntered]
+Throwing exception in MonitorEntered
+MonitorEnter returned: -1
+Lock state is: MonitorUsage{ monitor: NamedLock[test testNativeLockStateThrowEntered], owner: NativeLockStateThrowEntered thread, entryCount: 1, waiters: [], notify_waiters: [] }
+Caught exception: art.Monitors$TestException: throwing exception during monitorEntered of NamedLock[test testNativeLockStateThrowEntered]
diff --git a/test/1932-monitor-events-misc/jvm-expected.patch b/test/1932-monitor-events-misc/jvm-expected.patch
deleted file mode 100644
index f6b2285..0000000
--- a/test/1932-monitor-events-misc/jvm-expected.patch
+++ /dev/null
@@ -1,2 +0,0 @@
-29a30
-> Locker thread 8 for NamedLock[Lock testThrowWait] monitor-waited NamedLock[Lock testThrowWait] timed_out: false
diff --git a/test/1932-monitor-events-misc/run b/test/1932-monitor-events-misc/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1932-monitor-events-misc/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1932-monitor-events-misc/run.py b/test/1932-monitor-events-misc/run.py
new file mode 100644
index 0000000..43e66fd
--- /dev/null
+++ b/test/1932-monitor-events-misc/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # The RI sends an extra event that art doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1933-monitor-current-contended/run b/test/1933-monitor-current-contended/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1933-monitor-current-contended/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1933-monitor-current-contended/run.py b/test/1933-monitor-current-contended/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1933-monitor-current-contended/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1934-jvmti-signal-thread/run b/test/1934-jvmti-signal-thread/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1934-jvmti-signal-thread/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1934-jvmti-signal-thread/run.py b/test/1934-jvmti-signal-thread/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run
deleted file mode 100755
index e569d08..0000000
--- a/test/1935-get-set-current-frame-jit/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ensure the test is not subject to code collection
-./default-run "$@" --jvmti --runtime-option -Xjitinitialsize:32M
diff --git a/test/1935-get-set-current-frame-jit/run.py b/test/1935-get-set-current-frame-jit/run.py
new file mode 100644
index 0000000..fb36023
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ensure the test is not subject to code collection
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/1936-thread-end-events/check b/test/1936-thread-end-events/check
deleted file mode 100644
index 0fe4429..0000000
--- a/test/1936-thread-end-events/check
+++ /dev/null
@@ -1,28 +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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI sends an extra event that art doesn't. Add it to the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1936-thread-end-events/expected-stdout.jvm.txt b/test/1936-thread-end-events/expected-stdout.jvm.txt
new file mode 100644
index 0000000..a8e79a4
--- /dev/null
+++ b/test/1936-thread-end-events/expected-stdout.jvm.txt
@@ -0,0 +1,49 @@
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered private void java.lang.Thread.exit()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered void java.lang.ThreadGroup.threadTerminated(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered private void java.lang.ThreadGroup.remove(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static native void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static void art.Test1936.NotifyThreadEnd(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: null
+
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: null
+
diff --git a/test/1936-thread-end-events/jvm-expected.patch b/test/1936-thread-end-events/jvm-expected.patch
deleted file mode 100644
index ddb30a3..0000000
--- a/test/1936-thread-end-events/jvm-expected.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-7a8,14
-> Entered private void java.lang.Thread.exit()
-> Thread: test-thread
-> | alive: true
-> | interrupted: false
-> | daemon: false
-> | group: java.lang.ThreadGroup[name=main,maxpri=10]
->
-34c41
-< | group: java.lang.ThreadGroup[name=main,maxpri=10]
----
-> | group: null
-41c48
-< | group: java.lang.ThreadGroup[name=main,maxpri=10]
----
-> | group: null
diff --git a/test/1936-thread-end-events/run b/test/1936-thread-end-events/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1936-thread-end-events/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1936-thread-end-events/run.py b/test/1936-thread-end-events/run.py
new file mode 100644
index 0000000..3e1a8e9
--- /dev/null
+++ b/test/1936-thread-end-events/run.py
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
+
+ # The RI sends an extra event that art doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1937-transform-soft-fail/run b/test/1937-transform-soft-fail/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1937-transform-soft-fail/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1937-transform-soft-fail/run.py b/test/1937-transform-soft-fail/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1937-transform-soft-fail/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1938-transform-abstract-single-impl/run b/test/1938-transform-abstract-single-impl/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/1938-transform-abstract-single-impl/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --no-app-image
diff --git a/test/1938-transform-abstract-single-impl/run.py b/test/1938-transform-abstract-single-impl/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/1938-transform-abstract-single-impl/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/1939-proxy-frames/run b/test/1939-proxy-frames/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1939-proxy-frames/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1939-proxy-frames/run.py b/test/1939-proxy-frames/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1939-proxy-frames/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1940-ddms-ext/ddm_ext.cc b/test/1940-ddms-ext/ddm_ext.cc
index 110ad64..6461bdd 100644
--- a/test/1940-ddms-ext/ddm_ext.cc
+++ b/test/1940-ddms-ext/ddm_ext.cc
@@ -21,8 +21,8 @@
// Test infrastructure
#include "jvmti_helper.h"
-#include "nativehelper/scoped_local_ref.h"
-#include "nativehelper/scoped_primitive_array.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
#include "test_env.h"
namespace art {
diff --git a/test/1940-ddms-ext/run b/test/1940-ddms-ext/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1940-ddms-ext/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1940-ddms-ext/run.py b/test/1940-ddms-ext/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1940-ddms-ext/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java
index 605e409..7adcd4a 100644
--- a/test/1940-ddms-ext/src-art/art/Test1940.java
+++ b/test/1940-ddms-ext/src-art/art/Test1940.java
@@ -16,6 +16,7 @@
package art;
+import java.lang.reflect.Field;
import org.apache.harmony.dalvik.ddmc.*;
import dalvik.system.VMDebug;
@@ -50,18 +51,21 @@
}
}
- private static boolean chunkEq(Chunk a, Chunk b) {
- return a.type == b.type &&
- a.offset == b.offset &&
- a.length == b.length &&
- Arrays.equals(a.data, b.data);
+ private static boolean chunkEq(Chunk c1, Chunk c2) {
+ ChunkWrapper a = new ChunkWrapper(c1);
+ ChunkWrapper b = new ChunkWrapper(c2);
+ return a.type() == b.type() &&
+ a.offset() == b.offset() &&
+ a.length() == b.length() &&
+ Arrays.equals(a.data(), b.data());
}
private static String printChunk(Chunk k) {
- byte[] out = new byte[k.length];
- System.arraycopy(k.data, k.offset, out, 0, k.length);
+ ChunkWrapper c = new ChunkWrapper(k);
+ byte[] out = new byte[c.length()];
+ System.arraycopy(c.data(), c.offset(), out, 0, c.length());
return String.format("Chunk(Type: 0x%X, Len: %d, data: %s)",
- k.type, k.length, Arrays.toString(out));
+ c.type(), c.length(), Arrays.toString(out));
}
private static final class MyDdmHandler extends ChunkHandler {
@@ -73,7 +77,8 @@
// For this test we will simply calculate the checksum
ByteBuffer b = ByteBuffer.wrap(new byte[8]);
Adler32 a = new Adler32();
- a.update(req.data, req.offset, req.length);
+ ChunkWrapper reqWrapper = new ChunkWrapper(req);
+ a.update(reqWrapper.data(), reqWrapper.offset(), reqWrapper.length());
b.order(ByteOrder.BIG_ENDIAN);
long val = a.getValue();
b.putLong(val);
@@ -92,6 +97,49 @@
}
}
+
+ /**
+ * Wrapper for accessing the hidden fields in {@link Chunk} in CTS.
+ */
+ private static class ChunkWrapper {
+ private Chunk c;
+
+ ChunkWrapper(Chunk c) {
+ this.c = c;
+ }
+
+ int type() {
+ return c.type;
+ }
+
+ int length() {
+ try {
+ Field f = Chunk.class.getField("length");
+ return (int) f.get(c);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ byte[] data() {
+ try {
+ Field f = Chunk.class.getField("data");
+ return (byte[]) f.get(c);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ int offset() {
+ try {
+ Field f = Chunk.class.getField("offset");
+ return (int) f.get(c);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler();
public static DdmHandler CURRENT_HANDLER;
diff --git a/test/1941-dispose-stress/run b/test/1941-dispose-stress/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1941-dispose-stress/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1941-dispose-stress/run.py b/test/1941-dispose-stress/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1941-dispose-stress/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1942-suspend-raw-monitor-exit/run b/test/1942-suspend-raw-monitor-exit/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1942-suspend-raw-monitor-exit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1942-suspend-raw-monitor-exit/run.py b/test/1942-suspend-raw-monitor-exit/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1942-suspend-raw-monitor-exit/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1943-suspend-raw-monitor-wait/run b/test/1943-suspend-raw-monitor-wait/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1943-suspend-raw-monitor-wait/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1943-suspend-raw-monitor-wait/run.py b/test/1943-suspend-raw-monitor-wait/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1943-suspend-raw-monitor-wait/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1946-list-descriptors/run b/test/1946-list-descriptors/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1946-list-descriptors/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1946-list-descriptors/run.py b/test/1946-list-descriptors/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1946-list-descriptors/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1947-breakpoint-redefine-deopt/run b/test/1947-breakpoint-redefine-deopt/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1947-breakpoint-redefine-deopt/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1947-breakpoint-redefine-deopt/run.py b/test/1947-breakpoint-redefine-deopt/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1947-breakpoint-redefine-deopt/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1948-obsolete-const-method-handle/build b/test/1948-obsolete-const-method-handle/build
deleted file mode 100644
index d0e7a8c..0000000
--- a/test/1948-obsolete-const-method-handle/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-mkdir classes
-./util-src/build-classes $PWD/classes
-
-./default-build --api-level 28 "$@"
diff --git a/test/1948-obsolete-const-method-handle/build.py b/test/1948-obsolete-const-method-handle/build.py
new file mode 100644
index 0000000..4eb0ccb
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level=28)
diff --git a/test/1948-obsolete-const-method-handle/generate-sources b/test/1948-obsolete-const-method-handle/generate-sources
new file mode 100755
index 0000000..9cdc749
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Make us exit on a failure
+set -e
+
+mkdir classes
+./util-src/build-classes $PWD/classes
diff --git a/test/1948-obsolete-const-method-handle/run b/test/1948-obsolete-const-method-handle/run
deleted file mode 100755
index 9eacb4c..0000000
--- a/test/1948-obsolete-const-method-handle/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Squash the exit status and put it in expected
-./default-run --jvmti "$@"
diff --git a/test/1948-obsolete-const-method-handle/run.py b/test/1948-obsolete-const-method-handle/run.py
new file mode 100644
index 0000000..afb1374
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Squash the exit status and put it in expected
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
index 3d194b4..f20012f 100644
--- a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
@@ -71,7 +71,7 @@
ClassReader cr = new ClassReader(initClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cr.accept(
- new ClassVisitor(Opcodes.ASM7, cw) {
+ new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public void visitEnd() {
generateStringAccessorMethod(
@@ -143,7 +143,7 @@
ClassReader cr = new ClassReader(inputClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cr.accept(
- new ClassVisitor(Opcodes.ASM7, cw) {
+ new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public void visitEnd() {
generateRunTest(cw, toCall);
diff --git a/test/1949-short-dex-file/run b/test/1949-short-dex-file/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1949-short-dex-file/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1949-short-dex-file/run.py b/test/1949-short-dex-file/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1949-short-dex-file/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1950-unprepared-transform/check b/test/1950-unprepared-transform/check
deleted file mode 100755
index 0fe4429..0000000
--- a/test/1950-unprepared-transform/check
+++ /dev/null
@@ -1,28 +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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI sends an extra event that art doesn't. Add it to the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1950-unprepared-transform/expected-stdout.jvm.txt b/test/1950-unprepared-transform/expected-stdout.jvm.txt
new file mode 100644
index 0000000..cc470e1
--- /dev/null
+++ b/test/1950-unprepared-transform/expected-stdout.jvm.txt
@@ -0,0 +1,7 @@
+Redefine in ClassLoad on current thread.
+retransformClasses on an unprepared class succeeded
+Object out is: Transformed object!
+Redefine during ClassLoad on another thread.
+retransformClasses on an unprepared class succeeded
+Object out is: Transformed object!
+Redefinition thread finished.
diff --git a/test/1950-unprepared-transform/jvm-expected.patch b/test/1950-unprepared-transform/jvm-expected.patch
deleted file mode 100644
index fd0131e..0000000
--- a/test/1950-unprepared-transform/jvm-expected.patch
+++ /dev/null
@@ -1,6 +0,0 @@
-2,3c2,3
-< Trying to redefine: class Transform. Caught error class java.lang.Exception: Failed to retransform class <LTransform;> due to JVMTI_ERROR_INTERNAL
-< Object out is: NON Transformed Object
----
-> retransformClasses on an unprepared class succeeded
-> Object out is: Transformed object!
diff --git a/test/1950-unprepared-transform/run b/test/1950-unprepared-transform/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/1950-unprepared-transform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --no-app-image
diff --git a/test/1950-unprepared-transform/run.py b/test/1950-unprepared-transform/run.py
new file mode 100644
index 0000000..995bbbd
--- /dev/null
+++ b/test/1950-unprepared-transform/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
+
+ # The RI sends an extra event that art doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1951-monitor-enter-no-suspend/run b/test/1951-monitor-enter-no-suspend/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1951-monitor-enter-no-suspend/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1951-monitor-enter-no-suspend/run.py b/test/1951-monitor-enter-no-suspend/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1951-monitor-enter-no-suspend/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1953-pop-frame/check b/test/1953-pop-frame/check
deleted file mode 100755
index 24bbf07..0000000
--- a/test/1953-pop-frame/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < class-loading-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1953-pop-frame/class-loading-expected.patch b/test/1953-pop-frame/class-loading-expected.patch
deleted file mode 100644
index 1a5eda7..0000000
--- a/test/1953-pop-frame/class-loading-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-36a37,50
-> Test stopped during notifyFramePop without exception on pop of calledFunction
-> Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-> result is StandardTestObject { cnt: 2 } base-call count: 1
-> Test stopped during notifyFramePop without exception on pop of doNothing
-> Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-> result is StandardTestObject { cnt: 1 } base-call count: 1
-> Test stopped during notifyFramePop with exception on pop of calledFunction
-> Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-> art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-> result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-> Test stopped during notifyFramePop with exception on pop of doThrow
-> Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-> art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-> result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-60a75,94
-> Test stopped during a ClassLoad event.
-> Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-> Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.Test1953.popFrame(Native Method)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTests(Test1953.java)
-> <Additional frames hidden>
-> TC0.foo == 1
-> result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-> Test stopped during a ClassPrepare event.
-> Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-> Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.Test1953.popFrame(Native Method)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTests(Test1953.java)
-> <Additional frames hidden>
-> TC1.foo == 2
-> result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1953-pop-frame/expected-stdout.no-jvm.txt b/test/1953-pop-frame/expected-stdout.no-jvm.txt
new file mode 100644
index 0000000..e75ea64
--- /dev/null
+++ b/test/1953-pop-frame/expected-stdout.no-jvm.txt
@@ -0,0 +1,121 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during notifyFramePop without exception on pop of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during notifyFramePop without exception on pop of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during notifyFramePop with exception on pop of calledFunction
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during notifyFramePop with exception on pop of doThrow
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during a ClassLoad event.
+Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+TC0.foo == 1
+result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
+Test stopped during a ClassPrepare event.
+Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+TC1.foo == 2
+result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1953-pop-frame/run b/test/1953-pop-frame/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1953-pop-frame/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1953-pop-frame/run.py b/test/1953-pop-frame/run.py
new file mode 100644
index 0000000..81e237f
--- /dev/null
+++ b/test/1953-pop-frame/run.py
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if not (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".no-jvm.txt")
diff --git a/test/1954-pop-frame-jit/check b/test/1954-pop-frame-jit/check
deleted file mode 100755
index e25838a..0000000
--- a/test/1954-pop-frame-jit/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1954-pop-frame-jit/expected-stdout.jvm.txt b/test/1954-pop-frame-jit/expected-stdout.jvm.txt
new file mode 100644
index 0000000..dafc6b4
--- /dev/null
+++ b/test/1954-pop-frame-jit/expected-stdout.jvm.txt
@@ -0,0 +1,87 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1954-pop-frame-jit/jvm-expected.patch b/test/1954-pop-frame-jit/jvm-expected.patch
deleted file mode 100644
index 6539d56..0000000
--- a/test/1954-pop-frame-jit/jvm-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-37,50d36
-< Test stopped during notifyFramePop without exception on pop of calledFunction
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop without exception on pop of doNothing
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 1 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of calledFunction
-< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of doThrow
-< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-75,94d60
-< Test stopped during a ClassLoad event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC0.foo == 1
-< result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-< Test stopped during a ClassPrepare event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC1.foo == 2
-< result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1954-pop-frame-jit/run b/test/1954-pop-frame-jit/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1954-pop-frame-jit/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1954-pop-frame-jit/run.py b/test/1954-pop-frame-jit/run.py
new file mode 100644
index 0000000..459b447
--- /dev/null
+++ b/test/1954-pop-frame-jit/run.py
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1955-pop-frame-jit-called/check b/test/1955-pop-frame-jit-called/check
deleted file mode 100755
index e25838a..0000000
--- a/test/1955-pop-frame-jit-called/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1955-pop-frame-jit-called/expected-stdout.jvm.txt b/test/1955-pop-frame-jit-called/expected-stdout.jvm.txt
new file mode 100644
index 0000000..dafc6b4
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/expected-stdout.jvm.txt
@@ -0,0 +1,87 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1955-pop-frame-jit-called/jvm-expected.patch b/test/1955-pop-frame-jit-called/jvm-expected.patch
deleted file mode 100644
index 6539d56..0000000
--- a/test/1955-pop-frame-jit-called/jvm-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-37,50d36
-< Test stopped during notifyFramePop without exception on pop of calledFunction
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop without exception on pop of doNothing
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 1 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of calledFunction
-< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of doThrow
-< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-75,94d60
-< Test stopped during a ClassLoad event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC0.foo == 1
-< result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-< Test stopped during a ClassPrepare event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC1.foo == 2
-< result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1955-pop-frame-jit-called/run b/test/1955-pop-frame-jit-called/run
deleted file mode 100755
index 2984461..0000000
--- a/test/1955-pop-frame-jit-called/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-# The jitthreshold prevents the jit from compiling anything except those which
-# we explicitly request.
-./default-run "$@" --android-runtime-option -Xjitthreshold:1000 --jvmti $ARGS
diff --git a/test/1955-pop-frame-jit-called/run.py b/test/1955-pop-frame-jit-called/run.py
new file mode 100644
index 0000000..a43b17d
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/run.py
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ # The jitthreshold prevents the jit from compiling anything except those which
+ # we explicitly request.
+ ctx.default_run(
+ args,
+ android_runtime_option=["-Xjitthreshold:1000"],
+ jvmti=True,
+ test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1956-pop-frame-jit-calling/check b/test/1956-pop-frame-jit-calling/check
deleted file mode 100755
index e25838a..0000000
--- a/test/1956-pop-frame-jit-calling/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1956-pop-frame-jit-calling/expected-stdout.jvm.txt b/test/1956-pop-frame-jit-calling/expected-stdout.jvm.txt
new file mode 100644
index 0000000..dafc6b4
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/expected-stdout.jvm.txt
@@ -0,0 +1,87 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1956-pop-frame-jit-calling/jvm-expected.patch b/test/1956-pop-frame-jit-calling/jvm-expected.patch
deleted file mode 100644
index 6539d56..0000000
--- a/test/1956-pop-frame-jit-calling/jvm-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-37,50d36
-< Test stopped during notifyFramePop without exception on pop of calledFunction
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop without exception on pop of doNothing
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 1 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of calledFunction
-< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of doThrow
-< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-75,94d60
-< Test stopped during a ClassLoad event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC0.foo == 1
-< result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-< Test stopped during a ClassPrepare event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC1.foo == 2
-< result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1956-pop-frame-jit-calling/run b/test/1956-pop-frame-jit-calling/run
deleted file mode 100755
index 2984461..0000000
--- a/test/1956-pop-frame-jit-calling/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-# The jitthreshold prevents the jit from compiling anything except those which
-# we explicitly request.
-./default-run "$@" --android-runtime-option -Xjitthreshold:1000 --jvmti $ARGS
diff --git a/test/1956-pop-frame-jit-calling/run.py b/test/1956-pop-frame-jit-calling/run.py
new file mode 100644
index 0000000..a43b17d
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/run.py
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ # The jitthreshold prevents the jit from compiling anything except those which
+ # we explicitly request.
+ ctx.default_run(
+ args,
+ android_runtime_option=["-Xjitthreshold:1000"],
+ jvmti=True,
+ test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1957-error-ext/run b/test/1957-error-ext/run
deleted file mode 100755
index 8be0ed4..0000000
--- a/test/1957-error-ext/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-./default-run "$@" --jvmti
diff --git a/test/1957-error-ext/run.py b/test/1957-error-ext/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1957-error-ext/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1958-transform-try-jit/run b/test/1958-transform-try-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1958-transform-try-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1958-transform-try-jit/run.py b/test/1958-transform-try-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1958-transform-try-jit/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1959-redefine-object-instrument/run b/test/1959-redefine-object-instrument/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1959-redefine-object-instrument/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1959-redefine-object-instrument/run.py b/test/1959-redefine-object-instrument/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1959-redefine-object-instrument/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1960-obsolete-jit-multithread-native/run b/test/1960-obsolete-jit-multithread-native/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1960-obsolete-jit-multithread-native/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1960-obsolete-jit-multithread-native/run.py b/test/1960-obsolete-jit-multithread-native/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1960-obsolete-jit-multithread-native/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1961-obsolete-jit-multithread/run b/test/1961-obsolete-jit-multithread/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1961-obsolete-jit-multithread/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1961-obsolete-jit-multithread/run.py b/test/1961-obsolete-jit-multithread/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1961-obsolete-jit-multithread/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1962-multi-thread-events/run b/test/1962-multi-thread-events/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1962-multi-thread-events/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1962-multi-thread-events/run.py b/test/1962-multi-thread-events/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1962-multi-thread-events/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1963-add-to-dex-classloader-in-memory/check b/test/1963-add-to-dex-classloader-in-memory/check
deleted file mode 100755
index d536891..0000000
--- a/test/1963-add-to-dex-classloader-in-memory/check
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Some of our test devices are so old that they don't have memfd_create and are setup in such a way
-# that tmpfile() doesn't work. In these cases this test cannot complete successfully.
-
-if grep -q -- '---NO memfd_create---' $@; then
- echo "The test device doesn't have memfd_create. Cannot verify test!" >&2
- exit 0
-fi
-
-
-./default-check "$@"
diff --git a/test/1963-add-to-dex-classloader-in-memory/run b/test/1963-add-to-dex-classloader-in-memory/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1963-add-to-dex-classloader-in-memory/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1963-add-to-dex-classloader-in-memory/run.py b/test/1963-add-to-dex-classloader-in-memory/run.py
new file mode 100644
index 0000000..ad7c1ae
--- /dev/null
+++ b/test/1963-add-to-dex-classloader-in-memory/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # Some of our test devices are so old that they don't have memfd_create and are setup in such a way
+ # that tmpfile() doesn't work. In these cases this test cannot complete successfully.
+ # If we see this in stdout, make the expected stdout identical.
+ ctx.run(
+ fr"grep -q -- '---NO memfd_create---' '{args.stdout_file}' &&"
+ fr" echo '---NO memfd_create---' > expected-stdout.txt",
+ check=False)
diff --git a/test/1964-add-to-dex-classloader-file/run b/test/1964-add-to-dex-classloader-file/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1964-add-to-dex-classloader-file/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1964-add-to-dex-classloader-file/run.py b/test/1964-add-to-dex-classloader-file/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1964-add-to-dex-classloader-file/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1965-get-set-local-primitive-no-tables/build b/test/1965-get-set-local-primitive-no-tables/build
deleted file mode 100644
index 6631df9..0000000
--- a/test/1965-get-set-local-primitive-no-tables/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ != *"--jvm"* ]]; then
- mv jasmin jasmin-unused
-else
- mv smali smali-unused
-fi
-./default-build "$@"
diff --git a/test/1965-get-set-local-primitive-no-tables/build.py b/test/1965-get-set-local-primitive-no-tables/build.py
new file mode 100644
index 0000000..3a908c0
--- /dev/null
+++ b/test/1965-get-set-local-primitive-no-tables/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ ctx.default_build()
diff --git a/test/1965-get-set-local-primitive-no-tables/generate-sources b/test/1965-get-set-local-primitive-no-tables/generate-sources
new file mode 100755
index 0000000..c2f8653
--- /dev/null
+++ b/test/1965-get-set-local-primitive-no-tables/generate-sources
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ mv jasmin jasmin-unused
+else
+ mv smali smali-unused
+fi
diff --git a/test/1965-get-set-local-primitive-no-tables/run b/test/1965-get-set-local-primitive-no-tables/run
deleted file mode 100755
index 9b741ee..0000000
--- a/test/1965-get-set-local-primitive-no-tables/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-# If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
-# which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
-# on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
-# this from happening.
-./default-run "$@" --jvmti --compiler-only-option --debuggable
diff --git a/test/1965-get-set-local-primitive-no-tables/run.py b/test/1965-get-set-local-primitive-no-tables/run.py
new file mode 100644
index 0000000..77a45e9
--- /dev/null
+++ b/test/1965-get-set-local-primitive-no-tables/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+
+def run(ctx, args):
+ # If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
+ # which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
+ # on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
+ # this from happening.
+ ctx.default_run(args, jvmti=True, compiler_only_option=["--debuggable"])
diff --git a/test/1966-get-set-local-objects-no-table/build b/test/1966-get-set-local-objects-no-table/build
deleted file mode 100644
index 6631df9..0000000
--- a/test/1966-get-set-local-objects-no-table/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ != *"--jvm"* ]]; then
- mv jasmin jasmin-unused
-else
- mv smali smali-unused
-fi
-./default-build "$@"
diff --git a/test/1966-get-set-local-objects-no-table/build.py b/test/1966-get-set-local-objects-no-table/build.py
new file mode 100644
index 0000000..3a908c0
--- /dev/null
+++ b/test/1966-get-set-local-objects-no-table/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ ctx.default_build()
diff --git a/test/1966-get-set-local-objects-no-table/generate-sources b/test/1966-get-set-local-objects-no-table/generate-sources
new file mode 100755
index 0000000..c2f8653
--- /dev/null
+++ b/test/1966-get-set-local-objects-no-table/generate-sources
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ mv jasmin jasmin-unused
+else
+ mv smali smali-unused
+fi
diff --git a/test/1966-get-set-local-objects-no-table/run b/test/1966-get-set-local-objects-no-table/run
deleted file mode 100755
index 9b741ee..0000000
--- a/test/1966-get-set-local-objects-no-table/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-# If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
-# which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
-# on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
-# this from happening.
-./default-run "$@" --jvmti --compiler-only-option --debuggable
diff --git a/test/1966-get-set-local-objects-no-table/run.py b/test/1966-get-set-local-objects-no-table/run.py
new file mode 100644
index 0000000..77a45e9
--- /dev/null
+++ b/test/1966-get-set-local-objects-no-table/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+
+def run(ctx, args):
+ # If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
+ # which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
+ # on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
+ # this from happening.
+ ctx.default_run(args, jvmti=True, compiler_only_option=["--debuggable"])
diff --git a/test/1967-get-set-local-bad-slot/run b/test/1967-get-set-local-bad-slot/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1967-get-set-local-bad-slot/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/1967-get-set-local-bad-slot/run.py b/test/1967-get-set-local-bad-slot/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1967-get-set-local-bad-slot/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1968-force-early-return/run b/test/1968-force-early-return/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1968-force-early-return/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1968-force-early-return/run.py b/test/1968-force-early-return/run.py
new file mode 100644
index 0000000..fa08256
--- /dev/null
+++ b/test/1968-force-early-return/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
diff --git a/test/1969-force-early-return-void/check b/test/1969-force-early-return-void/check
deleted file mode 100755
index 24bbf07..0000000
--- a/test/1969-force-early-return-void/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < class-loading-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1969-force-early-return-void/class-loading-expected.patch b/test/1969-force-early-return-void/class-loading-expected.patch
deleted file mode 100644
index 5e13595..0000000
--- a/test/1969-force-early-return-void/class-loading-expected.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-178a179,212
-> Test stopped during class-load.
-> NORMAL RUN: Single call with no interference on (ID: 46) ClassLoadObject { cnt: 0, curClass: 0}
-> TC0.foo == 100
-> NORMAL RUN: result for (ID: 46) ClassLoadObject { cnt: 1, curClass: 1} on Test1969 target thread - 46
-> Single call with force-early-return on (ID: 47) ClassLoadObject { cnt: 0, curClass: 1}
-> Will force return of Test1969 target thread - 47
-> Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.NonStandardExit.forceEarlyReturnVoid(Native Method)
-> art.Test1969$TestSuspender.performForceReturn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTests(Test1969.java)
-> <Additional frames hidden>
->
-> TC1.foo == 201
-> result for (ID: 47) ClassLoadObject { cnt: 1, curClass: 2} on Test1969 target thread - 47
-> Test stopped during class-load.
-> NORMAL RUN: Single call with no interference on (ID: 48) ClassLoadObject { cnt: 0, curClass: 2}
-> TC2.foo == 302
-> NORMAL RUN: result for (ID: 48) ClassLoadObject { cnt: 1, curClass: 3} on Test1969 target thread - 48
-> Single call with force-early-return on (ID: 49) ClassLoadObject { cnt: 0, curClass: 3}
-> Will force return of Test1969 target thread - 49
-> Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.NonStandardExit.forceEarlyReturnVoid(Native Method)
-> art.Test1969$TestSuspender.performForceReturn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTests(Test1969.java)
-> <Additional frames hidden>
->
-> TC3.foo == 403
-> result for (ID: 49) ClassLoadObject { cnt: 1, curClass: 4} on Test1969 target thread - 49
diff --git a/test/1969-force-early-return-void/expected-stdout.no-jvm.txt b/test/1969-force-early-return-void/expected-stdout.no-jvm.txt
new file mode 100644
index 0000000..1bb3252
--- /dev/null
+++ b/test/1969-force-early-return-void/expected-stdout.no-jvm.txt
@@ -0,0 +1,212 @@
+Test stopped using breakpoint
+NORMAL RUN: Single call with no interference on (ID: 0) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 0) StandardTestObject { cnt: 2 } on Test1969 target thread - 0
+Single call with force-early-return on (ID: 1) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 1
+result for (ID: 1) StandardTestObject { cnt: 1 } on Test1969 target thread - 1
+Test stopped using breakpoint with declared synchronized function
+NORMAL RUN: Single call with no interference on (ID: 2) SynchronizedFunctionTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 2) SynchronizedFunctionTestObject { cnt: 2 } on Test1969 target thread - 2
+Single call with force-early-return on (ID: 3) SynchronizedFunctionTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 3
+result for (ID: 3) SynchronizedFunctionTestObject { cnt: 1 } on Test1969 target thread - 3
+Test stopped using breakpoint with synchronized block
+NORMAL RUN: Single call with no interference on (ID: 4) SynchronizedTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 4) SynchronizedTestObject { cnt: 2 } on Test1969 target thread - 4
+Single call with force-early-return on (ID: 5) SynchronizedTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 5
+result for (ID: 5) SynchronizedTestObject { cnt: 1 } on Test1969 target thread - 5
+Test stopped on single step
+NORMAL RUN: Single call with no interference on (ID: 6) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 6) StandardTestObject { cnt: 2 } on Test1969 target thread - 6
+Single call with force-early-return on (ID: 7) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 7
+result for (ID: 7) StandardTestObject { cnt: 1 } on Test1969 target thread - 7
+Test stopped on field access
+NORMAL RUN: Single call with no interference on (ID: 8) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+NORMAL RUN: result for (ID: 8) FieldBasedTestObject { TARGET_FIELD: 10, cnt: 2 } on Test1969 target thread - 8
+Single call with force-early-return on (ID: 9) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+Will force return of Test1969 target thread - 9
+result for (ID: 9) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 1 } on Test1969 target thread - 9
+Test stopped on field modification
+NORMAL RUN: Single call with no interference on (ID: 10) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+NORMAL RUN: result for (ID: 10) FieldBasedTestObject { TARGET_FIELD: 10, cnt: 2 } on Test1969 target thread - 10
+Single call with force-early-return on (ID: 11) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+Will force return of Test1969 target thread - 11
+result for (ID: 11) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 1 } on Test1969 target thread - 11
+Test stopped during Method Exit of calledFunction
+NORMAL RUN: Single call with no interference on (ID: 12) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 12) StandardTestObject { cnt: 2 } on Test1969 target thread - 12
+Single call with force-early-return on (ID: 13) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 13
+result for (ID: 13) StandardTestObject { cnt: 2 } on Test1969 target thread - 13
+Test stopped during Method Enter of calledFunction
+NORMAL RUN: Single call with no interference on (ID: 14) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 14) StandardTestObject { cnt: 2 } on Test1969 target thread - 14
+Single call with force-early-return on (ID: 15) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 15
+result for (ID: 15) StandardTestObject { cnt: 0 } on Test1969 target thread - 15
+Test stopped during Method Exit due to exception thrown in same function
+NORMAL RUN: Single call with no interference on (ID: 16) ExceptionOnceObject { cnt: 0, throwInSub: false }
+Uncaught exception in thread Thread[Test1969 target thread - 16,5,main] - art.Test1969$ExceptionOnceObject$TestError: null
+ art.Test1969$ExceptionOnceObject.calledFunction(Test1969.java)
+ art.Test1969$AbstractTestObject.run(Test1969.java)
+ art.Test1969$2.run(Test1969.java)
+ java.lang.Thread.run(Thread.java)
+
+NORMAL RUN: result for (ID: 16) ExceptionOnceObject { cnt: 1, throwInSub: false } on Test1969 target thread - 16
+Single call with force-early-return on (ID: 17) ExceptionOnceObject { cnt: 0, throwInSub: false }
+Will force return of Test1969 target thread - 17
+result for (ID: 17) ExceptionOnceObject { cnt: 1, throwInSub: false } on Test1969 target thread - 17
+Test stopped during Method Exit due to exception thrown in subroutine
+NORMAL RUN: Single call with no interference on (ID: 18) ExceptionOnceObject { cnt: 0, throwInSub: true }
+Uncaught exception in thread Thread[Test1969 target thread - 18,5,main] - art.Test1969$ExceptionOnceObject$TestError: null
+ art.Test1969$ExceptionOnceObject.doThrow(Test1969.java)
+ art.Test1969$ExceptionOnceObject.calledFunction(Test1969.java)
+ art.Test1969$AbstractTestObject.run(Test1969.java)
+ art.Test1969$2.run(Test1969.java)
+ java.lang.Thread.run(Thread.java)
+
+NORMAL RUN: result for (ID: 18) ExceptionOnceObject { cnt: 1, throwInSub: true } on Test1969 target thread - 18
+Single call with force-early-return on (ID: 19) ExceptionOnceObject { cnt: 0, throwInSub: true }
+Will force return of Test1969 target thread - 19
+result for (ID: 19) ExceptionOnceObject { cnt: 1, throwInSub: true } on Test1969 target thread - 19
+Test stopped during notifyFramePop with exception on pop of calledFunction
+NORMAL RUN: Single call with no interference on (ID: 20) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError thrown and caught!
+NORMAL RUN: result for (ID: 20) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 20
+Single call with force-early-return on (ID: 21) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 21
+result for (ID: 21) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 21
+Test stopped during notifyFramePop with exception on pop of doThrow
+NORMAL RUN: Single call with no interference on (ID: 22) ExceptionCatchTestObject { cnt: 0 }
+art.Test1969$ExceptionCatchTestObject$TestError caught in called function.
+NORMAL RUN: result for (ID: 22) ExceptionCatchTestObject { cnt: 2 } on Test1969 target thread - 22
+Single call with force-early-return on (ID: 23) ExceptionCatchTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 23
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_TYPE_MISMATCH
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+art.Test1969$ExceptionCatchTestObject$TestError caught in called function.
+result for (ID: 23) ExceptionCatchTestObject { cnt: 2 } on Test1969 target thread - 23
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+NORMAL RUN: Single call with no interference on (ID: 24) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError caught in same function.
+NORMAL RUN: result for (ID: 24) ExceptionThrowTestObject { cnt: 111, baseCnt: 2 } on Test1969 target thread - 24
+Single call with force-early-return on (ID: 25) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 25
+result for (ID: 25) ExceptionThrowTestObject { cnt: 11, baseCnt: 2 } on Test1969 target thread - 25
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+NORMAL RUN: Single call with no interference on (ID: 26) ExceptionCatchTestObject { cnt: 0 }
+art.Test1969$ExceptionCatchTestObject$TestError caught in called function.
+NORMAL RUN: result for (ID: 26) ExceptionCatchTestObject { cnt: 2 } on Test1969 target thread - 26
+Single call with force-early-return on (ID: 27) ExceptionCatchTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 27
+result for (ID: 27) ExceptionCatchTestObject { cnt: 1 } on Test1969 target thread - 27
+Test stopped during Exception event of calledFunction (catch in calling function)
+NORMAL RUN: Single call with no interference on (ID: 28) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError thrown and caught!
+NORMAL RUN: result for (ID: 28) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 28
+Single call with force-early-return on (ID: 29) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 29
+result for (ID: 29) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 29
+Test stopped during Exception event of calledFunction (catch in called function)
+NORMAL RUN: Single call with no interference on (ID: 30) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError caught in same function.
+NORMAL RUN: result for (ID: 30) ExceptionThrowTestObject { cnt: 111, baseCnt: 2 } on Test1969 target thread - 30
+Single call with force-early-return on (ID: 31) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 31
+result for (ID: 31) ExceptionThrowTestObject { cnt: 11, baseCnt: 2 } on Test1969 target thread - 31
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+NORMAL RUN: Single call with no interference on (ID: 32) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowFarTestObject$TestError thrown and caught!
+NORMAL RUN: result for (ID: 32) ExceptionThrowFarTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 32
+Single call with force-early-return on (ID: 33) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 33
+result for (ID: 33) ExceptionThrowFarTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 33
+Test stopped during Exception event of calledFunction (catch in called function)
+NORMAL RUN: Single call with no interference on (ID: 34) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowFarTestObject$TestError caught in same function.
+NORMAL RUN: result for (ID: 34) ExceptionThrowFarTestObject { cnt: 111, baseCnt: 2 } on Test1969 target thread - 34
+Single call with force-early-return on (ID: 35) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 35
+result for (ID: 35) ExceptionThrowFarTestObject { cnt: 101, baseCnt: 2 } on Test1969 target thread - 35
+Test stopped during random Suspend.
+NORMAL RUN: Single call with no interference on (ID: 36) SuspendSuddenlyObject { cnt: 0, spun: false }
+NORMAL RUN: result for (ID: 36) SuspendSuddenlyObject { cnt: 2, spun: true } on Test1969 target thread - 36
+Single call with force-early-return on (ID: 37) SuspendSuddenlyObject { cnt: 0, spun: false }
+Will force return of Test1969 target thread - 37
+result for (ID: 37) SuspendSuddenlyObject { cnt: 1, spun: true } on Test1969 target thread - 37
+Test stopped during a native method fails
+NORMAL RUN: Single call with no interference on (ID: 38) NativeCalledObject { cnt: 0 }
+NORMAL RUN: result for (ID: 38) NativeCalledObject { cnt: 2 } on Test1969 target thread - 38
+Single call with force-early-return on (ID: 39) NativeCalledObject { cnt: 0 }
+Will force return of Test1969 target thread - 39
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+result for (ID: 39) NativeCalledObject { cnt: 2 } on Test1969 target thread - 39
+Test stopped in a method called by native succeeds
+NORMAL RUN: Single call with no interference on (ID: 40) NativeCallerObject { cnt: 0 }
+NORMAL RUN: result for (ID: 40) NativeCallerObject { cnt: 2 } on Test1969 target thread - 40
+Single call with force-early-return on (ID: 41) NativeCallerObject { cnt: 0 }
+Will force return of Test1969 target thread - 41
+result for (ID: 41) NativeCallerObject { cnt: 2 } on Test1969 target thread - 41
+Test stopped in a static method
+NORMAL RUN: Single call with no interference on (ID: 42) StaticMethodObject { cnt: 0 }
+NORMAL RUN: result for (ID: 42) StaticMethodObject { cnt: 2 } on Test1969 target thread - 42
+Single call with force-early-return on (ID: 43) StaticMethodObject { cnt: 0 }
+Will force return of Test1969 target thread - 43
+result for (ID: 43) StaticMethodObject { cnt: 1 } on Test1969 target thread - 43
+Test stopped in a Object <init> method
+NORMAL RUN: Single call with no interference on (ID: 44) ObjectInitTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 44) ObjectInitTestObject { cnt: 2 } on Test1969 target thread - 44
+Single call with force-early-return on (ID: 45) ObjectInitTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 45
+result for (ID: 45) ObjectInitTestObject { cnt: 1 } on Test1969 target thread - 45
+Test stopped during class-load.
+NORMAL RUN: Single call with no interference on (ID: 46) ClassLoadObject { cnt: 0, curClass: 0}
+TC0.foo == 100
+NORMAL RUN: result for (ID: 46) ClassLoadObject { cnt: 1, curClass: 1} on Test1969 target thread - 46
+Single call with force-early-return on (ID: 47) ClassLoadObject { cnt: 0, curClass: 1}
+Will force return of Test1969 target thread - 47
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+TC1.foo == 201
+result for (ID: 47) ClassLoadObject { cnt: 1, curClass: 2} on Test1969 target thread - 47
+Test stopped during class-load.
+NORMAL RUN: Single call with no interference on (ID: 48) ClassLoadObject { cnt: 0, curClass: 2}
+TC2.foo == 302
+NORMAL RUN: result for (ID: 48) ClassLoadObject { cnt: 1, curClass: 3} on Test1969 target thread - 48
+Single call with force-early-return on (ID: 49) ClassLoadObject { cnt: 0, curClass: 3}
+Will force return of Test1969 target thread - 49
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+TC3.foo == 403
+result for (ID: 49) ClassLoadObject { cnt: 1, curClass: 4} on Test1969 target thread - 49
diff --git a/test/1969-force-early-return-void/run b/test/1969-force-early-return-void/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1969-force-early-return-void/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/1969-force-early-return-void/run.py b/test/1969-force-early-return-void/run.py
new file mode 100644
index 0000000..dda04df
--- /dev/null
+++ b/test/1969-force-early-return-void/run.py
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if not (args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".no-jvm.txt")
diff --git a/test/1970-force-early-return-long/run b/test/1970-force-early-return-long/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1970-force-early-return-long/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1970-force-early-return-long/run.py b/test/1970-force-early-return-long/run.py
new file mode 100644
index 0000000..fa08256
--- /dev/null
+++ b/test/1970-force-early-return-long/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
diff --git a/test/1971-multi-force-early-return/run b/test/1971-multi-force-early-return/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1971-multi-force-early-return/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1971-multi-force-early-return/run.py b/test/1971-multi-force-early-return/run.py
new file mode 100644
index 0000000..fa08256
--- /dev/null
+++ b/test/1971-multi-force-early-return/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
diff --git a/test/1972-jni-id-swap-indices/run b/test/1972-jni-id-swap-indices/run
deleted file mode 100755
index 999b92a..0000000
--- a/test/1972-jni-id-swap-indices/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-args=$(echo "$@" | sed 's/--runtime-option -Xopaque-jni-ids\:true//g')
-
-./default-run $args --android-runtime-option -Xopaque-jni-ids:swapable --android-runtime-option -Xauto-promote-opaque-jni-ids:false
\ No newline at end of file
diff --git a/test/1972-jni-id-swap-indices/run.py b/test/1972-jni-id-swap-indices/run.py
new file mode 100644
index 0000000..81ec061
--- /dev/null
+++ b/test/1972-jni-id-swap-indices/run.py
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ for i, opt in enumerate(args.runtime_option):
+ if opt == '-Xopaque-jni-ids:true':
+ args.runtime_option.pop(i)
+ break
+
+ ctx.default_run(
+ args,
+ android_runtime_option=[
+ '-Xopaque-jni-ids:swapable', '-Xauto-promote-opaque-jni-ids:false'
+ ])
diff --git a/test/1973-jni-id-swap-pointer/run b/test/1973-jni-id-swap-pointer/run
deleted file mode 100755
index 999b92a..0000000
--- a/test/1973-jni-id-swap-pointer/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-args=$(echo "$@" | sed 's/--runtime-option -Xopaque-jni-ids\:true//g')
-
-./default-run $args --android-runtime-option -Xopaque-jni-ids:swapable --android-runtime-option -Xauto-promote-opaque-jni-ids:false
\ No newline at end of file
diff --git a/test/1973-jni-id-swap-pointer/run.py b/test/1973-jni-id-swap-pointer/run.py
new file mode 100644
index 0000000..b7ed57a
--- /dev/null
+++ b/test/1973-jni-id-swap-pointer/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ for i, opt in enumerate(args.runtime_option):
+ if opt == '-Xopaque-jni-ids:true':
+ args.runtime_option.pop(i)
+
+ ctx.default_run(
+ args,
+ android_runtime_option=[
+ '-Xopaque-jni-ids:swapable', '-Xauto-promote-opaque-jni-ids:false'
+ ])
diff --git a/test/1974-resize-array/run b/test/1974-resize-array/run
deleted file mode 100755
index 96646c8..0000000
--- a/test/1974-resize-array/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-
-./default-run "$@" --jvmti
diff --git a/test/1974-resize-array/run.py b/test/1974-resize-array/run.py
new file mode 100644
index 0000000..4f5f922
--- /dev/null
+++ b/test/1974-resize-array/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1975-hello-structural-transformation/run b/test/1975-hello-structural-transformation/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1975-hello-structural-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1975-hello-structural-transformation/run.py b/test/1975-hello-structural-transformation/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1975-hello-structural-transformation/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1976-hello-structural-static-methods/run b/test/1976-hello-structural-static-methods/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1976-hello-structural-static-methods/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1976-hello-structural-static-methods/run.py b/test/1976-hello-structural-static-methods/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1976-hello-structural-static-methods/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1977-hello-structural-obsolescence/run b/test/1977-hello-structural-obsolescence/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1977-hello-structural-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1977-hello-structural-obsolescence/run.py b/test/1977-hello-structural-obsolescence/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1977-hello-structural-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1978-regular-obsolete-then-structural-obsolescence/run b/test/1978-regular-obsolete-then-structural-obsolescence/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1978-regular-obsolete-then-structural-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1978-regular-obsolete-then-structural-obsolescence/run.py b/test/1978-regular-obsolete-then-structural-obsolescence/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1978-regular-obsolete-then-structural-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1979-threaded-structural-transformation/run b/test/1979-threaded-structural-transformation/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1979-threaded-structural-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1979-threaded-structural-transformation/run.py b/test/1979-threaded-structural-transformation/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1979-threaded-structural-transformation/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1980-obsolete-object-cleared/expected-stdout.txt b/test/1980-obsolete-object-cleared/expected-stdout.txt
index 9d18b2c..43251f1 100644
--- a/test/1980-obsolete-object-cleared/expected-stdout.txt
+++ b/test/1980-obsolete-object-cleared/expected-stdout.txt
@@ -72,6 +72,13 @@
public native java.lang.annotation.Annotation java.lang.Class.getDeclaredAnnotation(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() with params: []
public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+Calling public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Object] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [(obsolete)class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [long] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public native java.lang.Class[] java.lang.Class.getDeclaredClasses() with params: []
public native java.lang.Class[] java.lang.Class.getDeclaredClasses() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.lang.reflect.Constructor java.lang.Class.getDeclaredConstructor(java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException with params: [[new java.lang.Object[0], new java.lang.Class[0], null]]
@@ -154,10 +161,16 @@
public int java.lang.Class.getModifiers() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.lang.String java.lang.Class.getName() with params: []
public java.lang.String java.lang.Class.getName() on (obsolete)class Main$Transform with [] = Main$Transform
+Calling public java.lang.Class java.lang.Class.getNestHost() with params: []
+public java.lang.Class java.lang.Class.getNestHost() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+Calling public java.lang.Class[] java.lang.Class.getNestMembers() with params: []
+public java.lang.Class[] java.lang.Class.getNestMembers() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.lang.Package java.lang.Class.getPackage() with params: []
public java.lang.Package java.lang.Class.getPackage() on (obsolete)class Main$Transform with [] = null
Calling public java.lang.String java.lang.Class.getPackageName() with params: []
public java.lang.String java.lang.Class.getPackageName() on (obsolete)class Main$Transform with [] =
+Calling public java.lang.Class[] java.lang.Class.getPermittedSubclasses() with params: []
+public java.lang.Class[] java.lang.Class.getPermittedSubclasses() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() with params: []
public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() on (obsolete)class Main$Transform with [] = null
Calling public java.net.URL java.lang.Class.getResource(java.lang.String) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY]]
@@ -213,10 +226,19 @@
public boolean java.lang.Class.isLocalClass() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public boolean java.lang.Class.isMemberClass() with params: []
public boolean java.lang.Class.isMemberClass() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+Calling public boolean java.lang.Class.isNestmateOf(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Class.isPrimitive()' on a null object reference
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [class java.lang.Object] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on (obsolete)class Main$Transform with [(obsolete)class Main$Transform] = true
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on (obsolete)class Main$Transform with [long] = false
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public boolean java.lang.Class.isPrimitive() with params: []
public boolean java.lang.Class.isPrimitive() on (obsolete)class Main$Transform with [] = false
Calling public boolean java.lang.Class.isProxy() with params: []
public boolean java.lang.Class.isProxy() on (obsolete)class Main$Transform with [] = false
+Calling public boolean java.lang.Class.isSealed() with params: []
+public boolean java.lang.Class.isSealed() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public boolean java.lang.Class.isSynthetic() with params: []
public boolean java.lang.Class.isSynthetic() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public native java.lang.Object java.lang.Class.newInstance() throws java.lang.InstantiationException,java.lang.IllegalAccessException with params: []
@@ -285,6 +307,13 @@
public native java.lang.annotation.Annotation java.lang.Class.getDeclaredAnnotation(java.lang.Class) on class Main$Transform with [class java.lang.Class] = null
Calling public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() with params: []
public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() on class Main$Transform with [] = []
+Calling public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Object] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: java.lang.Object[] cannot be cast to java.lang.annotation.Annotation[]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [(obsolete)class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: Main$Transform[] cannot be cast to java.lang.annotation.Annotation[]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [long] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: long[] cannot be cast to java.lang.annotation.Annotation[]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: java.lang.Class[] cannot be cast to java.lang.annotation.Annotation[]
Calling public native java.lang.Class[] java.lang.Class.getDeclaredClasses() with params: []
public native java.lang.Class[] java.lang.Class.getDeclaredClasses() on class Main$Transform with [] = []
Calling public java.lang.reflect.Constructor java.lang.Class.getDeclaredConstructor(java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException with params: [[new java.lang.Object[0], new java.lang.Class[0], null]]
@@ -367,10 +396,16 @@
public int java.lang.Class.getModifiers() on class Main$Transform with [] = 9
Calling public java.lang.String java.lang.Class.getName() with params: []
public java.lang.String java.lang.Class.getName() on class Main$Transform with [] = Main$Transform
+Calling public java.lang.Class java.lang.Class.getNestHost() with params: []
+public java.lang.Class java.lang.Class.getNestHost() on class Main$Transform with [] = class Main$Transform
+Calling public java.lang.Class[] java.lang.Class.getNestMembers() with params: []
+public java.lang.Class[] java.lang.Class.getNestMembers() on class Main$Transform with [] = [class Main$Transform]
Calling public java.lang.Package java.lang.Class.getPackage() with params: []
public java.lang.Package java.lang.Class.getPackage() on class Main$Transform with [] = null
Calling public java.lang.String java.lang.Class.getPackageName() with params: []
public java.lang.String java.lang.Class.getPackageName() on class Main$Transform with [] =
+Calling public java.lang.Class[] java.lang.Class.getPermittedSubclasses() with params: []
+public java.lang.Class[] java.lang.Class.getPermittedSubclasses() on class Main$Transform with [] = null
Calling public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() with params: []
public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() on class Main$Transform with [] = null
Calling public java.net.URL java.lang.Class.getResource(java.lang.String) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY]]
@@ -426,10 +461,19 @@
public boolean java.lang.Class.isLocalClass() on class Main$Transform with [] = false
Calling public boolean java.lang.Class.isMemberClass() with params: []
public boolean java.lang.Class.isMemberClass() on class Main$Transform with [] = true
+Calling public boolean java.lang.Class.isNestmateOf(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Class.isPrimitive()' on a null object reference
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [class java.lang.Object] = false
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [(obsolete)class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [class Main$Transform] = true
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [long] = false
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [class java.lang.Class] = false
Calling public boolean java.lang.Class.isPrimitive() with params: []
public boolean java.lang.Class.isPrimitive() on class Main$Transform with [] = false
Calling public boolean java.lang.Class.isProxy() with params: []
public boolean java.lang.Class.isProxy() on class Main$Transform with [] = false
+Calling public boolean java.lang.Class.isSealed() with params: []
+public boolean java.lang.Class.isSealed() on class Main$Transform with [] = false
Calling public boolean java.lang.Class.isSynthetic() with params: []
public boolean java.lang.Class.isSynthetic() on class Main$Transform with [] = false
Calling public native java.lang.Object java.lang.Class.newInstance() throws java.lang.InstantiationException,java.lang.IllegalAccessException with params: []
diff --git a/test/1980-obsolete-object-cleared/run b/test/1980-obsolete-object-cleared/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1980-obsolete-object-cleared/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1980-obsolete-object-cleared/run.py b/test/1980-obsolete-object-cleared/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1980-obsolete-object-cleared/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1981-structural-redef-private-method-handles/build b/test/1981-structural-redef-private-method-handles/build
deleted file mode 100755
index c80d7ad..0000000
--- a/test/1981-structural-redef-private-method-handles/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/1981-structural-redef-private-method-handles/build.py b/test/1981-structural-redef-private-method-handles/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/1981-structural-redef-private-method-handles/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/1981-structural-redef-private-method-handles/run b/test/1981-structural-redef-private-method-handles/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1981-structural-redef-private-method-handles/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1981-structural-redef-private-method-handles/run.py b/test/1981-structural-redef-private-method-handles/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1981-structural-redef-private-method-handles/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1981-structural-redef-private-method-handles/test-metadata.json b/test/1981-structural-redef-private-method-handles/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/1981-structural-redef-private-method-handles/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/1982-no-virtuals-structural-redefinition/run b/test/1982-no-virtuals-structural-redefinition/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1982-no-virtuals-structural-redefinition/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1982-no-virtuals-structural-redefinition/run.py b/test/1982-no-virtuals-structural-redefinition/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1982-no-virtuals-structural-redefinition/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1983-structural-redefinition-failures/build b/test/1983-structural-redefinition-failures/build
deleted file mode 100755
index c80d7ad..0000000
--- a/test/1983-structural-redefinition-failures/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/1983-structural-redefinition-failures/build.py b/test/1983-structural-redefinition-failures/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/1983-structural-redefinition-failures/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/1983-structural-redefinition-failures/run b/test/1983-structural-redefinition-failures/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1983-structural-redefinition-failures/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1983-structural-redefinition-failures/run.py b/test/1983-structural-redefinition-failures/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1983-structural-redefinition-failures/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1983-structural-redefinition-failures/test-metadata.json b/test/1983-structural-redefinition-failures/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/1983-structural-redefinition-failures/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/1984-structural-redefine-field-trace/run b/test/1984-structural-redefine-field-trace/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1984-structural-redefine-field-trace/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true
diff --git a/test/1984-structural-redefine-field-trace/run.py b/test/1984-structural-redefine-field-trace/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1985-structural-redefine-stack-scope/run b/test/1985-structural-redefine-stack-scope/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1985-structural-redefine-stack-scope/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true
diff --git a/test/1985-structural-redefine-stack-scope/run.py b/test/1985-structural-redefine-stack-scope/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1985-structural-redefine-stack-scope/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/run b/test/1986-structural-redefine-multi-thread-stack-scope/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1986-structural-redefine-multi-thread-stack-scope/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true
diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/run.py b/test/1986-structural-redefine-multi-thread-stack-scope/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1986-structural-redefine-multi-thread-stack-scope/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1987-structural-redefine-recursive-stack-scope/run b/test/1987-structural-redefine-recursive-stack-scope/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1987-structural-redefine-recursive-stack-scope/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true
diff --git a/test/1987-structural-redefine-recursive-stack-scope/run.py b/test/1987-structural-redefine-recursive-stack-scope/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1987-structural-redefine-recursive-stack-scope/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1988-multi-structural-redefine/run b/test/1988-multi-structural-redefine/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1988-multi-structural-redefine/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true
diff --git a/test/1988-multi-structural-redefine/run.py b/test/1988-multi-structural-redefine/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1988-multi-structural-redefine/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1989-transform-bad-monitor/run b/test/1989-transform-bad-monitor/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1989-transform-bad-monitor/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1989-transform-bad-monitor/run.py b/test/1989-transform-bad-monitor/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1989-transform-bad-monitor/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1990-structural-bad-verify/run b/test/1990-structural-bad-verify/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1990-structural-bad-verify/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1990-structural-bad-verify/run.py b/test/1990-structural-bad-verify/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1990-structural-bad-verify/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1991-hello-structural-retransform/run b/test/1991-hello-structural-retransform/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1991-hello-structural-retransform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1991-hello-structural-retransform/run.py b/test/1991-hello-structural-retransform/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1991-hello-structural-retransform/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1992-retransform-no-such-field/run b/test/1992-retransform-no-such-field/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1992-retransform-no-such-field/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/1992-retransform-no-such-field/run.py b/test/1992-retransform-no-such-field/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1992-retransform-no-such-field/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1993-fallback-non-structural/run b/test/1993-fallback-non-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1993-fallback-non-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1993-fallback-non-structural/run.py b/test/1993-fallback-non-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1993-fallback-non-structural/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1994-final-virtual-structural/run b/test/1994-final-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1994-final-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1994-final-virtual-structural/run.py b/test/1994-final-virtual-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1994-final-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1995-final-virtual-structural-multithread/run b/test/1995-final-virtual-structural-multithread/run
deleted file mode 100755
index e912529..0000000
--- a/test/1995-final-virtual-structural-multithread/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# TODO(b/144168550) This test uses access patterns that can be replaced by
-# iget-object-quick during dex2dex compilation. This breaks the test since the
-# -quick opcode encodes the exact byte offset of fields. Since this test changes
-# the offset this causes problems.
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1995-final-virtual-structural-multithread/run.py b/test/1995-final-virtual-structural-multithread/run.py
new file mode 100644
index 0000000..3fd3061
--- /dev/null
+++ b/test/1995-final-virtual-structural-multithread/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # TODO(b/144168550) This test uses access patterns that can be replaced by
+ # iget-object-quick during dex2dex compilation. This breaks the test since the
+ # -quick opcode encodes the exact byte offset of fields. Since this test changes
+ # the offset this causes problems.
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1995-final-virtual-structural-multithread/src/Main.java b/test/1995-final-virtual-structural-multithread/src/Main.java
index f19358d..f3bf4f6 100644
--- a/test/1995-final-virtual-structural-multithread/src/Main.java
+++ b/test/1995-final-virtual-structural-multithread/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1995.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1995.run();
+ }
}
diff --git a/test/1995-final-virtual-structural-multithread/src/art/Test1995.java b/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
index 7073494..a960c74 100644
--- a/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
+++ b/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
@@ -21,150 +21,153 @@
import java.util.Base64;
import java.util.concurrent.CountDownLatch;
public class Test1995 {
- private static final int NUM_THREADS = 20;
- // Don't perform more than this many repeats per thread to prevent OOMEs
- private static final int TASK_COUNT_LIMIT = 1000;
+ private static final int NUM_THREADS = 20;
+ // Don't perform more than this many repeats per thread to prevent OOMEs
+ private static final int TASK_COUNT_LIMIT = 1000;
- public static final class Transform {
- public String greetingEnglish;
- public Transform() {
- this.greetingEnglish = "Hello";
- }
- public String sayHi() {
- return greetingEnglish + " from " + Thread.currentThread().getName();
- }
- }
-
- /**
- * base64 encoded class/dex file for
- * public static final class Transform {
- * public String greetingEnglish;
- * public String greetingFrench;
- * public String greetingDanish;
- * public String greetingJapanese;
- *
- * public Transform() {
- * this.greetingEnglish = "Hello World";
- * this.greetingFrench = "Bonjour le Monde";
- * this.greetingDanish = "Hej Verden";
- * this.greetingJapanese = "こんにちは世界";
- * }
- * public String sayHi() {
- * return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " + sayHiJapanese() + " from " + Thread.currentThread().getName();
- * }
- * public String sayHiEnglish() {
- * return greetingEnglish;
- * }
- * public String sayHiDanish() {
- * return greetingDanish;
- * }
- * public String sayHiJapanese() {
- * return greetingJapanese;
- * }
- * public String sayHiFrench() {
- * return greetingFrench;
- * }
- * }
- */
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQCsHrUqkb8cYgT2oYN7HlVbeOxJT/kONRvgBgAAcAAAAHhWNBIAAAAAAAAAABwGAAAl" +
-"AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAADoBAAA+AEAAEoD" +
-"AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA" +
-"AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA" +
-"7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAK8FAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO" +
-"AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA" +
-"AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA" +
-"AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA" +
-"BwACABUAAAAHAAAAFgAAAAAAAAARAAAABAAAAAAAAAAQAAAADAYAANUFAAAAAAAABwABAAIAAAAt" +
-"AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF" +
-"BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA" +
-"AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA" +
-"NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU" +
-"EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA" +
-"WxADAA4ACQAOPEtLS0sAEAAOABYADgATAA4AHAAOABkADgAAAAABAAAABQAGIGZyb20gAAIsIAAG" +
-"PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA" +
-"GExhcnQvVGVzdDE5OTUkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5NTsAIkxkYWx2aWsvYW5ub3Rh" +
-"dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
-"dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
-"bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDE5OTUuamF2YQAJVHJhbnNmb3JtAAFWAAth" +
-"Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz" +
-"aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt" +
-"ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph" +
-"cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIs" +
-"Im1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2ZDg4" +
-"YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AB+OBk+OCk+OBq+OBoeOBr+S4lueVjAACAgEi" +
-"GAECAwITBBkbFxEABAEFAAEBAQEBAQEAgYAE7AUBAfgDAQGMBQEBpAUBAbwFAQHUBQAAAAAAAgAA" +
-"AMYFAADMBQAAAAYAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAJQAAAHAAAAACAAAA" +
-"CQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAFAAAADAAAAHgBAAAGAAAAAQAAANgBAAAB" +
-"IAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQDAAACIAAAJQAAAEoDAAAEIAAAAgAAAMYF" +
-"AAAAIAAAAQAAANUFAAADEAAAAgAAAPwFAAAGIAAAAQAAAAwGAAAAEAAAAQAAABwGAAA=");
-
-
- public static void run() throws Exception {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest();
- }
-
- public static final class MyThread extends Thread {
- public MyThread(CountDownLatch delay, int id) {
- super("Thread: " + id);
- this.thr_id = id;
- this.results = new ArrayList<>(TASK_COUNT_LIMIT);
- this.finish = false;
- this.delay = delay;
- }
-
- public void run() {
- delay.countDown();
- while (!finish && results.size() < TASK_COUNT_LIMIT) {
- Transform t = new Transform();
- results.add(t.sayHi());
- }
- }
-
- public void finish() throws Exception {
- finish = true;
- this.join();
- }
-
- public void Check() throws Exception {
- for (String s : results) {
- if (!s.equals("Hello from " + getName()) &&
- !s.equals("Hello, null, null, null from " + getName()) &&
- !s.equals("Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " + getName())) {
- System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
+ public static final class Transform {
+ public String greetingEnglish;
+ public Transform() {
+ this.greetingEnglish = "Hello";
}
- }
+ public String sayHi() {
+ return greetingEnglish + " from " + Thread.currentThread().getName();
+ }
}
- public ArrayList<String> results;
- public volatile boolean finish;
- public int thr_id;
- public CountDownLatch delay;
- }
+ /**
+ * base64 encoded class/dex file for
+ * public static final class Transform {
+ * public String greetingEnglish;
+ * public String greetingFrench;
+ * public String greetingDanish;
+ * public String greetingJapanese;
+ *
+ * public Transform() {
+ * this.greetingEnglish = "Hello World";
+ * this.greetingFrench = "Bonjour le Monde";
+ * this.greetingDanish = "Hej Verden";
+ * this.greetingJapanese = "こんにちは世界";
+ * }
+ * public String sayHi() {
+ * return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " +
+ * sayHiJapanese() + " from " + Thread.currentThread().getName();
+ * }
+ * public String sayHiEnglish() {
+ * return greetingEnglish;
+ * }
+ * public String sayHiDanish() {
+ * return greetingDanish;
+ * }
+ * public String sayHiJapanese() {
+ * return greetingJapanese;
+ * }
+ * public String sayHiFrench() {
+ * return greetingFrench;
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCsHrUqkb8cYgT2oYN7HlVbeOxJT/kONRvgBgAAcAAAAHhWNBIAAAAAAAAAABwGAAAl" +
+ "AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAADoBAAA+AEAAEoD" +
+ "AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA" +
+ "AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA" +
+ "7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAK8FAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO" +
+ "AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA" +
+ "AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA" +
+ "AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA" +
+ "BwACABUAAAAHAAAAFgAAAAAAAAARAAAABAAAAAAAAAAQAAAADAYAANUFAAAAAAAABwABAAIAAAAt" +
+ "AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF" +
+ "BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA" +
+ "AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA" +
+ "NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU" +
+ "EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA" +
+ "WxADAA4ACQAOPEtLS0sAEAAOABYADgATAA4AHAAOABkADgAAAAABAAAABQAGIGZyb20gAAIsIAAG" +
+ "PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA" +
+ "GExhcnQvVGVzdDE5OTUkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5NTsAIkxkYWx2aWsvYW5ub3Rh" +
+ "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
+ "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
+ "bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDE5OTUuamF2YQAJVHJhbnNmb3JtAAFWAAth" +
+ "Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz" +
+ "aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt" +
+ "ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph" +
+ "cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIs" +
+ "Im1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2ZDg4" +
+ "YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AB+OBk+OCk+OBq+OBoeOBr+S4lueVjAACAgEi" +
+ "GAECAwITBBkbFxEABAEFAAEBAQEBAQEAgYAE7AUBAfgDAQGMBQEBpAUBAbwFAQHUBQAAAAAAAgAA" +
+ "AMYFAADMBQAAAAYAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAJQAAAHAAAAACAAAA" +
+ "CQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAFAAAADAAAAHgBAAAGAAAAAQAAANgBAAAB" +
+ "IAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQDAAACIAAAJQAAAEoDAAAEIAAAAgAAAMYF" +
+ "AAAAIAAAAQAAANUFAAADEAAAAgAAAPwFAAAGIAAAAQAAAAwGAAAAEAAAAQAAABwGAAA=");
- public static MyThread[] startThreads(int num_threads) throws Exception {
- CountDownLatch cdl = new CountDownLatch(num_threads);
- MyThread[] res = new MyThread[num_threads];
- for (int i = 0; i < num_threads; i++) {
- res[i] = new MyThread(cdl, i);
- res[i].start();
- }
- cdl.await();
- return res;
- }
- public static void finishThreads(MyThread[] thrs) throws Exception {
- for (MyThread t : thrs) {
- t.finish();
- }
- for (MyThread t : thrs) {
- t.Check();
- }
- }
- public static void doTest() throws Exception {
- MyThread[] threads = startThreads(NUM_THREADS);
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- finishThreads(threads);
- }
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest();
+ }
+
+ public static final class MyThread extends Thread {
+ public MyThread(CountDownLatch delay, int id) {
+ super("Thread: " + id);
+ this.thr_id = id;
+ this.results = new ArrayList<>(TASK_COUNT_LIMIT);
+ this.finish = false;
+ this.delay = delay;
+ }
+
+ public void run() {
+ delay.countDown();
+ while (!finish && results.size() < TASK_COUNT_LIMIT) {
+ Transform t = new Transform();
+ results.add(t.sayHi());
+ }
+ }
+
+ public void finish() throws Exception {
+ finish = true;
+ this.join();
+ }
+
+ public void Check() throws Exception {
+ for (String s : results) {
+ if (!s.equals("Hello from " + getName()) &&
+ !s.equals("Hello, null, null, null from " + getName()) &&
+ !s.equals(
+ "Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " +
+ getName())) {
+ System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
+ }
+ }
+ }
+
+ public ArrayList<String> results;
+ public volatile boolean finish;
+ public int thr_id;
+ public CountDownLatch delay;
+ }
+
+ public static MyThread[] startThreads(int num_threads) throws Exception {
+ CountDownLatch cdl = new CountDownLatch(num_threads);
+ MyThread[] res = new MyThread[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ res[i] = new MyThread(cdl, i);
+ res[i].start();
+ }
+ cdl.await();
+ return res;
+ }
+ public static void finishThreads(MyThread[] thrs) throws Exception {
+ for (MyThread t : thrs) {
+ t.finish();
+ }
+ for (MyThread t : thrs) {
+ t.Check();
+ }
+ }
+
+ public static void doTest() throws Exception {
+ MyThread[] threads = startThreads(NUM_THREADS);
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ finishThreads(threads);
+ }
}
diff --git a/test/1996-final-override-virtual-structural/run b/test/1996-final-override-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1996-final-override-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1996-final-override-virtual-structural/run.py b/test/1996-final-override-virtual-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1996-final-override-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1996-final-override-virtual-structural/src/Main.java b/test/1996-final-override-virtual-structural/src/Main.java
index ade69cf..ff10798 100644
--- a/test/1996-final-override-virtual-structural/src/Main.java
+++ b/test/1996-final-override-virtual-structural/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1996.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1996.run();
+ }
}
diff --git a/test/1996-final-override-virtual-structural/src/art/Test1996.java b/test/1996-final-override-virtual-structural/src/art/Test1996.java
index c2b1125..2649476 100644
--- a/test/1996-final-override-virtual-structural/src/art/Test1996.java
+++ b/test/1996-final-override-virtual-structural/src/art/Test1996.java
@@ -19,76 +19,76 @@
import java.util.Base64;
public class Test1996 {
- public static class SuperTransform {
- public String hiValue = "Hi";
- public String sayHi() {
- return this.hiValue;
+ public static class SuperTransform {
+ public String hiValue = "Hi";
+ public String sayHi() {
+ return this.hiValue;
+ }
}
- }
- public static final class Transform extends SuperTransform {
- public void PostTransform() { }
- public String sayHiTwice(Runnable run) {
- run.run();
- return "super: " + super.sayHi() + " this: " + sayHi();
+ public static final class Transform extends SuperTransform {
+ public void PostTransform() { }
+ public String sayHiTwice(Runnable run) {
+ run.run();
+ return "super: " + super.sayHi() + " this: " + sayHi();
+ }
}
- }
- /**
- * base64 encoded class/dex file for
- * public static final class Transform extends SuperTransform {
- * public String myGreeting;
- * public void PostTransform() {
- * myGreeting = "SALUTATIONS";
- * }
- * public String sayHiTwice(Runnable run) {
- * run.run();
- * return "super: " + super.sayHi() + " and then this: " + sayHi();
- * }
- * public String sayHi() {
- * return myGreeting;
- * }
- * }
- */
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQAO4Dwurw97RcUtfH7np7S5RR8gsJYOfmeABQAAcAAAAHhWNBIAAAAAAAAAALwEAAAc" +
-"AAAAcAAAAAkAAADgAAAABAAAAAQBAAABAAAANAEAAAoAAAA8AQAAAQAAAIwBAADUAwAArAEAAHYC" +
-"AACIAgAAkAIAAJMCAACXAgAAtgIAANACAADgAgAABAMAACQDAAA6AwAATgMAAGkDAAB4AwAAhQMA" +
-"AJQDAACfAwAAogMAAK8DAAC3AwAAwwMAAMkDAADOAwAA1QMAAOEDAADqAwAA9AMAAPsDAAAEAAAA" +
-"BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAABAAAAACAAAABgAAAAAAAAADAAAABgAAAGgCAAAD" +
-"AAAABwAAAHACAAAQAAAACAAAAAAAAAABAAYAEwAAAAAAAwABAAAAAAAAABYAAAABAAMAAQAAAAEA" +
-"AwAMAAAAAQAAABYAAAABAAEAFwAAAAUAAwAVAAAABwADAAEAAAAHAAIAEgAAAAcAAAAZAAAAAQAA" +
-"ABEAAAAAAAAAAAAAAA4AAACsBAAAggQAAAAAAAACAAEAAAAAAFsCAAADAAAAVBAAABEAAAAFAAIA" +
-"AgAAAF8CAAAlAAAAchAGAAQAbxABAAMADARuEAQAAwAMACIBBwBwEAcAAQAaAhgAbiAIACEAbiAI" +
-"AEEAGgQAAG4gCABBAG4gCAABAG4QCQABAAwEEQQAAAEAAQABAAAAUgIAAAQAAABwEAAAAAAOAAIA" +
-"AQAAAAAAVgIAAAUAAAAaAA0AWxAAAA4ACgAOAA0ADksAFAAOABABAA48AAAAAAEAAAAFAAAAAQAA" +
-"AAYAECBhbmQgdGhlbiB0aGlzOiAABjxpbml0PgABTAACTEwAHUxhcnQvVGVzdDE5OTYkU3VwZXJU" +
-"cmFuc2Zvcm07ABhMYXJ0L1Rlc3QxOTk2JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5OTY7ACJMZGFs" +
-"dmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" +
-"bGFzczsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xh" +
-"bmcvU3RyaW5nQnVpbGRlcjsADVBvc3RUcmFuc2Zvcm0AC1NBTFVUQVRJT05TAA1UZXN0MTk5Ni5q" +
-"YXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAAZhcHBlbmQACm15R3JlZXRpbmcABG5hbWUA" +
-"A3J1bgAFc2F5SGkACnNheUhpVHdpY2UAB3N1cGVyOiAACHRvU3RyaW5nAAV2YWx1ZQB2fn5EOHsi" +
-"Y29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2N2Iz" +
-"ODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYifQAC" +
-"AwEaGAICBAIRBBkUFw8AAQEDAAECgYAEoAQDAbgEAQGsAwEBxAMAAAAAAAACAAAAcwQAAHkEAACg" +
-"BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAJAAAA4AAAAAMA" +
-"AAAEAAAABAEAAAQAAAABAAAANAEAAAUAAAAKAAAAPAEAAAYAAAABAAAAjAEAAAEgAAAEAAAArAEA" +
-"AAMgAAAEAAAAUgIAAAEQAAACAAAAaAIAAAIgAAAcAAAAdgIAAAQgAAACAAAAcwQAAAAgAAABAAAA" +
-"ggQAAAMQAAACAAAAnAQAAAYgAAABAAAArAQAAAAQAAABAAAAvAQAAA==");
+ /**
+ * base64 encoded class/dex file for
+ * public static final class Transform extends SuperTransform {
+ * public String myGreeting;
+ * public void PostTransform() {
+ * myGreeting = "SALUTATIONS";
+ * }
+ * public String sayHiTwice(Runnable run) {
+ * run.run();
+ * return "super: " + super.sayHi() + " and then this: " + sayHi();
+ * }
+ * public String sayHi() {
+ * return myGreeting;
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAO4Dwurw97RcUtfH7np7S5RR8gsJYOfmeABQAAcAAAAHhWNBIAAAAAAAAAALwEAAAc" +
+ "AAAAcAAAAAkAAADgAAAABAAAAAQBAAABAAAANAEAAAoAAAA8AQAAAQAAAIwBAADUAwAArAEAAHYC" +
+ "AACIAgAAkAIAAJMCAACXAgAAtgIAANACAADgAgAABAMAACQDAAA6AwAATgMAAGkDAAB4AwAAhQMA" +
+ "AJQDAACfAwAAogMAAK8DAAC3AwAAwwMAAMkDAADOAwAA1QMAAOEDAADqAwAA9AMAAPsDAAAEAAAA" +
+ "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAABAAAAACAAAABgAAAAAAAAADAAAABgAAAGgCAAAD" +
+ "AAAABwAAAHACAAAQAAAACAAAAAAAAAABAAYAEwAAAAAAAwABAAAAAAAAABYAAAABAAMAAQAAAAEA" +
+ "AwAMAAAAAQAAABYAAAABAAEAFwAAAAUAAwAVAAAABwADAAEAAAAHAAIAEgAAAAcAAAAZAAAAAQAA" +
+ "ABEAAAAAAAAAAAAAAA4AAACsBAAAggQAAAAAAAACAAEAAAAAAFsCAAADAAAAVBAAABEAAAAFAAIA" +
+ "AgAAAF8CAAAlAAAAchAGAAQAbxABAAMADARuEAQAAwAMACIBBwBwEAcAAQAaAhgAbiAIACEAbiAI" +
+ "AEEAGgQAAG4gCABBAG4gCAABAG4QCQABAAwEEQQAAAEAAQABAAAAUgIAAAQAAABwEAAAAAAOAAIA" +
+ "AQAAAAAAVgIAAAUAAAAaAA0AWxAAAA4ACgAOAA0ADksAFAAOABABAA48AAAAAAEAAAAFAAAAAQAA" +
+ "AAYAECBhbmQgdGhlbiB0aGlzOiAABjxpbml0PgABTAACTEwAHUxhcnQvVGVzdDE5OTYkU3VwZXJU" +
+ "cmFuc2Zvcm07ABhMYXJ0L1Rlc3QxOTk2JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5OTY7ACJMZGFs" +
+ "dmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" +
+ "bGFzczsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xh" +
+ "bmcvU3RyaW5nQnVpbGRlcjsADVBvc3RUcmFuc2Zvcm0AC1NBTFVUQVRJT05TAA1UZXN0MTk5Ni5q" +
+ "YXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAAZhcHBlbmQACm15R3JlZXRpbmcABG5hbWUA" +
+ "A3J1bgAFc2F5SGkACnNheUhpVHdpY2UAB3N1cGVyOiAACHRvU3RyaW5nAAV2YWx1ZQB2fn5EOHsi" +
+ "Y29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2N2Iz" +
+ "ODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYifQAC" +
+ "AwEaGAICBAIRBBkUFw8AAQEDAAECgYAEoAQDAbgEAQGsAwEBxAMAAAAAAAACAAAAcwQAAHkEAACg" +
+ "BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAJAAAA4AAAAAMA" +
+ "AAAEAAAABAEAAAQAAAABAAAANAEAAAUAAAAKAAAAPAEAAAYAAAABAAAAjAEAAAEgAAAEAAAArAEA" +
+ "AAMgAAAEAAAAUgIAAAEQAAACAAAAaAIAAAIgAAAcAAAAdgIAAAQgAAACAAAAcwQAAAAgAAABAAAA" +
+ "ggQAAAMQAAACAAAAnAQAAAYgAAABAAAArAQAAAAQAAABAAAAvAQAAA==");
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new Transform());
- }
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform());
+ }
- public static void doTest(final Transform t) {
- System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
- System.out.println(t.sayHiTwice(
- () -> {
- System.out.println("Redefining calling class");
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- t.PostTransform();
- }));
- System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
- }
+ public static void doTest(final Transform t) {
+ System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
+ System.out.println(t.sayHiTwice(
+ () -> {
+ System.out.println("Redefining calling class");
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ t.PostTransform();
+ }));
+ System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
+ }
}
diff --git a/test/1997-structural-shadow-method/run b/test/1997-structural-shadow-method/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1997-structural-shadow-method/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1997-structural-shadow-method/run.py b/test/1997-structural-shadow-method/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1997-structural-shadow-method/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1997-structural-shadow-method/src/Main.java b/test/1997-structural-shadow-method/src/Main.java
index 3c9bc85..f6552ee 100644
--- a/test/1997-structural-shadow-method/src/Main.java
+++ b/test/1997-structural-shadow-method/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1997.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1997.run();
+ }
}
diff --git a/test/1997-structural-shadow-method/src/art/Test1997.java b/test/1997-structural-shadow-method/src/art/Test1997.java
index 7309a31..5186518 100644
--- a/test/1997-structural-shadow-method/src/art/Test1997.java
+++ b/test/1997-structural-shadow-method/src/art/Test1997.java
@@ -20,65 +20,63 @@
public class Test1997 {
- public static class SuperTransform {
- // We will be shadowing this function.
- public static void sayHi() {
- System.out.println("Hello!");
+ public static class SuperTransform {
+ // We will be shadowing this function.
+ public static void sayHi() {
+ System.out.println("Hello!");
+ }
}
- }
- // The class we will be transforming.
- public static class Transform extends SuperTransform {
- public static void sayHiTwice() {
- Transform.sayHi();
- Transform.sayHi();
+ // The class we will be transforming.
+ public static class Transform extends SuperTransform {
+ public static void sayHiTwice() {
+ Transform.sayHi();
+ Transform.sayHi();
+ }
}
- }
- // public static class Transform extends SuperTransform {
- // public static void sayHiTwice() {
- // Transform.sayHi();
- // Transform.sayHi();
- // }
- // public static void sayHi() {
- // System.out.println("Hello World!");
- // }
- // }
- private static final byte[] DEX_BYTES =
- Base64.getDecoder()
- .decode(
- "ZGV4CjAzNQA9wdy7Lgbrv+sD+wixborREr0maZCK5yqABAAAcAAAAHhWNBIAAAAAAAAAALwDAAAW"
- + "AAAAcAAAAAkAAADIAAAAAgAAAOwAAAABAAAABAEAAAUAAAAMAQAAAQAAADQBAAAsAwAAVAEAAMIB"
- + "AADKAQAA2AEAAPcBAAARAgAAIQIAAEUCAABlAgAAfAIAAJACAACkAgAAswIAAL4CAADBAgAAxQIA"
- + "ANICAADYAgAA3QIAAOYCAADtAgAA+QIAAAADAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA"
- + "CQAAAAwAAAAMAAAACAAAAAAAAAANAAAACAAAALwBAAAHAAUAEAAAAAAAAAAAAAAAAQAAAAAAAAAB"
- + "AAAAEgAAAAEAAAATAAAABQABABEAAAABAAAAAQAAAAAAAAAAAAAACgAAAKwDAACHAwAAAAAAAAEA"
- + "AQABAAAAqgEAAAQAAABwEAAAAAAOAAIAAAACAAAArgEAAAgAAABiAAAAGgEBAG4gBAAQAA4AAAAA"
- + "AAAAAACzAQAABwAAAHEAAgAAAHEAAgAAAA4ADwAOABUADngAEQAOPDwAAAAAAQAAAAYABjxpbml0"
- + "PgAMSGVsbG8gV29ybGQhAB1MYXJ0L1Rlc3QxOTk3JFN1cGVyVHJhbnNmb3JtOwAYTGFydC9UZXN0"
- + "MTk5NyRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTk3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xv"
- + "c2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9Qcmlu"
- + "dFN0cmVhbTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AA1UZXN0MTk5"
- + "Ny5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxu"
- + "AAVzYXlIaQAKc2F5SGlUd2ljZQAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1"
- + "ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2"
- + "ZDg4YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AAgMBFBgCAgQCDgQJDxcLAAADAAGBgATU"
- + "AgEJ7AIBCYwDAAAAAAAAAAIAAAB4AwAAfgMAAKADAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAA"
- + "AAAAAQAAABYAAABwAAAAAgAAAAkAAADIAAAAAwAAAAIAAADsAAAABAAAAAEAAAAEAQAABQAAAAUA"
- + "AAAMAQAABgAAAAEAAAA0AQAAASAAAAMAAABUAQAAAyAAAAMAAACqAQAAARAAAAEAAAC8AQAAAiAA"
- + "ABYAAADCAQAABCAAAAIAAAB4AwAAACAAAAEAAACHAwAAAxAAAAIAAACcAwAABiAAAAEAAACsAwAA"
- + "ABAAAAEAAAC8AwAA");
+ // public static class Transform extends SuperTransform {
+ // public static void sayHiTwice() {
+ // Transform.sayHi();
+ // Transform.sayHi();
+ // }
+ // public static void sayHi() {
+ // System.out.println("Hello World!");
+ // }
+ // }
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQA9wdy7Lgbrv+sD+wixborREr0maZCK5yqABAAAcAAAAHhWNBIAAAAAAAAAALwDAAAW"
+ + "AAAAcAAAAAkAAADIAAAAAgAAAOwAAAABAAAABAEAAAUAAAAMAQAAAQAAADQBAAAsAwAAVAEAAMIB"
+ + "AADKAQAA2AEAAPcBAAARAgAAIQIAAEUCAABlAgAAfAIAAJACAACkAgAAswIAAL4CAADBAgAAxQIA"
+ + "ANICAADYAgAA3QIAAOYCAADtAgAA+QIAAAADAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA"
+ + "CQAAAAwAAAAMAAAACAAAAAAAAAANAAAACAAAALwBAAAHAAUAEAAAAAAAAAAAAAAAAQAAAAAAAAAB"
+ + "AAAAEgAAAAEAAAATAAAABQABABEAAAABAAAAAQAAAAAAAAAAAAAACgAAAKwDAACHAwAAAAAAAAEA"
+ + "AQABAAAAqgEAAAQAAABwEAAAAAAOAAIAAAACAAAArgEAAAgAAABiAAAAGgEBAG4gBAAQAA4AAAAA"
+ + "AAAAAACzAQAABwAAAHEAAgAAAHEAAgAAAA4ADwAOABUADngAEQAOPDwAAAAAAQAAAAYABjxpbml0"
+ + "PgAMSGVsbG8gV29ybGQhAB1MYXJ0L1Rlc3QxOTk3JFN1cGVyVHJhbnNmb3JtOwAYTGFydC9UZXN0"
+ + "MTk5NyRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTk3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xv"
+ + "c2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9Qcmlu"
+ + "dFN0cmVhbTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AA1UZXN0MTk5"
+ + "Ny5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxu"
+ + "AAVzYXlIaQAKc2F5SGlUd2ljZQAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1"
+ + "ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2"
+ + "ZDg4YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AAgMBFBgCAgQCDgQJDxcLAAADAAGBgATU"
+ + "AgEJ7AIBCYwDAAAAAAAAAAIAAAB4AwAAfgMAAKADAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAA"
+ + "AAAAAQAAABYAAABwAAAAAgAAAAkAAADIAAAAAwAAAAIAAADsAAAABAAAAAEAAAAEAQAABQAAAAUA"
+ + "AAAMAQAABgAAAAEAAAA0AQAAASAAAAMAAABUAQAAAyAAAAMAAACqAQAAARAAAAEAAAC8AQAAAiAA"
+ + "ABYAAADCAQAABCAAAAIAAAB4AwAAACAAAAEAAACHAwAAAxAAAAIAAACcAwAABiAAAAEAAACsAwAA"
+ + "ABAAAAEAAAC8AwAA");
- public static void run() throws Exception {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest();
- }
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest();
+ }
- public static void doTest() throws Exception {
- Transform.sayHiTwice();
- Transform.sayHi();
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- Transform.sayHiTwice();
- Transform.sayHi();
- }
+ public static void doTest() throws Exception {
+ Transform.sayHiTwice();
+ Transform.sayHi();
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ Transform.sayHiTwice();
+ Transform.sayHi();
+ }
}
diff --git a/test/1998-structural-shadow-field/run b/test/1998-structural-shadow-field/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1998-structural-shadow-field/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1998-structural-shadow-field/run.py b/test/1998-structural-shadow-field/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1998-structural-shadow-field/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1998-structural-shadow-field/src/Main.java b/test/1998-structural-shadow-field/src/Main.java
index f6aeca5..e35d1b1 100644
--- a/test/1998-structural-shadow-field/src/Main.java
+++ b/test/1998-structural-shadow-field/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1998.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1998.run();
+ }
}
diff --git a/test/1998-structural-shadow-field/src/art/Test1998.java b/test/1998-structural-shadow-field/src/art/Test1998.java
index 3fda936..3aaa9f3 100644
--- a/test/1998-structural-shadow-field/src/art/Test1998.java
+++ b/test/1998-structural-shadow-field/src/art/Test1998.java
@@ -20,46 +20,44 @@
public class Test1998 {
- public static class SuperTransform {
- public static String greeting = "Hello";
- }
+ public static class SuperTransform {
+ public static String greeting = "Hello";
+ }
- // The class we will be transforming.
- public static class Transform extends SuperTransform { }
+ // The class we will be transforming.
+ public static class Transform extends SuperTransform { }
- // public static class Transform extends SuperTransform {
- // public static String greeting;
- // }
- private static final byte[] DEX_BYTES =
- Base64.getDecoder()
- .decode(
-"ZGV4CjAzNQCYmnoWz4BqygrZQM4zf/mJ/25+dM86MHKAAwAAcAAAAHhWNBIAAAAAAAAAAMgCAAAP" +
-"AAAAcAAAAAcAAACsAAAAAQAAAMgAAAABAAAA1AAAAAIAAADcAAAAAQAAAOwAAAB0AgAADAEAACgB" +
-"AAAwAQAATwEAAGkBAAB5AQAAnQEAAL0BAADRAQAA4AEAAOsBAADuAQAA+wEAAAUCAAALAgAAEgIA" +
-"AAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAJAAAABgAAAAAAAAABAAUACwAAAAAAAAAAAAAA" +
-"AQAAAAAAAAABAAAAAQAAAAAAAAAAAAAABwAAALgCAACZAgAAAAAAAAEAAQABAAAAJAEAAAQAAABw" +
-"EAAAAAAOAAUADgAGPGluaXQ+AB1MYXJ0L1Rlc3QxOTk4JFN1cGVyVHJhbnNmb3JtOwAYTGFydC9U" +
-"ZXN0MTk5OCRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTk4OwAiTGRhbHZpay9hbm5vdGF0aW9uL0Vu" +
-"Y2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5n" +
-"L1N0cmluZzsADVRlc3QxOTk4LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MACGdyZWV0" +
-"aW5nAARuYW1lAAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFw" +
-"aSI6MSwic2hhLTEiOiI2MGRhNGQ2N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwi" +
-"dmVyc2lvbiI6IjEuNy4xMi1kZXYifQACAwENGAICBAIKBAkMFwgBAAEAAAkBgYAEjAIAAAAAAAAA" +
-"AgAAAIoCAACQAgAArAIAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAAC" +
-"AAAABwAAAKwAAAADAAAAAQAAAMgAAAAEAAAAAQAAANQAAAAFAAAAAgAAANwAAAAGAAAAAQAAAOwA" +
-"AAABIAAAAQAAAAwBAAADIAAAAQAAACQBAAACIAAADwAAACgBAAAEIAAAAgAAAIoCAAAAIAAAAQAA" +
-"AJkCAAADEAAAAgAAAKgCAAAGIAAAAQAAALgCAAAAEAAAAQAAAMgCAAA=");
+ // public static class Transform extends SuperTransform {
+ // public static String greeting;
+ // }
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCYmnoWz4BqygrZQM4zf/mJ/25+dM86MHKAAwAAcAAAAHhWNBIAAAAAAAAAAMgCAAAP" +
+ "AAAAcAAAAAcAAACsAAAAAQAAAMgAAAABAAAA1AAAAAIAAADcAAAAAQAAAOwAAAB0AgAADAEAACgB" +
+ "AAAwAQAATwEAAGkBAAB5AQAAnQEAAL0BAADRAQAA4AEAAOsBAADuAQAA+wEAAAUCAAALAgAAEgIA" +
+ "AAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAJAAAABgAAAAAAAAABAAUACwAAAAAAAAAAAAAA" +
+ "AQAAAAAAAAABAAAAAQAAAAAAAAAAAAAABwAAALgCAACZAgAAAAAAAAEAAQABAAAAJAEAAAQAAABw" +
+ "EAAAAAAOAAUADgAGPGluaXQ+AB1MYXJ0L1Rlc3QxOTk4JFN1cGVyVHJhbnNmb3JtOwAYTGFydC9U" +
+ "ZXN0MTk5OCRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTk4OwAiTGRhbHZpay9hbm5vdGF0aW9uL0Vu" +
+ "Y2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5n" +
+ "L1N0cmluZzsADVRlc3QxOTk4LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MACGdyZWV0" +
+ "aW5nAARuYW1lAAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFw" +
+ "aSI6MSwic2hhLTEiOiI2MGRhNGQ2N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwi" +
+ "dmVyc2lvbiI6IjEuNy4xMi1kZXYifQACAwENGAICBAIKBAkMFwgBAAEAAAkBgYAEjAIAAAAAAAAA" +
+ "AgAAAIoCAACQAgAArAIAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAAC" +
+ "AAAABwAAAKwAAAADAAAAAQAAAMgAAAAEAAAAAQAAANQAAAAFAAAAAgAAANwAAAAGAAAAAQAAAOwA" +
+ "AAABIAAAAQAAAAwBAAADIAAAAQAAACQBAAACIAAADwAAACgBAAAEIAAAAgAAAIoCAAAAIAAAAQAA" +
+ "AJkCAAADEAAAAgAAAKgCAAAGIAAAAQAAALgCAAAAEAAAAQAAAMgCAAA=");
- public static void run() throws Exception {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest();
- }
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest();
+ }
- public static void doTest() throws Exception {
- System.out.println(Transform.greeting);
- System.out.println(SuperTransform.greeting);
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- System.out.println(Transform.greeting);
- System.out.println(SuperTransform.greeting);
- }
+ public static void doTest() throws Exception {
+ System.out.println(Transform.greeting);
+ System.out.println(SuperTransform.greeting);
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ System.out.println(Transform.greeting);
+ System.out.println(SuperTransform.greeting);
+ }
}
diff --git a/test/1999-virtual-structural/run b/test/1999-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1999-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1999-virtual-structural/run.py b/test/1999-virtual-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1999-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1999-virtual-structural/src/Main.java b/test/1999-virtual-structural/src/Main.java
index 86a492b..f4a1bd6 100644
--- a/test/1999-virtual-structural/src/Main.java
+++ b/test/1999-virtual-structural/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1999.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1999.run();
+ }
}
diff --git a/test/1999-virtual-structural/src/art/Test1999.java b/test/1999-virtual-structural/src/art/Test1999.java
index f6811a9..6943442 100644
--- a/test/1999-virtual-structural/src/art/Test1999.java
+++ b/test/1999-virtual-structural/src/art/Test1999.java
@@ -19,67 +19,67 @@
import java.util.Base64;
public class Test1999 {
- public static class Transform {
- public String getGreeting() {
- return "Hi";
+ public static class Transform {
+ public String getGreeting() {
+ return "Hi";
+ }
}
- }
- public static class SubTransform extends Transform {
- private int count = 0;
- public void sayHi() {
- System.out.println(getGreeting() + "(SubTransform called " + (++count) + " times)");
+ public static class SubTransform extends Transform {
+ private int count = 0;
+ public void sayHi() {
+ System.out.println(getGreeting() + "(SubTransform called " + (++count) + " times)");
+ }
}
- }
- /**
- * base64 encoded class/dex file for
- * public static class Transform {
- * private int count;
- * public String getGreeting() {
- * return "Hello (Transform called " + incrCount() + " times)";
- * }
- * protected int incrCount() {
- * return ++count;
- * }
- * }
- */
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQAwwbMpPdPdWkU+6UJnvqa7v4VBdcuq2vkMBQAAcAAAAHhWNBIAAAAAAAAAAEgEAAAa" +
-"AAAAcAAAAAkAAADYAAAABQAAAPwAAAABAAAAOAEAAAgAAABAAQAAAQAAAIABAABsAwAAoAEAADoC" +
-"AABDAgAASwIAAGUCAABoAgAAawIAAG8CAABzAgAAjQIAAJ0CAADBAgAA4QIAAPUCAAAJAwAAJAMA" +
-"ADMDAAA+AwAAQQMAAE4DAABWAwAAXQMAAGoDAAB1AwAAewMAAIUDAACMAwAAAwAAAAcAAAAIAAAA" +
-"CQAAAAoAAAALAAAADAAAAA0AAAAQAAAAAwAAAAAAAAAAAAAABAAAAAYAAAAAAAAABQAAAAcAAAAs" +
-"AgAABgAAAAcAAAA0AgAAEAAAAAgAAAAAAAAAAQAAABMAAAABAAQAAQAAAAEAAQAUAAAAAQAAABUA" +
-"AAAFAAQAAQAAAAcABAABAAAABwACABIAAAAHAAMAEgAAAAcAAQAXAAAAAQAAAAEAAAAFAAAAAAAA" +
-"AA4AAAA4BAAAEwQAAAAAAAACAAEAAAAAACgCAAAHAAAAUhAAANgAAAFZEAAADwAAAAQAAQACAAAA" +
-"JAIAABsAAABuEAIAAwAKACIBBwBwEAQAAQAaAgIAbiAGACEAbiAFAAEAGgAAAG4gBgABAG4QBwAB" +
-"AAwAEQAAAAEAAQABAAAAIAIAAAQAAABwEAMAAAAOAAMADgAGAA4ACQAOAAEAAAAAAAAAAQAAAAYA" +
-"ByB0aW1lcykABjxpbml0PgAYSGVsbG8gKFRyYW5zZm9ybSBjYWxsZWQgAAFJAAFMAAJMSQACTEwA" +
-"GExhcnQvVGVzdDE5OTkkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5OTsAIkxkYWx2aWsvYW5ub3Rh" +
-"dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
-"dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
-"bGRlcjsADVRlc3QxOTk5LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MABmFwcGVuZAAF" +
-"Y291bnQAC2dldEdyZWV0aW5nAAlpbmNyQ291bnQABG5hbWUACHRvU3RyaW5nAAV2YWx1ZQB2fn5E" +
-"OHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2" +
-"N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYi" +
-"fQACAwEYGAICBAIRBAkWFw8AAQECAAIAgYAEiAQBAcADAQSgAwAAAAAAAgAAAAQEAAAKBAAALAQA" +
-"AAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAGgAAAHAAAAACAAAACQAAANgAAAADAAAA" +
-"BQAAAPwAAAAEAAAAAQAAADgBAAAFAAAACAAAAEABAAAGAAAAAQAAAIABAAABIAAAAwAAAKABAAAD" +
-"IAAAAwAAACACAAABEAAAAgAAACwCAAACIAAAGgAAADoCAAAEIAAAAgAAAAQEAAAAIAAAAQAAABME" +
-"AAADEAAAAgAAACgEAAAGIAAAAQAAADgEAAAAEAAAAQAAAEgEAAA=");
+ /**
+ * base64 encoded class/dex file for
+ * public static class Transform {
+ * private int count;
+ * public String getGreeting() {
+ * return "Hello (Transform called " + incrCount() + " times)";
+ * }
+ * protected int incrCount() {
+ * return ++count;
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAwwbMpPdPdWkU+6UJnvqa7v4VBdcuq2vkMBQAAcAAAAHhWNBIAAAAAAAAAAEgEAAAa" +
+ "AAAAcAAAAAkAAADYAAAABQAAAPwAAAABAAAAOAEAAAgAAABAAQAAAQAAAIABAABsAwAAoAEAADoC" +
+ "AABDAgAASwIAAGUCAABoAgAAawIAAG8CAABzAgAAjQIAAJ0CAADBAgAA4QIAAPUCAAAJAwAAJAMA" +
+ "ADMDAAA+AwAAQQMAAE4DAABWAwAAXQMAAGoDAAB1AwAAewMAAIUDAACMAwAAAwAAAAcAAAAIAAAA" +
+ "CQAAAAoAAAALAAAADAAAAA0AAAAQAAAAAwAAAAAAAAAAAAAABAAAAAYAAAAAAAAABQAAAAcAAAAs" +
+ "AgAABgAAAAcAAAA0AgAAEAAAAAgAAAAAAAAAAQAAABMAAAABAAQAAQAAAAEAAQAUAAAAAQAAABUA" +
+ "AAAFAAQAAQAAAAcABAABAAAABwACABIAAAAHAAMAEgAAAAcAAQAXAAAAAQAAAAEAAAAFAAAAAAAA" +
+ "AA4AAAA4BAAAEwQAAAAAAAACAAEAAAAAACgCAAAHAAAAUhAAANgAAAFZEAAADwAAAAQAAQACAAAA" +
+ "JAIAABsAAABuEAIAAwAKACIBBwBwEAQAAQAaAgIAbiAGACEAbiAFAAEAGgAAAG4gBgABAG4QBwAB" +
+ "AAwAEQAAAAEAAQABAAAAIAIAAAQAAABwEAMAAAAOAAMADgAGAA4ACQAOAAEAAAAAAAAAAQAAAAYA" +
+ "ByB0aW1lcykABjxpbml0PgAYSGVsbG8gKFRyYW5zZm9ybSBjYWxsZWQgAAFJAAFMAAJMSQACTEwA" +
+ "GExhcnQvVGVzdDE5OTkkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5OTsAIkxkYWx2aWsvYW5ub3Rh" +
+ "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
+ "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
+ "bGRlcjsADVRlc3QxOTk5LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MABmFwcGVuZAAF" +
+ "Y291bnQAC2dldEdyZWV0aW5nAAlpbmNyQ291bnQABG5hbWUACHRvU3RyaW5nAAV2YWx1ZQB2fn5E" +
+ "OHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2" +
+ "N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYi" +
+ "fQACAwEYGAICBAIRBAkWFw8AAQECAAIAgYAEiAQBAcADAQSgAwAAAAAAAgAAAAQEAAAKBAAALAQA" +
+ "AAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAGgAAAHAAAAACAAAACQAAANgAAAADAAAA" +
+ "BQAAAPwAAAAEAAAAAQAAADgBAAAFAAAACAAAAEABAAAGAAAAAQAAAIABAAABIAAAAwAAAKABAAAD" +
+ "IAAAAwAAACACAAABEAAAAgAAACwCAAACIAAAGgAAADoCAAAEIAAAAgAAAAQEAAAAIAAAAQAAABME" +
+ "AAADEAAAAgAAACgEAAAGIAAAAQAAADgEAAAAEAAAAQAAAEgEAAA=");
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new SubTransform());
- }
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new SubTransform());
+ }
- public static void doTest(SubTransform t) {
- t.sayHi();
- t.sayHi();
- t.sayHi();
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- t.sayHi();
- }
+ public static void doTest(SubTransform t) {
+ t.sayHi();
+ t.sayHi();
+ t.sayHi();
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ t.sayHi();
+ }
}
diff --git a/test/2000-virtual-list-structural/build b/test/2000-virtual-list-structural/build
deleted file mode 100755
index f8496bec..0000000
--- a/test/2000-virtual-list-structural/build
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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 on failure.
-set -e
-
-# Deref the symlink.
-mv src-ex/java/util/AbstractCollection.java src-ex/java/util/AbstractCollection.bak
-cp src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java
-
-# Patch the copied version.
-patch src-ex/java/util/AbstractCollection.java AbstractCollection.patch
-
-USE_DESUGAR=false ./default-build "$@"
-
-# restore the symlink
-rm src-ex/java/util/AbstractCollection.java
-mv src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java
diff --git a/test/2000-virtual-list-structural/build.py b/test/2000-virtual-list-structural/build.py
new file mode 100644
index 0000000..4791995
--- /dev/null
+++ b/test/2000-virtual-list-structural/build.py
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(use_desugar=False)
+
+ os.rename(ctx.test_dir / "src-ex/java/util/AbstractCollection.bak",
+ ctx.test_dir / "src-ex/java/util/AbstractCollection.java")
diff --git a/test/2000-virtual-list-structural/generate-sources b/test/2000-virtual-list-structural/generate-sources
new file mode 100755
index 0000000..5857b40
--- /dev/null
+++ b/test/2000-virtual-list-structural/generate-sources
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2019 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 on failure.
+set -e
+
+# Deref the symlink.
+mv src-ex/java/util/AbstractCollection.java src-ex/java/util/AbstractCollection.bak
+cp src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java
+
+# Patch the copied version.
+patch -s src-ex/java/util/AbstractCollection.java AbstractCollection.patch
diff --git a/test/2000-virtual-list-structural/run b/test/2000-virtual-list-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2000-virtual-list-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2000-virtual-list-structural/run.py b/test/2000-virtual-list-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2000-virtual-list-structural/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2001-virtual-structural-multithread/run b/test/2001-virtual-structural-multithread/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2001-virtual-structural-multithread/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2001-virtual-structural-multithread/run.py b/test/2001-virtual-structural-multithread/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2001-virtual-structural-multithread/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2002-virtual-structural-initializing/run b/test/2002-virtual-structural-initializing/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2002-virtual-structural-initializing/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2002-virtual-structural-initializing/run.py b/test/2002-virtual-structural-initializing/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2002-virtual-structural-initializing/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2003-double-virtual-structural/run b/test/2003-double-virtual-structural/run
deleted file mode 100755
index b59f97c..0000000
--- a/test/2003-double-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2003-double-virtual-structural/run.py b/test/2003-double-virtual-structural/run.py
new file mode 100644
index 0000000..882774b
--- /dev/null
+++ b/test/2003-double-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2004-double-virtual-structural-abstract/run b/test/2004-double-virtual-structural-abstract/run
deleted file mode 100755
index b59f97c..0000000
--- a/test/2004-double-virtual-structural-abstract/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2004-double-virtual-structural-abstract/run.py b/test/2004-double-virtual-structural-abstract/run.py
new file mode 100644
index 0000000..882774b
--- /dev/null
+++ b/test/2004-double-virtual-structural-abstract/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2005-pause-all-redefine-multithreaded/build.py b/test/2005-pause-all-redefine-multithreaded/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2005-pause-all-redefine-multithreaded/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2005-pause-all-redefine-multithreaded/pause-all.cc b/test/2005-pause-all-redefine-multithreaded/pause-all.cc
index 9928411..37d6c4d 100644
--- a/test/2005-pause-all-redefine-multithreaded/pause-all.cc
+++ b/test/2005-pause-all-redefine-multithreaded/pause-all.cc
@@ -41,10 +41,12 @@
jobjectArray new_fields,
jstring default_val) {
std::vector<jthread> threads;
+ threads.reserve(env->GetArrayLength(threads_arr));
for (jint i = 0; i < env->GetArrayLength(threads_arr); i++) {
threads.push_back(env->GetObjectArrayElement(threads_arr, i));
}
std::vector<jfieldID> fields;
+ fields.reserve(env->GetArrayLength(new_fields));
for (jint i = 0; i < env->GetArrayLength(new_fields); i++) {
fields.push_back(env->FromReflectedField(env->GetObjectArrayElement(new_fields, i)));
}
diff --git a/test/2005-pause-all-redefine-multithreaded/run b/test/2005-pause-all-redefine-multithreaded/run
deleted file mode 100755
index b59f97c..0000000
--- a/test/2005-pause-all-redefine-multithreaded/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2005-pause-all-redefine-multithreaded/run.py b/test/2005-pause-all-redefine-multithreaded/run.py
new file mode 100644
index 0000000..882774b
--- /dev/null
+++ b/test/2005-pause-all-redefine-multithreaded/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2005-pause-all-redefine-multithreaded/test-metadata.json b/test/2005-pause-all-redefine-multithreaded/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2005-pause-all-redefine-multithreaded/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2006-virtual-structural-finalizing/run b/test/2006-virtual-structural-finalizing/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2006-virtual-structural-finalizing/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2006-virtual-structural-finalizing/run.py b/test/2006-virtual-structural-finalizing/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2007-virtual-structural-finalizable/run b/test/2007-virtual-structural-finalizable/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2007-virtual-structural-finalizable/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2007-virtual-structural-finalizable/run.py b/test/2007-virtual-structural-finalizable/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2008-redefine-then-old-reflect-field/run b/test/2008-redefine-then-old-reflect-field/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/2008-redefine-then-old-reflect-field/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/2008-redefine-then-old-reflect-field/run.py b/test/2008-redefine-then-old-reflect-field/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/2008-redefine-then-old-reflect-field/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/2009-structural-local-ref/run b/test/2009-structural-local-ref/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2009-structural-local-ref/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2009-structural-local-ref/run.py b/test/2009-structural-local-ref/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2009-structural-local-ref/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc b/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
index 5eaaa05..78bb772 100644
--- a/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
+++ b/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
@@ -89,9 +89,8 @@
ScopedSuspendAll ssa(__FUNCTION__);
Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(other,
/* deopt_all_frames= */ false);
- MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_);
- bool updated = other->ModifySuspendCount(Thread::Current(), -1, nullptr, SuspendReason::kInternal);
- CHECK(updated);
+ bool resumed = art::Runtime::Current()->GetThreadList()->Resume(other, SuspendReason::kInternal);
+ CHECK(resumed);
instrumented = true;
return;
}
diff --git a/test/2012-structural-redefinition-failures-jni-id/run b/test/2012-structural-redefinition-failures-jni-id/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2012-structural-redefinition-failures-jni-id/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2012-structural-redefinition-failures-jni-id/run.py b/test/2012-structural-redefinition-failures-jni-id/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2012-structural-redefinition-failures-jni-id/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2031-zygote-compiled-frame-deopt/native-wait.cc b/test/2031-zygote-compiled-frame-deopt/native-wait.cc
index bd1d224..fb45345 100644
--- a/test/2031-zygote-compiled-frame-deopt/native-wait.cc
+++ b/test/2031-zygote-compiled-frame-deopt/native-wait.cc
@@ -42,7 +42,7 @@
}
runtime->SetAsZygoteChild(/*is_system_server=*/false, /*is_zygote=*/false);
runtime->AddCompilerOption("--debuggable");
- runtime->SetJavaDebuggable(true);
+ runtime->SetRuntimeDebugState(Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
{
// Deoptimize the boot image as it may be non-debuggable.
ScopedSuspendAll ssa(__FUNCTION__);
diff --git a/test/2031-zygote-compiled-frame-deopt/run b/test/2031-zygote-compiled-frame-deopt/run
deleted file mode 100755
index 900099f..0000000
--- a/test/2031-zygote-compiled-frame-deopt/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-# The -Xopaque-jni-ids makes sure we can do structural redefinition. The --add-libdir-argument tells
-# default-run to pass the directory where the jvmti-agent is so we can load it later. The others
-# set the process to zygote mode and setup the jit cache size. We use a larger than normal jit-size
-# to avoid having to deal with jit-gc, a complication that's not relevant to this test.
-./default-run "$@" --runtime-option -Xopaque-jni-ids:true --add-libdir-argument --runtime-option -Xzygote --runtime-option -Xjitinitialsize:64M
diff --git a/test/2031-zygote-compiled-frame-deopt/run.py b/test/2031-zygote-compiled-frame-deopt/run.py
new file mode 100644
index 0000000..1dedf3b
--- /dev/null
+++ b/test/2031-zygote-compiled-frame-deopt/run.py
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+
+
+def run(ctx, args):
+ # The -Xopaque-jni-ids makes sure we can do structural redefinition. The --add-libdir-argument tells
+ # default-run to pass the directory where the jvmti-agent is so we can load it later. The others
+ # set the process to zygote mode and setup the jit cache size. We use a larger than normal jit-size
+ # to avoid having to deal with jit-gc, a complication that's not relevant to this test.
+ ctx.default_run(
+ args,
+ runtime_option=[
+ "-Xopaque-jni-ids:true", "-Xzygote", "-Xjitinitialsize:64M"
+ ],
+ add_libdir_argument=True)
diff --git a/test/2033-shutdown-mechanics/check b/test/2033-shutdown-mechanics/check
deleted file mode 100755
index 9523159..0000000
--- a/test/2033-shutdown-mechanics/check
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The daemon thread seems to occasionally get stopped before finishing.
-# Check that the actual output is a line-by-line prefix of expected.
-head -n $(wc -l < $2) $1 | diff --strip-trailing-cr -q - "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/2033-shutdown-mechanics/run.py b/test/2033-shutdown-mechanics/run.py
new file mode 100644
index 0000000..3dfc965
--- /dev/null
+++ b/test/2033-shutdown-mechanics/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # The daemon thread seems to occasionally get stopped before finishing.
+ # Check that the actual output is a line-by-line prefix of expected.
+ ctx.run(
+ fr"head -n $(wc -l < '{args.stdout_file}') expected-stdout.txt > expected-stdout.txt.tmp &&"
+ fr"mv expected-stdout.txt.tmp expected-stdout.txt")
diff --git a/test/2034-spaces-in-SimpleName/build b/test/2034-spaces-in-SimpleName/build
deleted file mode 100755
index 8261ed2..0000000
--- a/test/2034-spaces-in-SimpleName/build
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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 on failure and be verbose.
-set -e -x
-
-export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
-
-# generate Java bytecode with ASM
-cd src_gen
-${JAVA:-java} -cp "$ASM_JAR:." SpacesInSimpleName.java
-mkdir ../classes && mv Main.class ../classes/Main.class
-cd ..
-
-# Use API level 10000 for spaces in SimpleName
-USE_DESUGAR=false ./default-build "$@" --api-level 10000
diff --git a/test/2034-spaces-in-SimpleName/build.py b/test/2034-spaces-in-SimpleName/build.py
new file mode 100644
index 0000000..c392e24
--- /dev/null
+++ b/test/2034-spaces-in-SimpleName/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Use API level 10000 for spaces in SimpleName
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(use_desugar=False, api_level=10000)
diff --git a/test/2034-spaces-in-SimpleName/generate-sources b/test/2034-spaces-in-SimpleName/generate-sources
new file mode 100755
index 0000000..2e921a7
--- /dev/null
+++ b/test/2034-spaces-in-SimpleName/generate-sources
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 2019 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 on failure
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# generate Java bytecode with ASM
+cd src_gen
+${JAVA:-java} -cp "$ASM_JAR:." SpacesInSimpleName.java
+mkdir ../classes && mv Main.class ../classes/Main.class
+cd ..
diff --git a/test/2035-structural-native-method/run b/test/2035-structural-native-method/run
deleted file mode 100755
index ff387ff..0000000
--- a/test/2035-structural-native-method/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2035-structural-native-method/run.py b/test/2035-structural-native-method/run.py
new file mode 100644
index 0000000..3774a6c
--- /dev/null
+++ b/test/2035-structural-native-method/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2036-structural-subclass-shadow/run b/test/2036-structural-subclass-shadow/run
deleted file mode 100755
index ff387ff..0000000
--- a/test/2036-structural-subclass-shadow/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2036-structural-subclass-shadow/run.py b/test/2036-structural-subclass-shadow/run.py
new file mode 100644
index 0000000..3774a6c
--- /dev/null
+++ b/test/2036-structural-subclass-shadow/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2038-hiddenapi-jvmti-ext/build b/test/2038-hiddenapi-jvmti-ext/build
deleted file mode 100644
index f4b029f..0000000
--- a/test/2038-hiddenapi-jvmti-ext/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/2038-hiddenapi-jvmti-ext/build.py b/test/2038-hiddenapi-jvmti-ext/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/2038-hiddenapi-jvmti-ext/run b/test/2038-hiddenapi-jvmti-ext/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/2038-hiddenapi-jvmti-ext/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/2038-hiddenapi-jvmti-ext/run.py b/test/2038-hiddenapi-jvmti-ext/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/2039-load-transform-larger/run b/test/2039-load-transform-larger/run
deleted file mode 100755
index 144c17d..0000000
--- a/test/2039-load-transform-larger/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-./default-run "$@" --jvmti --no-app-image
diff --git a/test/2039-load-transform-larger/run.py b/test/2039-load-transform-larger/run.py
new file mode 100644
index 0000000..157a24b
--- /dev/null
+++ b/test/2039-load-transform-larger/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2021 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/2040-huge-native-alloc/build.py b/test/2040-huge-native-alloc/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2040-huge-native-alloc/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2040-huge-native-alloc/src/Main.java b/test/2040-huge-native-alloc/src/Main.java
index 59e1266..3c8ae23 100644
--- a/test/2040-huge-native-alloc/src/Main.java
+++ b/test/2040-huge-native-alloc/src/Main.java
@@ -24,7 +24,7 @@
int allocated = 0;
int deallocated = 0;
static Object lock = new Object();
- final static int MAX_TRIES = 4;
+ final static int MAX_TRIES = 10;
WeakReference<BufferHolder>[] references = new WeakReference[HOW_MANY_HUGE];
class BufferHolder {
@@ -61,6 +61,14 @@
if (new Main().tryToRun(i == MAX_TRIES)) {
break;
}
+ if (i == MAX_TRIES / 2) {
+ // Maybe some transient CPU load is causing issues here?
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException ignored) {
+ System.out.println("Unexpected interrupt");
+ }
+ }
// Clean up and try again.
Runtime.getRuntime().gc();
System.runFinalization();
@@ -84,7 +92,10 @@
if (startingGcNum != getGcNum()) {
// Happens rarely, fail and retry.
- return false;
+ if (!lastChance) {
+ return false;
+ }
+ System.out.println("Triggered early GC");
}
// One of the notifications should block for GC to catch up.
long actualTime = timeNotifications();
diff --git a/test/2040-huge-native-alloc/test-metadata.json b/test/2040-huge-native-alloc/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2040-huge-native-alloc/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2041-bad-cleaner/build.py b/test/2041-bad-cleaner/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2041-bad-cleaner/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2041-bad-cleaner/expected-stdout.txt b/test/2041-bad-cleaner/expected-stdout.txt
index 848a352..db36097 100644
--- a/test/2041-bad-cleaner/expected-stdout.txt
+++ b/test/2041-bad-cleaner/expected-stdout.txt
@@ -2,4 +2,3 @@
Cleaner started and sleeping briefly...
Cleaner done snoozing.
Cleaner sleeping forever now.
-exit status: 2
diff --git a/test/2041-bad-cleaner/run b/test/2041-bad-cleaner/run
deleted file mode 100755
index 54747ee..0000000
--- a/test/2041-bad-cleaner/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-
-# Squash the exit status and put it in expected
-./default-run --external-log-tags "${@}"
-echo "exit status:" $?
diff --git a/test/2041-bad-cleaner/run.py b/test/2041-bad-cleaner/run.py
new file mode 100644
index 0000000..74fcbb9
--- /dev/null
+++ b/test/2041-bad-cleaner/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f", expected_exit_code=2)
diff --git a/test/2041-bad-cleaner/test-metadata.json b/test/2041-bad-cleaner/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2041-bad-cleaner/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2042-checker-dce-always-throw/src/Main.java b/test/2042-checker-dce-always-throw/src/Main.java
index 99738a7..e164917 100644
--- a/test/2042-checker-dce-always-throw/src/Main.java
+++ b/test/2042-checker-dce-always-throw/src/Main.java
@@ -20,12 +20,31 @@
assertEquals(0, $noinline$testSimplifyThrow(1));
// Basic test for non-trivial blocks (i.e. not just an invoke and a Goto)
+ assertEquals(0, $noinline$testSimplifyThrowAndPrint(1));
assertEquals(0, $noinline$testSimplifyTwoThrows(1));
+ assertEquals(0, $noinline$testSimplifyWithArgument(1));
// Try catch tests
assertEquals(0, $noinline$testDoNotSimplifyInTry(1));
assertEquals(0, $noinline$testSimplifyInCatch(1));
- assertEquals(0, $noinline$testDoNotSimplifyInCatchInOuterTry(1));
+ assertEquals(0, $noinline$testDoNotSimplifyInCatchInOuterTry(1, 1));
+
+ // Test that we update the phis correctly after simplifying an always throwing method, and
+ // recomputing dominance.
+ assertEquals(0, $noinline$testUpdatePhisCorrectly(1));
+ assertEquals(0, $noinline$testDeleteAllUsesBeforeDeletingInstruction(1));
+
+ // SimplifyAlwaysThrows for blocks without a goto at the end
+ assertEquals(0, $noinline$testEndsWithIf(1, 1));
+ assertEquals(0, $noinline$testEndsWithReturn(1));
+ // Since we cannot use `assertEquals`, not throwing would be the success.
+ $noinline$testEndsWithReturnVoid(1);
+ assertEquals(0, $noinline$testEndsWithSwitch(1, 1));
+ assertEquals(0, $noinline$testEndsWithThrow(1));
+ assertEquals(0, $noinline$testEndsWithTryBoundary(1));
+
+ // SimplifyAlwaysThrows for invokes in catch blocks
+ assertEquals(0, $noinline$testInsideCatch(1));
}
private static void alwaysThrows() throws Error {
@@ -51,6 +70,28 @@
return 0;
}
+ /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
+ /// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
+
+ /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testSimplifyThrowAndPrint(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ }
+ return 0;
+ }
+
/// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock>> method_name:Main.alwaysThrows always_throws:true
@@ -60,10 +101,14 @@
/// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
- /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+ // Check that the second `alwaysThrows` gets removed.
+ /// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-NOT: InvokeStaticOrDirect method_name:Main.alwaysThrows always_throws:true
+
// Tests that we simplify the always throwing branch directly to the exit, even with blocks that
// are not just the throwing instruction and a Goto.
private static int $noinline$testSimplifyTwoThrows(int num) {
@@ -74,6 +119,35 @@
return 0;
}
+ private static int throwIfZero(int num) {
+ if (num == 0) {
+ throw new Error("num is 0!");
+ }
+ return num / num;
+ }
+
+ /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.throwIfZero always_throws:true
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
+ /// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
+
+ /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.throwIfZero always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testSimplifyWithArgument(int num) {
+ if (num == 0) {
+ throwIfZero(0);
+ System.out.println("I am unrechable!");
+ }
+ return 0;
+ }
+
/// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
@@ -140,7 +214,7 @@
/// CHECK: TryBoundary kind:entry
// Consistency check to that we do not simplify it by the last DCE pass either
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
@@ -190,25 +264,25 @@
}
}
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
/// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
/// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
// Consistency check to make sure we have the try catches in the graph at this stage.
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: TryBoundary kind:entry
/// CHECK-DAG: TryBoundary kind:entry
// Consistency check to that we do not simplify it by the last DCE pass either
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
@@ -217,13 +291,15 @@
// Similar to testSimplifyInCatch, but now the throw is in an outer try and we shouldn't simplify
// it. Like in testDoNotSimplifyInTry, we need the help of the inliner to have an invoke followed
// by a Goto.
- private static int $noinline$testDoNotSimplifyInCatchInOuterTry(int num) {
+ private static int $noinline$testDoNotSimplifyInCatchInOuterTry(int num, int other_num) {
try {
try {
throw new Error();
} catch (Error e) {
if (num == 0) {
- $inline$testDoNotSimplifyInner(num);
+ // We use `other_num` here because otherwise we propagate the knowledge that `num` equals
+ // zero.
+ $inline$testDoNotSimplifyInner(other_num);
}
return 0;
}
@@ -232,6 +308,254 @@
}
}
+ // Check that when we perform SimplifyAlwaysThrows, that the phi for `phi_value` exists, and that
+ // we correctly update it after running DCE.
+
+ /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<ReturnValue:i\d+>> Phi [<<Const0>>,<<Const5>>]
+ /// CHECK-DAG: Return [<<ReturnValue>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
+ /// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
+
+ /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const0>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: Phi
+ private static int $noinline$testUpdatePhisCorrectly(int num) {
+ int phi_value = 0;
+ if (num == 0) {
+ alwaysThrows();
+ phi_value = 5;
+ }
+ return phi_value;
+ }
+
+ // Test to check that we delete all uses before the instruction.
+ private static int $noinline$foo(int num) {
+ return num;
+ }
+
+ /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect method_name:Main.$noinline$foo
+ /// CHECK-DAG: <<ReturnValue:i\d+>> Phi [<<Const0>>,<<Invoke>>]
+ /// CHECK-DAG: Return [<<ReturnValue>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: TryBoundary block:<<InvokeBlock>>
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const0>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: Phi
+ private static int $noinline$testDeleteAllUsesBeforeDeletingInstruction(int num) {
+ int phi_value = 0;
+ if (num == 0) {
+ alwaysThrows();
+ try {
+ phi_value = $noinline$foo(2);
+ } catch (Error e) {
+ throw new Error("We shouldn't hit this");
+ }
+ }
+ return phi_value;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: If block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithIf(int num, int other_num) {
+ if (num == 0) {
+ alwaysThrows();
+ if (other_num == 0) {
+ System.out.println("I am unrechable!");
+ }
+ }
+ return 0;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Return block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithReturn(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ return 1;
+ }
+ return 0;
+ }
+
+ /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: ReturnVoid block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static void $noinline$testEndsWithReturnVoid(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ return;
+ }
+ return;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: PackedSwitch block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithSwitch(int num, int other_num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ int result = 10;
+ switch (other_num) {
+ case 1:
+ result = 100;
+ break;
+ case 2:
+ result = 300;
+ break;
+ case 3:
+ result = 500;
+ break;
+ case 4:
+ result = 700;
+ break;
+ }
+ return result;
+ }
+ return 0;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Throw block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithThrow(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ throw new Error("Other error");
+ }
+ return 0;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: TryBoundary block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithTryBoundary(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ try {
+ alwaysThrows();
+ } catch (Error e) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ // Empty method to forbid the try from disappearing
+ private static void $noinline$emptyMethod() {}
+
+ /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Return block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testInsideCatch(int num) {
+ if (num == 0) {
+ try {
+ $noinline$emptyMethod();
+ } catch (Error e) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected " + expected + " got " + actual);
diff --git a/test/2042-reference-processing/Android.bp b/test/2042-reference-processing/Android.bp
new file mode 100644
index 0000000..a73b8d0
--- /dev/null
+++ b/test/2042-reference-processing/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2042-reference-processing`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2042-reference-processing",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2042-reference-processing-expected-stdout",
+ ":art-run-test-2042-reference-processing-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2042-reference-processing-expected-stdout",
+ out: ["art-run-test-2042-reference-processing-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2042-reference-processing-expected-stderr",
+ out: ["art-run-test-2042-reference-processing-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2042-reference-processing/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2042-reference-processing/expected-stderr.txt
diff --git a/test/2042-reference-processing/expected-stdout.txt b/test/2042-reference-processing/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2042-reference-processing/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2042-reference-processing/info.txt b/test/2042-reference-processing/info.txt
new file mode 100644
index 0000000..e2d7a99
--- /dev/null
+++ b/test/2042-reference-processing/info.txt
@@ -0,0 +1,6 @@
+A test for reference processing correctness.
+
+The emphasis here is on fundamental properties. In particular, references to
+unreachable referents should be enqueued, and this should ensure that uncleared
+References don't point to objects for which References were enqueued. We also
+check various other ordering properties for java.lang.ref.References.
diff --git a/test/2042-reference-processing/src/Main.java b/test/2042-reference-processing/src/Main.java
new file mode 100644
index 0000000..ed67052
--- /dev/null
+++ b/test/2042-reference-processing/src/Main.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 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 java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.math.BigInteger;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Test that objects get finalized and their references cleared in the right order.
+ *
+ * We maintain a list of nominally MAX_LIVE_OBJS numbered finalizable objects.
+ * We then alternately drop the last 50, and add 50 more. When we see an object finalized
+ * or its reference cleared, we make sure that the preceding objects in its group of 50
+ * have also had their references cleared. We also perform a number of other more
+ * straightforward checks, such as ensuring that all references are eventually cleared,
+ * and all objects are finalized.
+ */
+public class Main {
+ // TODO(b/216481630) Enable CHECK_PHANTOM_REFS. This currently occasionally reports a few
+ // PhantomReferences as not enqueued. If this report is correct, this needs to be tracked
+ // down and fixed.
+ static final boolean CHECK_PHANTOM_REFS = false;
+
+ static final int MAX_LIVE_OBJS = 150;
+ static final int DROP_OBJS = 50; // Number of linked objects dropped in each batch.
+ static final int MIN_LIVE_OBJS = MAX_LIVE_OBJS - DROP_OBJS;
+ static final int TOTAL_OBJS = 200_000; // Allocate this many finalizable objects in total.
+ static final boolean REPORT_DROPS = false;
+ static volatile boolean pleaseStop;
+
+ AtomicInteger totalFinalized = new AtomicInteger(0);
+ Object phantomRefsLock = new Object();
+ int maxDropped = 0;
+ int liveObjects = 0;
+
+ // Number of next finalizable object to be allocated.
+ int nextAllocated = 0;
+
+ // List of finalizable objects in descending order. We add to the front and drop
+ // from the rear.
+ FinalizableObject listHead;
+
+ // A possibly incomplete list of FinalizableObject indices that were finalized, but
+ // have yet to be checked for consistency with reference processing.
+ ArrayBlockingQueue<Integer> finalized = new ArrayBlockingQueue<>(20_000);
+
+ // Maps from object number to Reference; Cleared references are deleted when queues are
+ // processed.
+ TreeMap<Integer, MyWeakReference> weakRefs = new TreeMap<>();
+ HashMap<Integer, MyPhantomReference> phantomRefs = new HashMap<>();
+
+ class FinalizableObject {
+ int n;
+ FinalizableObject next;
+ FinalizableObject(int num, FinalizableObject nextObj) {
+ n = num;
+ next = nextObj;
+ }
+ protected void finalize() {
+ if (!inPhantomRefs(n)) {
+ System.out.println("PhantomRef enqueued before finalizer ran");
+ }
+ totalFinalized.incrementAndGet();
+ if (!finalized.offer(n) && REPORT_DROPS) {
+ System.out.println("Dropped finalization of " + n);
+ }
+ }
+ }
+ ReferenceQueue<FinalizableObject> refQueue = new ReferenceQueue<>();
+ class MyWeakReference extends WeakReference<FinalizableObject> {
+ int n;
+ MyWeakReference(FinalizableObject obj) {
+ super(obj, refQueue);
+ n = obj.n;
+ }
+ };
+ class MyPhantomReference extends PhantomReference<FinalizableObject> {
+ int n;
+ MyPhantomReference(FinalizableObject obj) {
+ super(obj, refQueue);
+ n = obj.n;
+ }
+ }
+ boolean inPhantomRefs(int n) {
+ synchronized(phantomRefsLock) {
+ MyPhantomReference ref = phantomRefs.get(n);
+ if (ref == null) {
+ return false;
+ }
+ if (ref.n != n) {
+ System.out.println("phantomRef retrieval failed");
+ }
+ return true;
+ }
+ }
+
+ void CheckOKToClearWeak(int num) {
+ if (num > maxDropped) {
+ System.out.println("WeakRef to live object " + num + " was cleared/enqueued.");
+ }
+ int batchEnd = (num / DROP_OBJS + 1) * DROP_OBJS;
+ for (MyWeakReference wr : weakRefs.subMap(num + 1, batchEnd).values()) {
+ if (wr.n <= num || wr.n / DROP_OBJS != num / DROP_OBJS) {
+ throw new AssertionError("MyWeakReference logic error!");
+ }
+ // wr referent was dropped in same batch and precedes it in list.
+ if (wr.get() != null) {
+ // This violates the WeakReference spec, and can result in strong references
+ // to objects that have been cleaned.
+ System.out.println("WeakReference to " + wr.n
+ + " was erroneously cleared after " + num);
+ }
+ }
+ }
+
+ void CheckOKToClearPhantom(int num) {
+ if (num > maxDropped) {
+ System.out.println("PhantomRef to live object " + num + " was enqueued.");
+ }
+ MyWeakReference wr = weakRefs.get(num);
+ if (wr != null && wr.get() != null) {
+ System.out.println("PhantomRef cleared before WeakRef for " + num);
+ }
+ }
+
+ void emptyAndCheckQueues() {
+ // Check recently finalized objects for consistency with cleared references.
+ while (true) {
+ Integer num = finalized.poll();
+ if (num == null) {
+ break;
+ }
+ MyWeakReference wr = weakRefs.get(num);
+ if (wr != null) {
+ if (wr.n != num) {
+ System.out.println("Finalization logic error!");
+ }
+ if (wr.get() != null) {
+ System.out.println("Finalizing object with uncleared reference");
+ }
+ }
+ CheckOKToClearWeak(num);
+ }
+ // Check recently enqueued references for consistency.
+ while (true) {
+ Reference<FinalizableObject> ref = (Reference<FinalizableObject>) refQueue.poll();
+ if (ref == null) {
+ break;
+ }
+ if (ref instanceof MyWeakReference) {
+ MyWeakReference wr = (MyWeakReference) ref;
+ if (wr.get() != null) {
+ System.out.println("WeakRef " + wr.n + " enqueued but not cleared");
+ }
+ CheckOKToClearWeak(wr.n);
+ if (weakRefs.remove(Integer.valueOf(wr.n)) != ref) {
+ System.out.println("Missing WeakReference: " + wr.n);
+ }
+ } else if (ref instanceof MyPhantomReference) {
+ MyPhantomReference pr = (MyPhantomReference) ref;
+ CheckOKToClearPhantom(pr.n);
+ if (phantomRefs.remove(Integer.valueOf(pr.n)) != ref) {
+ System.out.println("Missing PhantomReference: " + pr.n);
+ }
+ } else {
+ System.out.println("Found unrecognized reference in queue");
+ }
+ }
+ }
+
+
+ /**
+ * Add n objects to the head of the list. These will be assigned the next n consecutive
+ * numbers after the current head of the list.
+ */
+ void addObjects(int n) {
+ for (int i = 0; i < n; ++i) {
+ int me = nextAllocated++;
+ listHead = new FinalizableObject(me, listHead);
+ weakRefs.put(me, new MyWeakReference(listHead));
+ synchronized(phantomRefsLock) {
+ phantomRefs.put(me, new MyPhantomReference(listHead));
+ }
+ }
+ liveObjects += n;
+ }
+
+ /**
+ * Drop n finalizable objects from the tail of the list. These are the lowest-numbered objects
+ * in the list.
+ */
+ void dropObjects(int n) {
+ FinalizableObject list = listHead;
+ FinalizableObject last = null;
+ if (n > liveObjects) {
+ System.out.println("Removing too many elements");
+ }
+ if (liveObjects == n) {
+ maxDropped = list.n;
+ listHead = null;
+ } else {
+ final int skip = liveObjects - n;
+ for (int i = 0; i < skip; ++i) {
+ last = list;
+ list = list.next;
+ }
+ int expected = nextAllocated - skip - 1;
+ if (list.n != expected) {
+ System.out.println("dropObjects found " + list.n + " but expected " + expected);
+ }
+ maxDropped = expected;
+ last.next = null;
+ }
+ liveObjects -= n;
+ }
+
+ void testLoop() {
+ System.out.println("Starting");
+ addObjects(MIN_LIVE_OBJS);
+ final int ITERS = (TOTAL_OBJS - MIN_LIVE_OBJS) / DROP_OBJS;
+ for (int i = 0; i < ITERS; ++i) {
+ addObjects(DROP_OBJS);
+ if (liveObjects != MAX_LIVE_OBJS) {
+ System.out.println("Unexpected live object count");
+ }
+ dropObjects(DROP_OBJS);
+ emptyAndCheckQueues();
+ }
+ dropObjects(MIN_LIVE_OBJS);
+ if (liveObjects != 0 || listHead != null) {
+ System.out.println("Unexpected live objecs at end");
+ }
+ if (maxDropped != TOTAL_OBJS - 1) {
+ System.out.println("Unexpected dropped object count: " + maxDropped);
+ }
+ for (int i = 0; i < 2; ++i) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ emptyAndCheckQueues();
+ }
+ if (!weakRefs.isEmpty()) {
+ System.out.println("Weak Reference map nonempty size = " + weakRefs.size());
+ }
+ if (CHECK_PHANTOM_REFS && !phantomRefs.isEmpty()) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpected interrupt");
+ }
+ if (!phantomRefs.isEmpty()) {
+ System.out.println("Phantom Reference map nonempty size = " + phantomRefs.size());
+ System.out.print("First elements:");
+ int i = 0;
+ for (MyPhantomReference pr : phantomRefs.values()) {
+ System.out.print(" " + pr.n);
+ if (++i > 10) {
+ break;
+ }
+ }
+ System.out.println("");
+ }
+ }
+ if (totalFinalized.get() != TOTAL_OBJS) {
+ System.out.println("Finalized only " + totalFinalized + " objects");
+ }
+ }
+
+ static Runnable causeGCs = new Runnable() {
+ public void run() {
+ // Allocate a lot.
+ BigInteger counter = BigInteger.ZERO;
+ while (!pleaseStop) {
+ counter = counter.add(BigInteger.TEN);
+ }
+ // Look at counter to reduce chance of optimizing out the allocation.
+ if (counter.longValue() % 10 != 0) {
+ System.out.println("Bad causeGCs counter value: " + counter);
+ }
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+ Main theTest = new Main();
+ Thread gcThread = new Thread(causeGCs);
+ gcThread.setDaemon(true); // Terminate if main thread dies.
+ gcThread.start();
+ theTest.testLoop();
+ pleaseStop = true;
+ gcThread.join();
+ System.out.println("Finished");
+ }
+}
diff --git a/test/2043-reference-pauses/Android.bp b/test/2043-reference-pauses/Android.bp
new file mode 100644
index 0000000..a84aea2
--- /dev/null
+++ b/test/2043-reference-pauses/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2043-reference-pauses`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2043-reference-pauses",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2043-reference-pauses-expected-stdout",
+ ":art-run-test-2043-reference-pauses-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2043-reference-pauses-expected-stdout",
+ out: ["art-run-test-2043-reference-pauses-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2043-reference-pauses-expected-stderr",
+ out: ["art-run-test-2043-reference-pauses-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2043-reference-pauses/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2043-reference-pauses/expected-stderr.txt
diff --git a/test/2043-reference-pauses/expected-stdout.txt b/test/2043-reference-pauses/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2043-reference-pauses/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2043-reference-pauses/info.txt b/test/2043-reference-pauses/info.txt
new file mode 100644
index 0000000..f76fa32
--- /dev/null
+++ b/test/2043-reference-pauses/info.txt
@@ -0,0 +1,5 @@
+Tests WeakReference processing and retention of objects needed by finalizers.
+
+Can be used as Reference.get() blocking benchmark by setting PRINT_TIMES to
+true. This will print maximum observed latencies for Reference.get() when
+significant memory is only reachable from SoftReferences and Finalizers.
diff --git a/test/2043-reference-pauses/src/Main.java b/test/2043-reference-pauses/src/Main.java
new file mode 100644
index 0000000..e390155
--- /dev/null
+++ b/test/2043-reference-pauses/src/Main.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2022 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 java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+/**
+ * Basic test of WeakReferences with large amounts of memory that's only reachable through
+ * finalizers. Also makes sure that finalizer-reachable data is not collected.
+ * Can easily be modified to time Reference.get() blocking.
+ */
+public class Main {
+ static final boolean PRINT_TIMES = false; // true will cause benchmark failure.
+ // Data structures repeatedly allocated in background to trigger GC.
+ // Size of finalizer reachable trees.
+ static final int TREE_HEIGHT = 15; // Trees contain 2^TREE_HEIGHT -1 allocated objects.
+ // Number of finalizable tree-owning objects that exist at one point.
+ static final int N_RESURRECTING_OBJECTS = 10;
+ // Number of short-lived, not finalizer-reachable, objects allocated between trees.
+ static final int N_PLAIN_OBJECTS = 20_000;
+ // Number of SoftReferences to CBTs we allocate.
+ static final int N_SOFTREFS = 10;
+
+ static final boolean BACKGROUND_GC_THREAD = true;
+ static final int NBATCHES = 10;
+ static final int NREFS = PRINT_TIMES ? 1_000_000 : 300_000; // Multiple of NBATCHES.
+ static final int REFS_PER_BATCH = NREFS / NBATCHES;
+
+ static volatile boolean pleaseStop = false;
+
+ // Large array of WeakReferences filled and accessed by tests below.
+ ArrayList<WeakReference<Integer>> weakRefs = new ArrayList<>(NREFS);
+
+ /**
+ * Complete binary tree data structure. make(n) takes O(2^n) space.
+ */
+ static class CBT {
+ CBT left;
+ CBT right;
+ CBT(CBT l, CBT r) {
+ left = l;
+ right = r;
+ }
+ static CBT make(int n) {
+ if (n == 0) {
+ return null;
+ }
+ return new CBT(make(n - 1), make(n - 1));
+ }
+ /**
+ * Check that path described by bit-vector path has the correct length.
+ */
+ void check(int n, int path) {
+ CBT current = this;
+ for (int i = 0; i < n; i++, path = path >>> 1) {
+ // Unexpectedly short paths result in NPE.
+ if ((path & 1) == 0) {
+ current = current.left;
+ } else {
+ current = current.right;
+ }
+ }
+ if (current != null) {
+ System.out.println("Complete binary tree path too long");
+ }
+ }
+ }
+
+
+ /**
+ * A finalizable object that refers to O(2^TREE_HEIGHT) otherwise unreachable memory.
+ * When finalized, it creates a new identical object, making sure that one always stays
+ * around.
+ */
+ static class ResurrectingObject {
+ CBT stuff;
+ ResurrectingObject() {
+ stuff = CBT.make(TREE_HEIGHT);
+ }
+ static ResurrectingObject a[] = new ResurrectingObject[2];
+ static int i = 0;
+ static synchronized void allocOne() {
+ a[(++i) % 2] = new ResurrectingObject();
+ // Check the previous one to make it hard to optimize anything out.
+ if (i > 1) {
+ a[(i + 1) % 2].stuff.check(TREE_HEIGHT, i /* weirdly interpreted as path */);
+ }
+ }
+ protected void finalize() {
+ stuff.check(TREE_HEIGHT, 42 /* Some path descriptor */);
+ // Allocate a new one to replace this one.
+ allocOne();
+ }
+ }
+
+ void fillWeakRefs() {
+ for (int i = 0; i < NREFS; ++i) {
+ weakRefs.add(null);
+ }
+ }
+
+ /*
+ * Return maximum observed time in nanos to dereference a WeakReference to an unreachable
+ * object. weakRefs is presumed to be pre-filled to have the correct size.
+ */
+ long timeUnreachableInner() {
+ long maxNanos = 0;
+ // Fill weakRefs with WeakReferences to unreachable integers, a batch at a time.
+ // Then time and test .get() calls on carefully sampled array entries, some of which
+ // will have been cleared.
+ for (int i = 0; i < NBATCHES; ++i) {
+ for (int j = 0; j < REFS_PER_BATCH; ++j) {
+ weakRefs.set(i * REFS_PER_BATCH + j,
+ new WeakReference(new Integer(i * REFS_PER_BATCH + j)));
+ }
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpected exception");
+ }
+ // Iterate over the filled-in section of weakRefs, but look only at a subset of the
+ // elements, making sure the subsets for different top-level iterations are disjoint.
+ // Otherwise the get() calls here will extend the lifetimes of the referents, and we
+ // may never see any cleared WeakReferences.
+ for (int j = (i + 1) * REFS_PER_BATCH - i - 1; j >= 0; j -= NBATCHES) {
+ WeakReference<Integer> wr = weakRefs.get(j);
+ if (wr != null) {
+ long startNanos = System.nanoTime();
+ Integer referent = wr.get();
+ long totalNanos = System.nanoTime() - startNanos;
+ if (referent == null) {
+ // Optimization to reduce max space use and scanning time.
+ weakRefs.set(j, null);
+ }
+ maxNanos = Math.max(maxNanos, totalNanos);
+ if (referent != null && referent.intValue() != j) {
+ System.out.println("Unexpected referent; expected " + j + " got "
+ + referent.intValue());
+ }
+ }
+ }
+ }
+ return maxNanos;
+ }
+
+ /*
+ * Wrapper for the above that also checks that references were reclaimed.
+ * We do this separately to make sure any stack references from the core of the
+ * test are gone. Empirically, we otherwise sometimes see the zeroth WeakReference
+ * not reclaimed.
+ */
+ long timeUnreachable() {
+ long maxNanos = timeUnreachableInner();
+ Runtime.getRuntime().gc();
+ System.runFinalization(); // Presumed to wait for reference clearing.
+ for (int i = 0; i < NREFS; ++i) {
+ if (weakRefs.get(i) != null && weakRefs.get(i).get() != null) {
+ System.out.println("WeakReference to " + i + " wasn't cleared");
+ }
+ }
+ return maxNanos;
+ }
+
+ /**
+ * Return maximum observed time in nanos to dereference a WeakReference to a reachable
+ * object. Overwrites weakRefs, which is presumed to have NREFS entries already.
+ */
+ long timeReachable() {
+ long maxNanos = 0;
+ // Similar to the above, but we use WeakReferences to otherwise reachable objects,
+ // which should thus not get cleared.
+ Integer[] strongRefs = new Integer[NREFS];
+ for (int i = 0; i < NBATCHES; ++i) {
+ for (int j = i * REFS_PER_BATCH; j < (i + 1) * REFS_PER_BATCH; ++j) {
+ Integer newObj = new Integer(j);
+ strongRefs[j] = newObj;
+ weakRefs.set(j, new WeakReference(newObj));
+ }
+ for (int j = (i + 1) * REFS_PER_BATCH - 1; j >= 0; --j) {
+ WeakReference<Integer> wr = weakRefs.get(j);
+ long startNanos = System.nanoTime();
+ Integer referent = wr.get();
+ long totalNanos = System.nanoTime() - startNanos;
+ maxNanos = Math.max(maxNanos, totalNanos);
+ if (referent == null) {
+ System.out.println("Unexpectedly cleared referent at " + j);
+ } else if (referent.intValue() != j) {
+ System.out.println("Unexpected reachable referent; expected " + j + " got "
+ + referent.intValue());
+ }
+ }
+ }
+ Reference.reachabilityFence(strongRefs);
+ return maxNanos;
+ }
+
+ void runTest() {
+ System.out.println("Starting");
+ fillWeakRefs();
+ long unreachableNanos = timeUnreachable();
+ if (PRINT_TIMES) {
+ System.out.println("Finished timeUnrechable()");
+ }
+ long reachableNanos = timeReachable();
+ String unreachableMillis =
+ String. format("%,.3f", ((double) unreachableNanos) / 1_000_000);
+ String reachableMillis =
+ String. format("%,.3f", ((double) reachableNanos) / 1_000_000);
+ if (PRINT_TIMES) {
+ System.out.println(
+ "Max time for WeakReference.get (unreachable): " + unreachableMillis);
+ System.out.println(
+ "Max time for WeakReference.get (reachable): " + reachableMillis);
+ }
+ // Only report extremely egregious pauses to avoid spurious failures.
+ if (unreachableNanos > 10_000_000_000L) {
+ System.out.println("WeakReference.get (unreachable) time unreasonably long");
+ }
+ if (reachableNanos > 10_000_000_000L) {
+ System.out.println("WeakReference.get (reachable) time unreasonably long");
+ }
+ }
+
+ /**
+ * Allocate and GC a lot, while keeping significant amounts of finalizer and
+ * SoftReference-reachable memory around.
+ */
+ static Runnable allocFinalizable = new Runnable() {
+ public void run() {
+ // Allocate and drop some finalizable objects that take a long time
+ // to mark. Designed to be hard to optimize away. Each of these objects will
+ // build a new one in its finalizer before really going away.
+ ArrayList<SoftReference<CBT>> softRefs = new ArrayList<>(N_SOFTREFS);
+ for (int i = 0; i < N_SOFTREFS; ++i) {
+ // These should not normally get reclaimed, since we shouldn't run out of
+ // memory. They do increase tracing time.
+ softRefs.add(new SoftReference(CBT.make(TREE_HEIGHT)));
+ }
+ for (int i = 0; i < N_RESURRECTING_OBJECTS; ++i) {
+ ResurrectingObject.allocOne();
+ }
+ BigInteger counter = BigInteger.ZERO;
+ for (int i = 1; !pleaseStop; ++i) {
+ // Allocate a lot of short-lived objects, using BigIntegers to minimize the chance
+ // of the allocation getting optimized out. This makes things slightly more
+ // realistic, since not all objects will be finalizer reachable.
+ for (int j = 0; j < N_PLAIN_OBJECTS / 2; ++j) {
+ counter = counter.add(BigInteger.TEN);
+ }
+ // Look at counter to reduce chance of optimizing out the allocation.
+ if (counter.longValue() % 10 != 0) {
+ System.out.println("Bad allocFinalizable counter value: " + counter);
+ }
+ // Explicitly collect here, mostly to prevent heap growth. Otherwise we get
+ // ahead of the GC and eventually block on it.
+ Runtime.getRuntime().gc();
+ if (PRINT_TIMES && i % 100 == 0) {
+ System.out.println("Collected " + i + " times");
+ }
+ }
+ // To be safe, access softRefs.
+ final CBT sample = softRefs.get(N_SOFTREFS / 2).get();
+ if (sample != null) {
+ sample.check(TREE_HEIGHT, 47 /* some path descriptor */);
+ }
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+ Main theTest = new Main();
+ Thread allocThread = null;
+ if (BACKGROUND_GC_THREAD) {
+ allocThread = new Thread(allocFinalizable);
+ allocThread.setDaemon(true); // Terminate if main thread dies.
+ allocThread.start();
+ }
+ theTest.runTest();
+ if (BACKGROUND_GC_THREAD) {
+ pleaseStop = true;
+ allocThread.join();
+ }
+ System.out.println("Finished");
+ }
+}
diff --git a/test/2044-get-stack-traces/Android.bp b/test/2044-get-stack-traces/Android.bp
new file mode 100644
index 0000000..79aea7c
--- /dev/null
+++ b/test/2044-get-stack-traces/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2044-get-stack-traces`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2044-get-stack-traces",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2044-get-stack-traces-expected-stdout",
+ ":art-run-test-2044-get-stack-traces-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2044-get-stack-traces-expected-stdout",
+ out: ["art-run-test-2044-get-stack-traces-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2044-get-stack-traces-expected-stderr",
+ out: ["art-run-test-2044-get-stack-traces-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2044-get-stack-traces/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2044-get-stack-traces/expected-stderr.txt
diff --git a/test/2044-get-stack-traces/expected-stdout.txt b/test/2044-get-stack-traces/expected-stdout.txt
new file mode 100644
index 0000000..d396083
--- /dev/null
+++ b/test/2044-get-stack-traces/expected-stdout.txt
@@ -0,0 +1,8 @@
+Starting
+Starting helper
+Starting helper
+Starting helper
+Starting helper
+Starting helper
+Finished worker stack traces
+Finished
diff --git a/test/2044-get-stack-traces/info.txt b/test/2044-get-stack-traces/info.txt
new file mode 100644
index 0000000..f9cac7a
--- /dev/null
+++ b/test/2044-get-stack-traces/info.txt
@@ -0,0 +1,4 @@
+Tests multiple simultaneous calls to Thread.getStackTrace()
+
+This is a stress test for code under suspicion in the context of b/234542166
+and others.
diff --git a/test/2044-get-stack-traces/src/Main.java b/test/2044-get-stack-traces/src/Main.java
new file mode 100644
index 0000000..7840389
--- /dev/null
+++ b/test/2044-get-stack-traces/src/Main.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+/**
+ * We construct a main thread and worker threads, each retrieving stack traces
+ * from the other. Since there are multiple workers, we may get a large number
+ * of simultaneous stack trace attempts.
+ */
+public class Main {
+ static final int NUM_THREADS = 5;
+ static Thread mainThread;
+ static volatile boolean pleaseStop = false;
+
+ private static void getTrace(Thread t) {
+ StackTraceElement trace[] = t.getStackTrace();
+ if (!pleaseStop && (trace.length < 1 || trace.length > 20)) {
+ // If called from traceGetter, we were started by the main thread, and it was still
+ // running after the trace, so the main thread should have at least one frame on
+ // the stack. If called by main(), we waited for all the traceGetters to start,
+ // and didn't yet allow them to stop, so the same should be true.
+ System.out.println("Stack trace for " + t.getName() + " has size " + trace.length);
+ for (StackTraceElement e : trace) {
+ System.out.println(e.toString());
+ }
+ }
+ }
+
+ /**
+ * Repeatedly get and minimally check stack trace of main thread.
+ */
+ static Runnable traceGetter = new Runnable() {
+ public void run() {
+ System.out.println("Starting helper");
+ while (!pleaseStop) {
+ getTrace(mainThread);
+ }
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("Starting");
+ Thread[] t = new Thread[NUM_THREADS];
+ mainThread = Thread.currentThread();
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ t[i] = new Thread(traceGetter);
+ t[i].start();
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpectedly interrupted");
+ }
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ getTrace(t[i]);
+ }
+ System.out.println("Finished worker stack traces");
+ long now = System.currentTimeMillis();
+ while (System.currentTimeMillis() - now < 2000) {
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpectedly interrupted");
+ }
+ }
+ pleaseStop = true;
+ System.out.println("Finished");
+ }
+}
diff --git a/test/2230-profile-save-hotness/run b/test/2230-profile-save-hotness/run
deleted file mode 100644
index d0c49b6..0000000
--- a/test/2230-profile-save-hotness/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-${RUN} \
- -Xcompiler-option --count-hotness-in-compiled-code \
- -Xcompiler-option --compiler-filter=speed \
- --runtime-option -Xps-profile-aot-code \
- --runtime-option -Xjitsaveprofilinginfo \
- --runtime-option -Xusejit:true "${@}"
diff --git a/test/2230-profile-save-hotness/run.py b/test/2230-profile-save-hotness/run.py
new file mode 100644
index 0000000..526b841
--- /dev/null
+++ b/test/2230-profile-save-hotness/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ Xcompiler_option=[
+ "--count-hotness-in-compiled-code", "--compiler-filter=speed"
+ ],
+ runtime_option=[
+ "-Xps-profile-aot-code", "-Xjitsaveprofilinginfo", "-Xusejit:true"
+ ],
+ )
diff --git a/test/2232-write-metrics-to-log/Android.bp b/test/2232-write-metrics-to-log/Android.bp
index a64567e..6a1e68c 100644
--- a/test/2232-write-metrics-to-log/Android.bp
+++ b/test/2232-write-metrics-to-log/Android.bp
@@ -15,7 +15,7 @@
java_test {
name: "art-run-test-2232-write-metrics-to-log",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-2232-write-metrics-to-log-expected-stdout",
diff --git a/test/2232-write-metrics-to-log/check b/test/2232-write-metrics-to-log/check
deleted file mode 100755
index d12e8b1..0000000
--- a/test/2232-write-metrics-to-log/check
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Check that one of the metrics appears in stderr.
-grep 'ClassVerificationTotalTime' "$4" >/dev/null
-MSG_FOUND=$?
-
-if [[ $MSG_FOUND -ne 0 ]] ; then
- # Print out the log and return with error.
- cat "$4"
- exit 1
-fi
-
-# Success.
-exit 0
diff --git a/test/2232-write-metrics-to-log/expected-stderr.txt b/test/2232-write-metrics-to-log/expected-stderr.txt
index e69de29..5bc27fe 100644
--- a/test/2232-write-metrics-to-log/expected-stderr.txt
+++ b/test/2232-write-metrics-to-log/expected-stderr.txt
@@ -0,0 +1 @@
+ClassVerificationTotalTimeDelta
diff --git a/test/2232-write-metrics-to-log/run b/test/2232-write-metrics-to-log/run
deleted file mode 100755
index d34ec6c..0000000
--- a/test/2232-write-metrics-to-log/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-export ANDROID_LOG_TAGS="*:i"
-exec ${RUN} $@ --external-log-tags --runtime-option -Xmetrics-write-to-logcat:true --runtime-option -Xmetrics-reporting-mods:100
diff --git a/test/2232-write-metrics-to-log/run.py b/test/2232-write-metrics-to-log/run.py
new file mode 100644
index 0000000..89fe040
--- /dev/null
+++ b/test/2232-write-metrics-to-log/run.py
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ android_log_tags="*:i",
+ diff_min_log_tag="i",
+ runtime_option=[
+ "-Xmetrics-write-to-logcat:true", "-Xmetrics-reporting-mods:100"
+ ])
+
+ # Check that one of the metrics appears in stderr.
+ ctx.run(
+ fr"sed -i -n 's/.*\(ClassVerificationTotalTimeDelta\).*/\1/p' '{args.stderr_file}'"
+ )
diff --git a/test/2233-checker-remove-loop-suspend-check/Android.bp b/test/2233-checker-remove-loop-suspend-check/Android.bp
new file mode 100644
index 0000000..f581ee3
--- /dev/null
+++ b/test/2233-checker-remove-loop-suspend-check/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2233-checker-remove-loop-suspend-check`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2233-checker-remove-loop-suspend-check",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2233-checker-remove-loop-suspend-check-expected-stdout",
+ ":art-run-test-2233-checker-remove-loop-suspend-check-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2233-checker-remove-loop-suspend-check-expected-stdout",
+ out: ["art-run-test-2233-checker-remove-loop-suspend-check-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2233-checker-remove-loop-suspend-check-expected-stderr",
+ out: ["art-run-test-2233-checker-remove-loop-suspend-check-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2233-checker-remove-loop-suspend-check/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2233-checker-remove-loop-suspend-check/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2233-checker-remove-loop-suspend-check/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2233-checker-remove-loop-suspend-check/expected-stdout.txt
diff --git a/test/2233-checker-remove-loop-suspend-check/info.txt b/test/2233-checker-remove-loop-suspend-check/info.txt
new file mode 100644
index 0000000..fe8d8eb
--- /dev/null
+++ b/test/2233-checker-remove-loop-suspend-check/info.txt
@@ -0,0 +1 @@
+Test to check the removal of SuspendCheck for finite simple plain loops.
diff --git a/test/2233-checker-remove-loop-suspend-check/src/Main.java b/test/2233-checker-remove-loop-suspend-check/src/Main.java
new file mode 100644
index 0000000..c56bd66
--- /dev/null
+++ b/test/2233-checker-remove-loop-suspend-check/src/Main.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+
+ static final int ITERATIONS = 16;
+
+ // Test 1: This test checks whether the SuspendCheck is removed from the
+ // header.
+
+ /// CHECK-START-ARM64: void Main.$noinline$testRemoveSuspendCheck(int[]) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK-NEXT: dex_pc:{{.*}}
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: b
+
+ public static void $noinline$testRemoveSuspendCheck(int[] a) {
+ for (int i = 0; i < ITERATIONS; i++) {
+ a[i++] = i;
+ }
+ }
+
+ // Test 2: This test checks that the SuspendCheck is not removed from the
+ // header because it contains a call to another function.
+
+ /// CHECK-START-ARM64: void Main.testRemoveSuspendCheckWithCall(int[]) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: ldr
+
+ public static void testRemoveSuspendCheckWithCall(int[] a) {
+ for (int i = 0; i < ITERATIONS; i++) {
+ a[i++] = i;
+ $noinline$testRemoveSuspendCheck(a);
+ }
+ }
+
+ // Test 3: This test checks that the SuspendCheck is not removed from the
+ // header because INSTR_COUNT * TRIP_COUNT exceeds the defined heuristic.
+
+ /// CHECK-START-ARM64: void Main.testRemoveSuspendCheckAboveHeuristic(int[]) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: ldr
+
+ public static void testRemoveSuspendCheckAboveHeuristic(int[] a) {
+ for (int i = 0; i < ITERATIONS * 6; i++) {
+ a[i++] = i;
+ }
+ }
+
+ // Test 4: This test checks that the SuspendCheck is not removed from the
+ // header because the trip count is not known at compile time.
+
+ /// CHECK-START-ARM64: void Main.testRemoveSuspendCheckUnknownCount(int[], int) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: ldr
+
+ public static void testRemoveSuspendCheckUnknownCount(int[] a, int n) {
+ for (int i = 0; i < n; i++) {
+ a[i++] = i;
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] a = new int[100];
+ $noinline$testRemoveSuspendCheck(a);
+ testRemoveSuspendCheckWithCall(a);
+ testRemoveSuspendCheckAboveHeuristic(a);
+ testRemoveSuspendCheckUnknownCount(a, 4);
+ }
+}
diff --git a/test/2235-JdkUnsafeTest/build.py b/test/2235-JdkUnsafeTest/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2235-JdkUnsafeTest/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2235-JdkUnsafeTest/test-metadata.json b/test/2235-JdkUnsafeTest/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2235-JdkUnsafeTest/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2237-checker-inline-multidex/expected-stdout.txt b/test/2237-checker-inline-multidex/expected-stdout.txt
index 571349a..c2c127f 100644
--- a/test/2237-checker-inline-multidex/expected-stdout.txt
+++ b/test/2237-checker-inline-multidex/expected-stdout.txt
@@ -2,3 +2,4 @@
def
ghi
class Multi$Multi2
+122
diff --git a/test/2237-checker-inline-multidex/info.txt b/test/2237-checker-inline-multidex/info.txt
index fc6dc60..2e639ba 100644
--- a/test/2237-checker-inline-multidex/info.txt
+++ b/test/2237-checker-inline-multidex/info.txt
@@ -1 +1 @@
-Checks that we inline across dex files, even when we need an environment.
+Checks that we inline across dex files.
diff --git a/test/2237-checker-inline-multidex/src-multidex/Multi.java b/test/2237-checker-inline-multidex/src-multidex/Multi.java
index cd234c3..c830c59 100644
--- a/test/2237-checker-inline-multidex/src-multidex/Multi.java
+++ b/test/2237-checker-inline-multidex/src-multidex/Multi.java
@@ -38,4 +38,12 @@
}
private class Multi2 {}
+
+ public static int $inline$TryCatch(String str) {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
}
diff --git a/test/2237-checker-inline-multidex/src/Main.java b/test/2237-checker-inline-multidex/src/Main.java
index 7ab2e7f..7a2ca82 100644
--- a/test/2237-checker-inline-multidex/src/Main.java
+++ b/test/2237-checker-inline-multidex/src/Main.java
@@ -16,57 +16,73 @@
public class Main {
public static void main(String[] args) {
- System.out.println(testNeedsEnvironment());
- System.out.println(testNeedsBssEntryString());
- System.out.println(testNeedsBssEntryInvoke());
- System.out.println(testClass());
+ // Test that the cross-dex inlining is working for HInstructions that need an environment.
+ System.out.println($noinline$testNeedsEnvironment());
+
+ // Test that the cross-dex inlining is working for HInstructions that need a bss entry.
+ System.out.println($noinline$testNeedsBssEntryString());
+ System.out.println($noinline$testNeedsBssEntryInvoke());
+ System.out.println($noinline$testClass());
+
+ // Test that we are able to inline try catches across dex files.
+ System.out.println($noinline$testTryCatch());
}
- /// CHECK-START: java.lang.String Main.testNeedsEnvironment() inliner (before)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsEnvironment() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$inline$NeedsEnvironmentMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsEnvironment() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsEnvironment() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.$inline$NeedsEnvironmentMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsEnvironment() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsEnvironment() inliner (after)
/// CHECK: StringBuilderAppend
- public static String testNeedsEnvironment() {
+ private static String $noinline$testNeedsEnvironment() {
return Multi.$inline$NeedsEnvironmentMultiDex("abc");
}
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryString() inliner (before)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryString() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryStringMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryString() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryString() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryStringMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryString() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryString() inliner (after)
/// CHECK: LoadString load_kind:BssEntry
- public static String testNeedsBssEntryString() {
+ private static String $noinline$testNeedsBssEntryString() {
return Multi.$inline$NeedsBssEntryStringMultiDex();
}
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryInvoke() inliner (before)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryInvoke() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryInvokeMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryInvoke() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryInvoke() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryInvokeMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryInvoke() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryInvoke() inliner (after)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$noinline$InnerInvokeMultiDex method_load_kind:BssEntry
- public static String testNeedsBssEntryInvoke() {
+ private static String $noinline$testNeedsBssEntryInvoke() {
return Multi.$inline$NeedsBssEntryInvokeMultiDex();
}
- /// CHECK-START: java.lang.Class Main.testClass() inliner (before)
+ /// CHECK-START: java.lang.Class Main.$noinline$testClass() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.NeedsBssEntryClassMultiDex
- /// CHECK-START: java.lang.Class Main.testClass() inliner (after)
+ /// CHECK-START: java.lang.Class Main.$noinline$testClass() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.NeedsBssEntryClassMultiDex
- /// CHECK-START: java.lang.Class Main.testClass() inliner (after)
+ /// CHECK-START: java.lang.Class Main.$noinline$testClass() inliner (after)
/// CHECK: LoadClass load_kind:BssEntry class_name:Multi$Multi2
- public static Class<?> testClass() {
+ private static Class<?> $noinline$testClass() {
return Multi.NeedsBssEntryClassMultiDex();
}
+
+
+ /// CHECK-START: int Main.$noinline$testTryCatch() inliner (before)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testTryCatch() inliner (after)
+ /// CHECK: TryBoundary
+ private static int $noinline$testTryCatch() {
+ return Multi.$inline$TryCatch("123") + Multi.$inline$TryCatch("abc");
+ }
}
diff --git a/test/2238-checker-polymorphic-recursive-inlining/run b/test/2238-checker-polymorphic-recursive-inlining/run
deleted file mode 100644
index a4e2692..0000000
--- a/test/2238-checker-polymorphic-recursive-inlining/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-# Use a profile to put specific classes in the app image to trigger polymorphic inlining.
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/2238-checker-polymorphic-recursive-inlining/run.py b/test/2238-checker-polymorphic-recursive-inlining/run.py
new file mode 100644
index 0000000..b85f926
--- /dev/null
+++ b/test/2238-checker-polymorphic-recursive-inlining/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image to trigger polymorphic inlining.
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/2239-varhandle-perf/build b/test/2239-varhandle-perf/build
deleted file mode 100755
index 115a0fb..0000000
--- a/test/2239-varhandle-perf/build
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Make us exit on a failure
-set -e
-
-# Set variables for source directories. Using src-art so we use
-# VarHandles in the bootclasspath and can compile with the Java 8
-# compiler.
-MANUAL_SRC=src
-GENERATED_SRC=src2
-
-# Build the Java files
-mkdir -p src2
-
-# Generate tests and Main that covers both the generated tests and manual tests
-python3 ./util-src/generate_java.py "${GENERATED_SRC}"
-
-./default-build "$@" --experimental var-handles
diff --git a/test/2239-varhandle-perf/build.py b/test/2239-varhandle-perf/build.py
new file mode 100644
index 0000000..3466dfe
--- /dev/null
+++ b/test/2239-varhandle-perf/build.py
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ if ctx.jvm:
+ return # The test does not build on JVM
+
+ ctx.default_build(api_level="var-handles")
diff --git a/test/2239-varhandle-perf/check b/test/2239-varhandle-perf/check
deleted file mode 100644
index 8ea102d..0000000
--- a/test/2239-varhandle-perf/check
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-# Dump the output of the benchmarks that run and report success. The
-# benchmarks ran successfully if we get as far as this script.
-
-cat "$2"
-exit 0
diff --git a/test/2239-varhandle-perf/generate-sources b/test/2239-varhandle-perf/generate-sources
new file mode 100755
index 0000000..310ad06
--- /dev/null
+++ b/test/2239-varhandle-perf/generate-sources
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make us exit on a failure
+set -e
+
+# Set variables for source directories. Using src-art so we use
+# VarHandles in the bootclasspath and can compile with the Java 8
+# compiler.
+MANUAL_SRC=src
+GENERATED_SRC=src2
+
+# Build the Java files
+mkdir -p src2
+
+# Generate tests and Main that covers both the generated tests and manual tests
+python3 ./util-src/generate_java.py "${GENERATED_SRC}"
diff --git a/test/2239-varhandle-perf/run.py b/test/2239-varhandle-perf/run.py
new file mode 100644
index 0000000..4e8755d
--- /dev/null
+++ b/test/2239-varhandle-perf/run.py
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Dump the output of the benchmarks that run and report success. The
+ # benchmarks ran successfully if we get as far as this script.
+ ctx.run(fr"cat '{args.stdout_file}'")
+
+ # Delete all output to make the diff unconditionally pass.
+ ctx.run(fr"> '{args.stdout_file}'")
diff --git a/test/2240-tracing-non-invokable-method/Android.bp b/test/2240-tracing-non-invokable-method/Android.bp
new file mode 100644
index 0000000..b3a2c79
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2240-tracing-non-invokable-method`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-2240-tracing-non-invokable-method-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2240-tracing-non-invokable-method",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-2240-tracing-non-invokable-method-src"
+ ],
+ data: [
+ ":art-run-test-2240-tracing-non-invokable-method-expected-stdout",
+ ":art-run-test-2240-tracing-non-invokable-method-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2240-tracing-non-invokable-method-expected-stdout",
+ out: ["art-run-test-2240-tracing-non-invokable-method-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2240-tracing-non-invokable-method-expected-stderr",
+ out: ["art-run-test-2240-tracing-non-invokable-method-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2240-tracing-non-invokable-method/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2240-tracing-non-invokable-method/expected-stderr.txt
diff --git a/test/2240-tracing-non-invokable-method/expected-stdout.txt b/test/2240-tracing-non-invokable-method/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/2240-tracing-non-invokable-method/info.txt b/test/2240-tracing-non-invokable-method/info.txt
new file mode 100644
index 0000000..27877aa
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/info.txt
@@ -0,0 +1,2 @@
+Tests that tracing handles non-invokable methods correctly without updating the
+entrypoints for non-invokable methods.
diff --git a/test/2240-tracing-non-invokable-method/run.py b/test/2240-tracing-non-invokable-method/run.py
new file mode 100644
index 0000000..ac17e68
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, runtime_option=["-Xmethod-trace", "-Xmethod-trace-file:/dev/null"])
diff --git a/test/2240-tracing-non-invokable-method/src/Main.java b/test/2240-tracing-non-invokable-method/src/Main.java
new file mode 100644
index 0000000..e5a4e43
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+interface Itf {
+ public default void m() throws Exception {
+ throw new Exception("Don't inline me");
+ }
+ public default void mConflict() throws Exception {
+ throw new Exception("Don't inline me");
+ }
+}
+
+// This is redefined in src2 with a mConflict method.
+interface Itf2 {
+}
+
+public class Main implements Itf, Itf2 {
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+
+ try {
+ itf.mConflict();
+ throw new Error("Expected IncompatibleClassChangeError");
+ } catch (Exception e) {
+ throw new Error("Unexpected exception");
+ } catch (IncompatibleClassChangeError e) {
+ // Expected.
+ }
+ }
+
+ static Itf itf = new Main();
+}
diff --git a/test/2240-tracing-non-invokable-method/src2/Itf2.java b/test/2240-tracing-non-invokable-method/src2/Itf2.java
new file mode 100644
index 0000000..e962411
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/src2/Itf2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+interface Itf2 {
+ public default void mConflict(){
+ }
+}
diff --git a/test/2241-checker-inline-try-catch/Android.bp b/test/2241-checker-inline-try-catch/Android.bp
new file mode 100644
index 0000000..15708d6
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2241-checker-inline-try-catch`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2241-checker-inline-try-catch",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2241-checker-inline-try-catch-expected-stdout",
+ ":art-run-test-2241-checker-inline-try-catch-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2241-checker-inline-try-catch-expected-stdout",
+ out: ["art-run-test-2241-checker-inline-try-catch-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2241-checker-inline-try-catch-expected-stderr",
+ out: ["art-run-test-2241-checker-inline-try-catch-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2241-checker-inline-try-catch/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2241-checker-inline-try-catch/expected-stderr.txt
diff --git a/test/2241-checker-inline-try-catch/expected-stdout.txt b/test/2241-checker-inline-try-catch/expected-stdout.txt
new file mode 100644
index 0000000..6d127d1
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/expected-stdout.txt
@@ -0,0 +1,2 @@
+Finally, a worthy opponent!
+Our battle it will be legendary!
diff --git a/test/2241-checker-inline-try-catch/info.txt b/test/2241-checker-inline-try-catch/info.txt
new file mode 100644
index 0000000..3b86006
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/info.txt
@@ -0,0 +1 @@
+Tests that we inline try catches, as long as we don't inline inside of other try catches.
diff --git a/test/2241-checker-inline-try-catch/src/Main.java b/test/2241-checker-inline-try-catch/src/Main.java
new file mode 100644
index 0000000..a80fbd7
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/src/Main.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ $noinline$testSingleTryCatch();
+ $noinline$testSingleTryCatchTwice();
+ $noinline$testSingleTryCatchDifferentInputs();
+ $noinline$testDifferentTryCatches();
+ $noinline$testTryCatchFinally();
+ $noinline$testTryCatchFinallyDifferentInputs();
+ $noinline$testRecursiveTryCatch();
+ $noinline$testDoNotInlineInsideTryInlineInsideCatch();
+ $noinline$testInlineInsideNestedCatches();
+ $noinline$testBeforeAfterTryCatch();
+ $noinline$testDifferentTypes();
+ $noinline$testRawThrow();
+ $noinline$testRawThrowTwice();
+ $noinline$testThrowCaughtInOuterMethod();
+ }
+
+ public static void $noinline$assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ // Basic try catch inline.
+ private static void $noinline$testSingleTryCatch() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+
+ // Two instances of the same method with a try catch.
+ private static void $noinline$testSingleTryCatchTwice() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+
+ // Triggering both normal and the exceptional flow.
+ private static void $noinline$testSingleTryCatchDifferentInputs() {
+ $noinline$assertEquals(1, $inline$OOBTryCatch(null));
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ int[] filled_numbers = {42};
+ $noinline$assertEquals(42, $inline$OOBTryCatch(filled_numbers));
+ }
+
+
+ // Two different try catches, with the same catch's dex_pc.
+ private static void $noinline$testDifferentTryCatches() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ $noinline$assertEquals(2, $inline$OtherOOBTryCatch(numbers));
+ }
+
+ // Basic try/catch/finally.
+ private static void $noinline$testTryCatchFinally() {
+ int[] numbers = {};
+ $noinline$assertEquals(3, $inline$OOBTryCatchFinally(numbers));
+ }
+
+ // Triggering both normal and the exceptional flow.
+ private static void $noinline$testTryCatchFinallyDifferentInputs() {
+ $noinline$assertEquals(3, $inline$OOBTryCatchFinally(null));
+ int[] numbers = {};
+ $noinline$assertEquals(3, $inline$OOBTryCatchFinally(numbers));
+ int[] filled_numbers = {42};
+ $noinline$assertEquals(42, $inline$OOBTryCatchFinally(filled_numbers));
+ }
+
+ // Test that we can inline even when the try catch is several levels deep.
+ private static void $noinline$testRecursiveTryCatch() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatchLevel4(numbers));
+ }
+
+ // Tests that we don't inline inside outer tries, but we do inline inside of catches.
+ /// CHECK-START: void Main.$noinline$testDoNotInlineInsideTryInlineInsideCatch() inliner (before)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.DoNotInlineOOBTryCatch
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$inline$OOBTryCatch
+
+ /// CHECK-START: void Main.$noinline$testDoNotInlineInsideTryInlineInsideCatch() inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.DoNotInlineOOBTryCatch
+ private static void $noinline$testDoNotInlineInsideTryInlineInsideCatch() {
+ int val = 0;
+ try {
+ int[] numbers = {};
+ val = DoNotInlineOOBTryCatch(numbers);
+ } catch (Exception ex) {
+ unreachable();
+ // This is unreachable but we will still compile it so it works for checking that it inlines.
+ int[] numbers = {};
+ $inline$OOBTryCatch(numbers);
+ }
+ $noinline$assertEquals(1, val);
+ }
+
+ private static void $noinline$emptyMethod() {}
+
+ private static void $inline$testInlineInsideNestedCatches_inner() {
+ try {
+ $noinline$emptyMethod();
+ } catch (Exception ex) {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+ }
+
+ private static void $noinline$testInlineInsideNestedCatches() {
+ try {
+ $noinline$emptyMethod();
+ } catch (Exception ex) {
+ $inline$testInlineInsideNestedCatches_inner();
+ }
+ }
+
+ // Tests that outer tries or catches don't affect as long as we are not inlining the inner
+ // try/catch inside of them.
+ private static void $noinline$testBeforeAfterTryCatch() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+
+ // Unrelated try catch does not block inlining outside of it. We fill it in to make sure it is
+ // still there by the time the inliner runs.
+ int val = 0;
+ try {
+ int[] other_array = {};
+ val = other_array[0];
+ } catch (Exception ex) {
+ $noinline$assertEquals(0, val);
+ val = 1;
+ }
+ $noinline$assertEquals(1, val);
+
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+
+ // Tests different try catch types in the same outer method.
+ private static void $noinline$testDifferentTypes() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ $noinline$assertEquals(2, $inline$OtherOOBTryCatch(numbers));
+ $noinline$assertEquals(123, $inline$ParseIntTryCatch("123"));
+ $noinline$assertEquals(-1, $inline$ParseIntTryCatch("abc"));
+ }
+
+ // Tests a raw throw (rather than an instruction that happens to throw).
+ private static void $noinline$testRawThrow() {
+ $noinline$assertEquals(1, $inline$rawThrowCaught());
+ }
+
+ // Tests a raw throw twice.
+ private static void $noinline$testRawThrowTwice() {
+ $noinline$assertEquals(1, $inline$rawThrowCaught());
+ $noinline$assertEquals(1, $inline$rawThrowCaught());
+ }
+
+ // Tests that the outer method can successfully catch the throw in the inner method.
+ private static void $noinline$testThrowCaughtInOuterMethod() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_simpleTryCatch(numbers));
+ $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_simpleTryCatch_inliningInner(numbers));
+ $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_withFinally(numbers));
+ }
+
+ // Building blocks for the test functions.
+ private static int $inline$OOBTryCatch(int[] array) {
+ try {
+ return array[0];
+ } catch (Exception e) {
+ return 1;
+ }
+ }
+
+ private static int $inline$OtherOOBTryCatch(int[] array) {
+ try {
+ return array[0];
+ } catch (Exception e) {
+ return 2;
+ }
+ }
+
+ private static int $inline$OOBTryCatchFinally(int[] array) {
+ int val = 0;
+ try {
+ val = 1;
+ return array[0];
+ } catch (Exception e) {
+ val = 2;
+ } finally {
+ val = 3;
+ }
+ return val;
+ }
+
+ // If we make the depthness a parameter, we wouldn't be able to mark as $inline$ and we would
+ // need extra CHECKer statements.
+ private static int $inline$OOBTryCatchLevel4(int[] array) {
+ return $inline$OOBTryCatchLevel3(array);
+ }
+
+ private static int $inline$OOBTryCatchLevel3(int[] array) {
+ return $inline$OOBTryCatchLevel2(array);
+ }
+
+ private static int $inline$OOBTryCatchLevel2(int[] array) {
+ return $inline$OOBTryCatchLevel1(array);
+ }
+
+ private static int $inline$OOBTryCatchLevel1(int[] array) {
+ return $inline$OOBTryCatch(array);
+ }
+
+ private static int DoNotInlineOOBTryCatch(int[] array) {
+ try {
+ return array[0];
+ } catch (Exception e) {
+ return 1;
+ }
+ }
+
+ private static void unreachable() {
+ throw new Error("Unreachable");
+ }
+
+ private static int $inline$ParseIntTryCatch(String str) {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ private static int $inline$rawThrowCaught() {
+ try {
+ throw new Error();
+ } catch (Error e) {
+ return 1;
+ }
+ }
+
+ private static int $inline$testThrowCaughtInOuterMethod_simpleTryCatch(int[] array) {
+ int val = 0;
+ try {
+ $noinline$throwingMethod(array);
+ } catch (Exception ex) {
+ val = 1;
+ }
+ return val;
+ }
+
+ private static int $noinline$throwingMethod(int[] array) {
+ return array[0];
+ }
+
+ private static int $inline$testThrowCaughtInOuterMethod_simpleTryCatch_inliningInner(int[] array) {
+ int val = 0;
+ try {
+ $inline$throwingMethod(array);
+ } catch (Exception ex) {
+ val = 1;
+ }
+ return val;
+ }
+
+ private static int $inline$throwingMethod(int[] array) {
+ return array[0];
+ }
+
+ private static int $inline$testThrowCaughtInOuterMethod_withFinally(int[] array) {
+ int val = 0;
+ try {
+ $noinline$throwingMethodWithFinally(array);
+ } catch (Exception ex) {
+ System.out.println("Our battle it will be legendary!");
+ val = 1;
+ }
+ return val;
+ }
+
+ private static int $noinline$throwingMethodWithFinally(int[] array) {
+ try {
+ return array[0];
+ } finally {
+ System.out.println("Finally, a worthy opponent!");
+ }
+ }
+}
diff --git a/test/2242-checker-lse-acquire-release-operations/Android.bp b/test/2242-checker-lse-acquire-release-operations/Android.bp
new file mode 100644
index 0000000..bb493ce
--- /dev/null
+++ b/test/2242-checker-lse-acquire-release-operations/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2242-checker-lse-acquire-release-operations`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2242-checker-lse-acquire-release-operations",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2242-checker-lse-acquire-release-operations-expected-stdout",
+ ":art-run-test-2242-checker-lse-acquire-release-operations-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2242-checker-lse-acquire-release-operations-expected-stdout",
+ out: ["art-run-test-2242-checker-lse-acquire-release-operations-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2242-checker-lse-acquire-release-operations-expected-stderr",
+ out: ["art-run-test-2242-checker-lse-acquire-release-operations-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2242-checker-lse-acquire-release-operations/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2242-checker-lse-acquire-release-operations/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2242-checker-lse-acquire-release-operations/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2242-checker-lse-acquire-release-operations/expected-stdout.txt
diff --git a/test/2242-checker-lse-acquire-release-operations/info.txt b/test/2242-checker-lse-acquire-release-operations/info.txt
new file mode 100644
index 0000000..efdc63f
--- /dev/null
+++ b/test/2242-checker-lse-acquire-release-operations/info.txt
@@ -0,0 +1,3 @@
+Tests that we perform LSE with graphs with acquire loads
+(i.e. monitor enter and volatile load) and release stores
+(i.e. monitor exit and voaltile stores)
diff --git a/test/2242-checker-lse-acquire-release-operations/src/Main.java b/test/2242-checker-lse-acquire-release-operations/src/Main.java
new file mode 100644
index 0000000..433a4cd
--- /dev/null
+++ b/test/2242-checker-lse-acquire-release-operations/src/Main.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class TestClass {
+ TestClass() {}
+ int i;
+ int j;
+ volatile int vi;
+}
+
+public class Main {
+ public static void main(String[] args) {
+ // Volatile accesses.
+ assertEquals($noinline$testVolatileAccessesMustBeKept(new TestClass()), 3);
+
+ // Volatile loads - Different fields shouldn't alias.
+ assertEquals($noinline$testVolatileLoadDifferentFields(new TestClass(), new TestClass()), 3);
+ assertEquals(
+ $noinline$testVolatileLoadDifferentFieldsBlocking(new TestClass(), new TestClass()),
+ 3);
+
+ // Volatile loads - Redundant store.
+ assertEquals($noinline$testVolatileLoadRedundantStore(new TestClass()), 2);
+ assertEquals($noinline$testVolatileLoadRedundantStoreBlocking(new TestClass()), 2);
+ assertEquals($noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(new TestClass()), 2);
+
+ // Volatile loads - Set and merge values.
+ assertEquals($noinline$testVolatileLoadSetAndMergeValues(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileLoadSetAndMergeValues(new TestClass(), false), 2);
+ assertEquals($noinline$testVolatileLoadSetAndMergeValuesBlocking(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileLoadSetAndMergeValuesBlocking(new TestClass(), false), 2);
+
+ // Volatile stores - Different fields shouldn't alias.
+ assertEquals($noinline$testVolatileStoreDifferentFields(new TestClass(), new TestClass()), 3);
+ assertEquals(
+ $noinline$testVolatileStoreDifferentFieldsBlocking(new TestClass(), new TestClass()),
+ 3);
+
+ // Volatile stores - Redundant store.
+ assertEquals($noinline$testVolatileStoreRedundantStore(new TestClass()), 2);
+ assertEquals($noinline$testVolatileStoreRedundantStoreBlocking(new TestClass()), 2);
+ assertEquals($noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(new TestClass()), 2);
+
+ // Volatile stores - Set and merge values.
+ assertEquals($noinline$testVolatileStoreSetAndMergeValues(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileStoreSetAndMergeValues(new TestClass(), false), 2);
+ assertEquals($noinline$testVolatileStoreSetAndMergeValuesNotBlocking(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileStoreSetAndMergeValuesNotBlocking(new TestClass(), false), 2);
+
+ // Monitor Operations - Different fields shouldn't alias.
+ assertEquals(
+ $noinline$testMonitorOperationDifferentFields(new TestClass(), new TestClass()), 3);
+ assertEquals($noinline$testMonitorOperationDifferentFieldsBlocking(
+ new TestClass(), new TestClass()),
+ 3);
+
+ // Monitor Operations - Redundant store.
+ assertEquals($noinline$testMonitorOperationRedundantStore(new TestClass()), 2);
+ assertEquals($noinline$testMonitorOperationRedundantStoreBlocking(new TestClass()), 2);
+ assertEquals(
+ $noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(new TestClass()), 2);
+ assertEquals($noinline$testMonitorOperationRedundantStoreBlockingExit(new TestClass()), 2);
+
+ // Monitor Operations - Set and merge values.
+ assertEquals($noinline$testMonitorOperationSetAndMergeValues(new TestClass(), true), 1);
+ assertEquals($noinline$testMonitorOperationSetAndMergeValues(new TestClass(), false), 2);
+ assertEquals(
+ $noinline$testMonitorOperationSetAndMergeValuesBlocking(new TestClass(), true), 1);
+ assertEquals(
+ $noinline$testMonitorOperationSetAndMergeValuesBlocking(new TestClass(), false), 2);
+ }
+
+ public static void assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileAccessesMustBeKept(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileAccessesMustBeKept(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ static int $noinline$testVolatileAccessesMustBeKept(TestClass obj1) {
+ int result;
+ obj1.vi = 3;
+ // Redundant load that has to be kept.
+ result = obj1.vi;
+ result = obj1.vi;
+ // Redundant store that has to be kept.
+ obj1.vi = 3;
+ result = obj1.vi;
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.i
+ /// CHECK: InstanceFieldGet field_name:TestClass.j
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.i
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+
+ // Unrelated volatile loads shouldn't block LSE.
+ static int $noinline$testVolatileLoadDifferentFields(TestClass obj1, TestClass obj2) {
+ int unused = obj1.vi;
+ obj1.i = 1;
+ obj2.j = 2;
+ int result = obj1.i + obj2.j;
+ unused = obj1.vi;
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ // A volatile load blocks load elimination.
+ static int $noinline$testVolatileLoadDifferentFieldsBlocking(TestClass obj1, TestClass obj2) {
+ obj1.i = 1;
+ obj2.j = 2;
+ int unused = obj1.vi;
+ return obj1.i + obj2.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldGet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK-NOT: InstanceFieldSet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+ static int $noinline$testVolatileLoadRedundantStore(TestClass obj) {
+ int unused = obj.vi;
+ obj.j = 1;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+ static int $noinline$testVolatileLoadRedundantStoreBlocking(TestClass obj) {
+ // This store must be kept due to the volatile load.
+ obj.j = 1;
+ int unused = obj.vi;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ static int $noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass obj) {
+ // This store can be safely removed.
+ obj.j = 1;
+ obj.j = 2;
+ int unused = obj.vi;
+ // This load remains due to the volatile load in the middle.
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.i
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.i
+
+ static int $noinline$testVolatileLoadSetAndMergeValues(TestClass obj, boolean b) {
+ if (b) {
+ int unused = obj.vi;
+ obj.i = 1;
+ } else {
+ int unused = obj.vi;
+ obj.i = 2;
+ }
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.i
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+
+ static int $noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass obj, boolean b) {
+ if (b) {
+ obj.i = 1;
+ } else {
+ obj.i = 2;
+ }
+ int unused = obj.vi;
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.i
+ /// CHECK: InstanceFieldGet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.i
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+
+ // Unrelated volatile stores shouldn't block LSE.
+ static int $noinline$testVolatileStoreDifferentFields(TestClass obj1, TestClass obj2) {
+ obj1.vi = 123;
+ obj1.i = 1;
+ obj2.j = 2;
+ int result = obj1.i + obj2.j;
+ obj1.vi = 123;
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ // A volatile store doesn't block load elimination, as it doesn't clobber existing values.
+ static int $noinline$testVolatileStoreDifferentFieldsBlocking(TestClass obj1, TestClass obj2) {
+ obj1.i = 1;
+ obj2.j = 2;
+ obj1.vi = 123;
+ return obj1.i + obj2.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK-NOT: InstanceFieldSet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreRedundantStore(TestClass obj) {
+ obj.vi = 123;
+ obj.j = 1;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreRedundantStoreBlocking(TestClass obj) {
+ // This store must be kept due to the volatile store.
+ obj.j = 1;
+ obj.vi = 123;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK-NOT: InstanceFieldSet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass obj) {
+ // This store can be safely removed.
+ obj.j = 1;
+ obj.j = 2;
+ obj.vi = 123;
+ // This load can also be safely eliminated as the volatile store doesn't clobber values.
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreSetAndMergeValues(TestClass obj, boolean b) {
+ if (b) {
+ obj.vi = 123;
+ obj.i = 1;
+ } else {
+ obj.vi = 123;
+ obj.i = 2;
+ }
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValuesNotBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValuesNotBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreSetAndMergeValuesNotBlocking(TestClass obj, boolean b) {
+ if (b) {
+ obj.i = 1;
+ } else {
+ obj.i = 2;
+ }
+ // This volatile store doesn't block the load elimination
+ obj.vi = 123;
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ // Unrelated monitor operations shouldn't block LSE.
+ static int $noinline$testMonitorOperationDifferentFields(TestClass obj1, TestClass obj2) {
+ Main m = new Main();
+ synchronized (m) {}
+
+ obj1.i = 1;
+ obj2.j = 2;
+ int result = obj1.i + obj2.j;
+
+ synchronized (m) {}
+
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ // A synchronized operation blocks loads.
+ static int $noinline$testMonitorOperationDifferentFieldsBlocking(TestClass obj1, TestClass obj2) {
+ Main m = new Main();
+
+ obj1.i = 1;
+ obj2.j = 2;
+ synchronized (m) {
+ return obj1.i + obj2.j;
+ }
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStore(TestClass obj) {
+ Main m = new Main();
+ synchronized (m) {
+ obj.j = 1;
+ obj.j = 2;
+ }
+
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStoreBlocking(TestClass obj) {
+ Main m = new Main();
+
+ // This store must be kept due to the monitor operation.
+ obj.j = 1;
+ synchronized (m) {}
+ obj.j = 2;
+
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass obj) {
+ Main m = new Main();
+
+ // This store can be safely removed.
+ obj.j = 1;
+ obj.j = 2;
+ synchronized (m) {}
+
+ // This load remains due to the monitor operation.
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass obj) {
+ Main m = new Main();
+
+ synchronized (m) {
+ // This store can be removed.
+ obj.j = 0;
+ // This store must be kept due to the monitor exit operation.
+ obj.j = 1;
+ }
+ obj.j = 2;
+
+ return obj.j;
+ }
+
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: MonitorOperation kind:enter
+ /// CHECK-DAG: MonitorOperation kind:exit
+ /// CHECK-DAG: MonitorOperation kind:enter
+ /// CHECK-DAG: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationSetAndMergeValues(TestClass obj, boolean b) {
+ Main m = new Main();
+
+ if (b) {
+ synchronized (m) {}
+ obj.i = 1;
+ } else {
+ synchronized (m) {}
+ obj.i = 2;
+ }
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: MonitorOperation kind:enter
+ /// CHECK-DAG: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass obj, boolean b) {
+ Main m = new Main();
+
+ if (b) {
+ obj.i = 1;
+ } else {
+ obj.i = 2;
+ }
+ synchronized (m) {}
+ return obj.i;
+ }
+}
diff --git a/test/2243-checker-not-inline-into-throw/Android.bp b/test/2243-checker-not-inline-into-throw/Android.bp
new file mode 100644
index 0000000..78c5c66
--- /dev/null
+++ b/test/2243-checker-not-inline-into-throw/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2243-checker-not-inline-into-throw`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2243-checker-not-inline-into-throw",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2243-checker-not-inline-into-throw-expected-stdout",
+ ":art-run-test-2243-checker-not-inline-into-throw-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2243-checker-not-inline-into-throw-expected-stdout",
+ out: ["art-run-test-2243-checker-not-inline-into-throw-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2243-checker-not-inline-into-throw-expected-stderr",
+ out: ["art-run-test-2243-checker-not-inline-into-throw-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2243-checker-not-inline-into-throw/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2243-checker-not-inline-into-throw/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2243-checker-not-inline-into-throw/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2243-checker-not-inline-into-throw/expected-stdout.txt
diff --git a/test/2243-checker-not-inline-into-throw/info.txt b/test/2243-checker-not-inline-into-throw/info.txt
new file mode 100644
index 0000000..a2ded81
--- /dev/null
+++ b/test/2243-checker-not-inline-into-throw/info.txt
@@ -0,0 +1 @@
+Tests that we don't inline methods if their basic blocks end with a throw.
diff --git a/test/2243-checker-not-inline-into-throw/src/Main.java b/test/2243-checker-not-inline-into-throw/src/Main.java
new file mode 100644
index 0000000..6f1280c
--- /dev/null
+++ b/test/2243-checker-not-inline-into-throw/src/Main.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try {
+ $noinline$testEndsWithThrow();
+ throw new Exception("Unreachable");
+ } catch (Error expected) {
+ }
+
+ try {
+ $noinline$testEndsWithThrowButNotDirectly();
+ throw new Exception("Unreachable");
+ } catch (Error expected) {
+ }
+ }
+
+ // Empty methods are easy to inline anywhere.
+ private static void easyToInline() {}
+ private static void $inline$easyToInline() {}
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow() inliner (before)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.easyToInline
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow() inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.easyToInline
+ static int $noinline$testEndsWithThrow() {
+ easyToInline();
+ throw new Error("");
+ }
+
+ // Currently we only stop inlining if the method's basic block ends with a throw. We do not stop
+ // inlining for methods that eventually always end with a throw.
+ static int $noinline$testEndsWithThrowButNotDirectly() {
+ $inline$easyToInline();
+ if (justABoolean) {
+ $inline$easyToInline();
+ } else {
+ $inline$easyToInline();
+ }
+ throw new Error("");
+ }
+
+ static boolean justABoolean;
+}
diff --git a/test/2244-checker-remove-try-boundary/Android.bp b/test/2244-checker-remove-try-boundary/Android.bp
new file mode 100644
index 0000000..9e03692
--- /dev/null
+++ b/test/2244-checker-remove-try-boundary/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2244-checker-remove-try-boundary`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2244-checker-remove-try-boundary",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2244-checker-remove-try-boundary-expected-stdout",
+ ":art-run-test-2244-checker-remove-try-boundary-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2244-checker-remove-try-boundary-expected-stdout",
+ out: ["art-run-test-2244-checker-remove-try-boundary-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2244-checker-remove-try-boundary-expected-stderr",
+ out: ["art-run-test-2244-checker-remove-try-boundary-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2244-checker-remove-try-boundary/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2244-checker-remove-try-boundary/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2244-checker-remove-try-boundary/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2244-checker-remove-try-boundary/expected-stdout.txt
diff --git a/test/2244-checker-remove-try-boundary/info.txt b/test/2244-checker-remove-try-boundary/info.txt
new file mode 100644
index 0000000..4247e14
--- /dev/null
+++ b/test/2244-checker-remove-try-boundary/info.txt
@@ -0,0 +1,2 @@
+Tests that we remove TryBoundary instructions if doesn't contain instructions that can throw.
+Sometimes we can remove the catch blocks too.
diff --git a/test/2244-checker-remove-try-boundary/src/Main.java b/test/2244-checker-remove-try-boundary/src/Main.java
new file mode 100644
index 0000000..1b616a5
--- /dev/null
+++ b/test/2244-checker-remove-try-boundary/src/Main.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ assertEquals(2, $noinline$testDivideOverTen(20));
+ assertEquals(-2, $noinline$testDivideOverTen(-20));
+ assertEquals(0, $noinline$testSimpleDivisionInLoop(0));
+ assertEquals(1, $noinline$testSimpleDivisionInLoop(81));
+ assertEquals(10, $noinline$testOptimizeSeparateBranches(60, true));
+ assertEquals(10, $noinline$testOptimizeSeparateBranches(80, false));
+ assertEquals(1, $noinline$testDoNotOptimizeOneBranchThrows(81, false));
+ assertEquals(-1000, $noinline$testDoNotOptimizeOneBranchThrows(81, true));
+ assertEquals(1, $noinline$testOptimizeAfterOneBranchDisappears(81, false));
+ assertEquals(10, $noinline$testRemoveTryBoundaryNested(60));
+ assertEquals(-2000, $noinline$testRemoveTryBoundaryNestedButNotCatch(60, true));
+ assertEquals(30, $noinline$testRemoveTryBoundaryNestedButNotCatch(60, false));
+ assertEquals(30, $noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(60, false));
+ }
+
+ public static void assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ // Check that this version cannot remove the TryBoundary instructions since we may throw.
+
+ /// CHECK-START: int Main.$inline$division(int, int) register (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$inline$division(int, int) register (after)
+ /// CHECK: flags "catch_block"
+ private static int $inline$division(int a, int b) {
+ try {
+ return a / b;
+ } catch (Error unexpected) {
+ return -1000;
+ }
+ }
+
+ // Check that we can remove the TryBoundary afer inlining since we know we can't throw.
+
+ /// CHECK-START: int Main.$noinline$testDivideOverTen(int) inliner (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testDivideOverTen(int) inliner (after)
+ /// CHECK-NOT: flags "catch_block"
+ private static int $noinline$testDivideOverTen(int a) {
+ return $inline$division(a, 10);
+ }
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ private static int $noinline$testSimpleDivisionInLoop(int a) {
+ try {
+ for (int i = 0; i < 4; i++) {
+ a /= 3;
+ }
+ } catch (Error unexpected) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // Even though the `TryBoundary`s are split, we can remove them as nothing in the try can throw.
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ private static int $noinline$testOptimizeSeparateBranches(int a, boolean val) {
+ try {
+ if (val) {
+ // TryBoundary kind:entry
+ a /= 3;
+ } else {
+ // TryBoundary kind:entry
+ a /= 4;
+ }
+ a /= 2;
+ // TryBoundary kind:exit
+ } catch (Error unexpected) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // Even though the `a /= 3;` can't throw, we don't eliminate any `TryBoundary` instructions. This
+ // is because we have the `throw new Error();` in the try as well. We could potentially support
+ // removing some `TryBoundary` instructions and not all in the try, but this would complicate the
+ // code and wouldn't bring code size reductions since we would be unable to remove the catch
+ // block.
+
+ /// CHECK-START: int Main.$noinline$testDoNotOptimizeOneBranchThrows(int, boolean) register (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testDoNotOptimizeOneBranchThrows(int, boolean) register (after)
+ /// CHECK: flags "catch_block"
+ public static int $noinline$testDoNotOptimizeOneBranchThrows(int a, boolean val) {
+ try {
+ for (int i = 0; i < 4; i++) {
+ // TryBoundary kind:entry
+ a /= 3;
+ // TryBoundary kind:exit
+ }
+
+ if (val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ } catch (Error e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // The throw gets eliminated by `SimplifyIfs` in DCE, so we can detect that nothing can throw in
+ // the graph and eliminate the `TryBoundary` instructions.
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: Throw
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: Throw
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ public static int $noinline$testOptimizeAfterOneBranchDisappears(int a, boolean val) {
+ try {
+ for (int i = 0; i < 4; i++) {
+ // TryBoundary kind:entry
+ a /= 3;
+ // TryBoundary kind:exit
+ }
+
+ if (val && !val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ } catch (Error e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ public static int $noinline$testRemoveTryBoundaryNested(int a) {
+ try {
+ // TryBoundary kind:entry
+ a /= 2;
+ // TryBoundary kind:exit
+ try {
+ // TryBoundary kind:entry
+ a /= 3;
+ // TryBoundary kind:exit
+ } catch (Error e) {
+ return -2000;
+ }
+ } catch (Exception e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // We can remove the `TryBoundary` instructions surrounding `a /= 2;` but since the inner try can
+ // throw, we must keep both the inner and outer catches as they are catch handlers of the inner
+ // try.
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+ public static int $noinline$testRemoveTryBoundaryNestedButNotCatch(int a, boolean val) {
+ try {
+ // TryBoundary kind:entry
+ a /= 2;
+ // TryBoundary kind:exit
+ try {
+ if (val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ // TryBoundary kind:exit
+ } catch (Error e) {
+ return -2000;
+ }
+ } catch (Exception e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // We eliminate the return -1000 catch block which is outside of the loop in
+ // dead_code_elimination$initial. We can do so since we eliminated the TryBoundary of `a /= 2;`.
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: IntConstant -1000
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: IntConstant -1000
+
+ // When removing that block, we are removing a block outside of a loop but we still need to update
+ // the loop information in the graph since we removed TryBoundary instructions inside of a loop
+ // and now `a /= 2;` is not considered part of a loop (Cannot throw so it will not `continue` and
+ // will always return).
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: Div loop:B2
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: Div loop:B2
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: Div
+ /// CHECK-NOT: Div
+ public static int $noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(
+ int a, boolean val) {
+ try {
+ for (int i = 0; i < 4; ++i) {
+ try {
+ try {
+ if (val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ // TryBoundary kind:exit
+ } catch (Exception e) {
+ continue;
+ }
+ // TryBoundary kind:entry
+ a /= 2;
+ // TryBoundary kind:exit
+ return a;
+ } catch (Error e) {
+ continue;
+ }
+ }
+ } catch (Exception e) {
+ return -1000;
+ }
+ return a;
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2245-checker-smali-instance-of-comparison/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2245-checker-smali-instance-of-comparison/expected-stderr.txt
diff --git a/test/2245-checker-smali-instance-of-comparison/expected-stdout.txt b/test/2245-checker-smali-instance-of-comparison/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/2245-checker-smali-instance-of-comparison/info.txt b/test/2245-checker-smali-instance-of-comparison/info.txt
new file mode 100644
index 0000000..5c6df8e
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/info.txt
@@ -0,0 +1 @@
+Smali test comparing instance-of (which returns 0 or 1) with a constant 2.
diff --git a/test/2245-checker-smali-instance-of-comparison/smali/b_252804549.smali b/test/2245-checker-smali-instance-of-comparison/smali/b_252804549.smali
new file mode 100644
index 0000000..38d4d0c
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/smali/b_252804549.smali
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+.class public LB252804549;
+
+.super Ljava/lang/Object;
+
+## CHECK-START: int B252804549.compareInstanceOfWithTwo(java.lang.Object) builder (after)
+## CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+## CHECK-DAG: <<InstanceOf:z\d+>> InstanceOf
+## CHECK-DAG: <<Eq:z\d+>> Equal [<<InstanceOf>>,<<Const2>>]
+## CHECK-DAG: If [<<Eq>>]
+.method public static compareInstanceOfWithTwo(Ljava/lang/Object;)I
+ .registers 2
+ instance-of v0, p0, Ljava/lang/String;
+ const/4 v1, 0x2
+ # Compare instance-of with 2 (i.e. neither 0 nor 1)
+ if-eq v0, v1, :Lequal
+ const/4 v1, 0x3
+ return v1
+:Lequal
+ return v1
+.end method
diff --git a/test/2245-checker-smali-instance-of-comparison/src/Main.java b/test/2245-checker-smali-instance-of-comparison/src/Main.java
new file mode 100644
index 0000000..cd9003a
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 java.lang.reflect.Method;
+
+/**
+ * Smali exercise, copied from 800-smali and modified for this test case.
+ */
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Object retValue = Class.forName("B252804549")
+ .getDeclaredMethod("compareInstanceOfWithTwo", Object.class)
+ .invoke(null, new Object[] {new Object()});
+ if (retValue == null || !retValue.equals(3)) {
+ throw new Exception("Expected 3, but got " + retValue);
+ }
+ }
+}
diff --git a/test/2246-trace-stream/Android.bp b/test/2246-trace-stream/Android.bp
new file mode 100644
index 0000000..bef9deb
--- /dev/null
+++ b/test/2246-trace-stream/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2246-trace-stream`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2246-trace-stream",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2246-trace-stream-expected-stdout",
+ ":art-run-test-2246-trace-stream-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2246-trace-stream-expected-stdout",
+ out: ["art-run-test-2246-trace-stream-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2246-trace-stream-expected-stderr",
+ out: ["art-run-test-2246-trace-stream-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2246-trace-stream/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2246-trace-stream/expected-stderr.txt
diff --git a/test/2246-trace-stream/expected-stdout.txt b/test/2246-trace-stream/expected-stdout.txt
new file mode 100644
index 0000000..c9d7405
--- /dev/null
+++ b/test/2246-trace-stream/expected-stdout.txt
@@ -0,0 +1,72 @@
+.>> TestThread2246 java.lang.Thread run ()V Thread.java
+..>> TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
+...>> TestThread2246 Main lambda$main$0 ()V Main.java
+....>> TestThread2246 Main <init> ()V Main.java
+.....>> TestThread2246 java.lang.Object <init> ()V Object.java
+.....<< TestThread2246 java.lang.Object <init> ()V Object.java
+....<< TestThread2246 Main <init> ()V Main.java
+....>> TestThread2246 Main $noinline$doSomeWork ()V Main.java
+.....>> TestThread2246 Main callOuterFunction ()V Main.java
+......>> TestThread2246 Main callLeafFunction ()V Main.java
+......<< TestThread2246 Main callLeafFunction ()V Main.java
+.....<< TestThread2246 Main callOuterFunction ()V Main.java
+.....>> TestThread2246 Main callLeafFunction ()V Main.java
+.....<< TestThread2246 Main callLeafFunction ()V Main.java
+....<< TestThread2246 Main $noinline$doSomeWork ()V Main.java
+...<< TestThread2246 Main lambda$main$0 ()V Main.java
+..<< TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
+.<< TestThread2246 java.lang.Thread run ()V Thread.java
+.>> main Main main ([Ljava/lang/String;)V Main.java
+..>> main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+...>> main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+....>> main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+.....>> main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+.....<< main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+....<< main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+...<< main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+..<< main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+..>> main java.lang.Thread start ()V Thread.java
+...>> main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+...<< main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+...>> main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+...<< main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+..<< main java.lang.Thread start ()V Thread.java
+..>> main java.lang.Thread join ()V Thread.java
+...>> main java.lang.Thread join (J)V Thread.java
+....>> main java.lang.System currentTimeMillis ()J System.java
+....<< main java.lang.System currentTimeMillis ()J System.java
+....>> main java.lang.Thread isAlive ()Z Thread.java
+....<< main java.lang.Thread isAlive ()Z Thread.java
+....>> main java.lang.Object wait (J)V Object.java
+.....>> main java.lang.Object wait (JI)V Object.java
+.....<< main java.lang.Object wait (JI)V Object.java
+....<< main java.lang.Object wait (J)V Object.java
+....>> main java.lang.Thread isAlive ()Z Thread.java
+....<< main java.lang.Thread isAlive ()Z Thread.java
+...<< main java.lang.Thread join (J)V Thread.java
+..<< main java.lang.Thread join ()V Thread.java
+..>> main Main $noinline$doSomeWork ()V Main.java
+...>> main Main callOuterFunction ()V Main.java
+....>> main Main callLeafFunction ()V Main.java
+....<< main Main callLeafFunction ()V Main.java
+...<< main Main callOuterFunction ()V Main.java
+...>> main Main callLeafFunction ()V Main.java
+...<< main Main callLeafFunction ()V Main.java
+..<< main Main $noinline$doSomeWork ()V Main.java
+..>> main Main doSomeWorkThrow ()V Main.java
+...>> main Main callThrowFunction ()V Main.java
+....>> main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+.....>> main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+......>> main java.lang.Object <init> ()V Object.java
+......<< main java.lang.Object <init> ()V Object.java
+......>> main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+......<< main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+......>> main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+.......>> main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+.......<< main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+......<< main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+.....<< main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+....<< main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+...<<E main Main callThrowFunction ()V Main.java
+..<< main Main doSomeWorkThrow ()V Main.java
+..>> main Main$VMDebug $noinline$stopMethodTracing ()V Main.java
diff --git a/test/2246-trace-stream/info.txt b/test/2246-trace-stream/info.txt
new file mode 100644
index 0000000..fa93a97
--- /dev/null
+++ b/test/2246-trace-stream/info.txt
@@ -0,0 +1,2 @@
+Tests streaming method tracing. It verifies the format of the generated file is
+as expected.
diff --git a/test/2246-trace-stream/run.py b/test/2246-trace-stream/run.py
new file mode 100644
index 0000000..955f39a
--- /dev/null
+++ b/test/2246-trace-stream/run.py
@@ -0,0 +1,22 @@
+#!/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.
+
+
+def run(ctx, args):
+ # The expected output non debuggable isn't consistent in all configurations.
+ # Investigate why the output is different and update the test to work for non
+ # debuggable runtimes too.
+ ctx.default_run(args, Xcompiler_option=["--debuggable"])
diff --git a/test/2246-trace-stream/src/Main.java b/test/2246-trace-stream/src/Main.java
new file mode 100644
index 0000000..37870f1
--- /dev/null
+++ b/test/2246-trace-stream/src/Main.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 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 java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+public class Main {
+ private static final String TEMP_FILE_NAME_PREFIX = "test";
+ private static final String TEMP_FILE_NAME_SUFFIX = ".trace";
+ private static File file;
+
+ public static void main(String[] args) throws Exception {
+ String name = System.getProperty("java.vm.name");
+ if (!"Dalvik".equals(name)) {
+ System.out.println("This test is not supported on " + name);
+ return;
+ }
+ file = createTempFile();
+ FileOutputStream out_file = new FileOutputStream(file);
+ Main m = new Main();
+ Thread t = new Thread(() -> {
+ Main m1 = new Main();
+ m1.$noinline$doSomeWork();
+ }, "TestThread2246");
+ try {
+ if (VMDebug.getMethodTracingMode() != 0) {
+ VMDebug.$noinline$stopMethodTracing();
+ }
+
+ VMDebug.startMethodTracing(file.getPath(), out_file.getFD(), 0, 0, false, 0, true);
+ t.start();
+ t.join();
+ m.$noinline$doSomeWork();
+ m.doSomeWorkThrow();
+ VMDebug.$noinline$stopMethodTracing();
+ out_file.close();
+ m.CheckTraceFileFormat(file);
+ } finally {
+ if (out_file != null) {
+ out_file.close();
+ }
+ }
+ }
+
+ private void CheckTraceFileFormat(File trace_file) throws Exception {
+ StreamTraceParser parser = new StreamTraceParser(trace_file);
+ parser.validateTraceHeader(StreamTraceParser.TRACE_VERSION_DUAL_CLOCK);
+ boolean has_entries = true;
+ boolean seen_stop_tracing_method = false;
+ while (has_entries) {
+ int header_type = parser.GetEntryHeader();
+ switch (header_type) {
+ case 1:
+ parser.ProcessMethodInfoEntry();
+ break;
+ case 2:
+ parser.ProcessThreadInfoEntry();
+ break;
+ case 3:
+ // TODO(mythria): Add test to also check format of trace summary.
+ has_entries = false;
+ break;
+ default:
+ String event_string = parser.ProcessEventEntry(header_type);
+ // Ignore events after method tracing was stopped. The code that is executed
+ // later could be non-deterministic.
+ if (!seen_stop_tracing_method) {
+ System.out.println(event_string);
+ }
+ if (event_string.contains("Main$VMDebug $noinline$stopMethodTracing")) {
+ seen_stop_tracing_method = true;
+ }
+ }
+ }
+ parser.closeFile();
+ }
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+
+ public void callOuterFunction() {
+ callLeafFunction();
+ }
+
+ public void callLeafFunction() {}
+
+ public void $noinline$doSomeWork() {
+ callOuterFunction();
+ callLeafFunction();
+ }
+
+ public void callThrowFunction() throws Exception {
+ throw new Exception("test");
+ }
+
+ public void doSomeWorkThrow() {
+ try {
+ callThrowFunction();
+ } catch (Exception e) {
+ }
+ }
+
+ private static class VMDebug {
+ private static final Method startMethodTracingMethod;
+ private static final Method stopMethodTracingMethod;
+ private static final Method getMethodTracingModeMethod;
+ static {
+ try {
+ Class<?> c = Class.forName("dalvik.system.VMDebug");
+ startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
+ FileDescriptor.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE,
+ Integer.TYPE, Boolean.TYPE);
+ stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
+ getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void startMethodTracing(String filename, FileDescriptor fd, int bufferSize,
+ int flags, boolean samplingEnabled, int intervalUs, boolean streaming)
+ throws Exception {
+ startMethodTracingMethod.invoke(
+ null, filename, fd, bufferSize, flags, samplingEnabled, intervalUs, streaming);
+ }
+ public static void $noinline$stopMethodTracing() throws Exception {
+ stopMethodTracingMethod.invoke(null);
+ }
+ public static int getMethodTracingMode() throws Exception {
+ return (int) getMethodTracingModeMethod.invoke(null);
+ }
+ }
+}
diff --git a/test/2246-trace-stream/src/StreamTraceParser.java b/test/2246-trace-stream/src/StreamTraceParser.java
new file mode 100644
index 0000000..8995342
--- /dev/null
+++ b/test/2246-trace-stream/src/StreamTraceParser.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 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 java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+
+public class StreamTraceParser {
+ public static final int MAGIC_NUMBER = 0x574f4c53;
+ public static final int DUAL_CLOCK_VERSION = 3;
+ public static final int TRACE_VERSION_DUAL_CLOCK = 0xF3;
+
+ public StreamTraceParser(File file) throws IOException {
+ dataStream = new DataInputStream(new FileInputStream(file));
+ method_id_map = new HashMap<Integer, String>();
+ thread_id_map = new HashMap<Integer, String>();
+ }
+
+ public void closeFile() throws IOException {
+ dataStream.close();
+ }
+
+ public String readString(int num_bytes) throws IOException {
+ byte[] buffer = new byte[num_bytes];
+ dataStream.readFully(buffer);
+ return new String(buffer, StandardCharsets.UTF_8);
+ }
+
+ public int readNumber(int num_bytes) throws IOException {
+ int number = 0;
+ for (int i = 0; i < num_bytes; i++) {
+ number += dataStream.readUnsignedByte() << (i * 8);
+ }
+ return number;
+ }
+
+ public void validateTraceHeader(int expected_version) throws Exception {
+ // Read 4-byte magic_number
+ int magic_number = readNumber(4);
+ if (magic_number != MAGIC_NUMBER) {
+ throw new Exception("Magic number doesn't match. Expected "
+ + Integer.toHexString(MAGIC_NUMBER) + " Got "
+ + Integer.toHexString(magic_number));
+ }
+ // Read 2-byte version
+ int version = readNumber(2);
+ if (version != expected_version) {
+ throw new Exception(
+ "Unexpected version. Expected " + expected_version + " Got " + version);
+ }
+ trace_format_version = version & 0xF;
+ // Read 2-byte header_length length
+ int header_length = readNumber(2);
+ // Read 8-byte starting time - Ignore timestamps since they are not deterministic
+ dataStream.skipBytes(8);
+ // 4 byte magic_number + 2 byte version + 2 byte offset + 8 byte timestamp
+ int num_bytes_read = 16;
+ if (version >= DUAL_CLOCK_VERSION) {
+ // Read 2-byte record size.
+ // TODO(mythria): Check why this is needed. We can derive record_size from version. Not
+ // sure why this is needed.
+ record_size = readNumber(2);
+ num_bytes_read += 2;
+ }
+ // Skip any padding
+ if (header_length > num_bytes_read) {
+ dataStream.skipBytes(header_length - num_bytes_read);
+ }
+ }
+
+ public int GetEntryHeader() throws IOException {
+ // Read 2-byte thread-id. On host thread-ids can be greater than 16-bit.
+ int thread_id = readNumber(2);
+ if (thread_id != 0) {
+ return thread_id;
+ }
+ // Read 1-byte header type
+ return readNumber(1);
+ }
+
+ public void ProcessMethodInfoEntry() throws IOException {
+ // Read 2-byte method info size
+ int header_length = readNumber(2);
+ // Read header_size data.
+ String method_info = readString(header_length);
+ String[] tokens = method_info.split("\t", 2);
+ // Get method_id and record method_id -> method_name map.
+ int method_id = Integer.decode(tokens[0]);
+ String method_line = tokens[1].replace('\t', ' ');
+ method_line = method_line.substring(0, method_line.length() - 1);
+ method_id_map.put(method_id, method_line);
+ }
+
+ public void ProcessThreadInfoEntry() throws IOException {
+ // Read 2-byte thread id
+ int thread_id = readNumber(2);
+ // Read 2-byte thread info size
+ int header_length = readNumber(2);
+ // Read header_size data.
+ String thread_info = readString(header_length);
+ thread_id_map.put(thread_id, thread_info);
+ }
+
+ public String eventTypeToString(int event_type) {
+ String str = "";
+ for (int i = 0; i < nesting_level; i++) {
+ str += ".";
+ }
+ switch (event_type) {
+ case 0:
+ nesting_level++;
+ str += ".>>";
+ break;
+ case 1:
+ nesting_level--;
+ str += "<<";
+ break;
+ case 2:
+ nesting_level--;
+ str += "<<E";
+ break;
+ default:
+ str += "??";
+ }
+ return str;
+ }
+
+ public String ProcessEventEntry(int thread_id) throws IOException {
+ // Read 4-byte method value
+ int method_and_event = readNumber(4);
+ int method_id = method_and_event & ~0x3;
+ int event_type = method_and_event & 0x3;
+
+ String str = eventTypeToString(event_type) + " " + thread_id_map.get(thread_id) + " "
+ + method_id_map.get(method_id);
+ // Depending on the version skip either one or two timestamps.
+ // TODO(mythria): Probably add a check that time stamps are always greater than initial
+ // timestamp.
+ int num_bytes_timestamp = (trace_format_version == 2) ? 4 : 8;
+ dataStream.skipBytes(num_bytes_timestamp);
+ return str;
+ }
+
+ DataInputStream dataStream;
+ HashMap<Integer, String> method_id_map;
+ HashMap<Integer, String> thread_id_map;
+ int record_size = 0;
+ int trace_format_version = 0;
+ int nesting_level = 0;
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2247-checker-write-barrier-elimination/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2247-checker-write-barrier-elimination/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2247-checker-write-barrier-elimination/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2247-checker-write-barrier-elimination/expected-stdout.txt
diff --git a/test/2247-checker-write-barrier-elimination/info.txt b/test/2247-checker-write-barrier-elimination/info.txt
new file mode 100644
index 0000000..2515317
--- /dev/null
+++ b/test/2247-checker-write-barrier-elimination/info.txt
@@ -0,0 +1 @@
+Tests that we eliminate unneeded write barriers.
diff --git a/test/2247-checker-write-barrier-elimination/src/Main.java b/test/2247-checker-write-barrier-elimination/src/Main.java
new file mode 100644
index 0000000..76fb05a
--- /dev/null
+++ b/test/2247-checker-write-barrier-elimination/src/Main.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class MultipleObject {
+ Object inner;
+ Object inner2;
+
+ static Object inner_static;
+}
+
+public class Main {
+ public static void main(String[] args) throws Error {
+ // Several sets, same receiver.
+ $noinline$testInstanceFieldSets(new Main(), new Object(), new Object(), new Object());
+ $noinline$testStaticFieldSets(new Object(), new Object(), new Object());
+ // Object ArraySets can throw since they need a type check so we cannot perform the
+ // optimization.
+ $noinline$testArraySets(new Object[3], new Object(), new Object(), new Object());
+ // If we are swapping elements in the array, no need for a type check.
+ $noinline$testSwapArray(new Object[3]);
+ // If the array and the values have the same RTI, no need for a type check.
+ $noinline$testArraySetsSameRTI();
+
+ // We cannot rely on `null` sets to perform the optimization.
+ $noinline$testNullInstanceFieldSets(new Main(), new Object());
+ $noinline$testNullStaticFieldSets(new Object());
+ $noinline$testNullArraySets(new Object[3], new Object());
+
+ // Several sets, multiple receivers. (set obj1, obj2, obj1 and see that the card of obj1
+ // gets eliminated)
+ $noinline$testInstanceFieldSetsMultipleReceivers(
+ new Main(), new Object(), new Object(), new Object());
+ $noinline$testStaticFieldSetsMultipleReceivers(new Object(), new Object(), new Object());
+ $noinline$testArraySetsMultipleReceiversSameRTI();
+
+ // The write barrier elimination optimization is blocked by invokes, suspend checks, and
+ // instructions that can throw.
+ $noinline$testInstanceFieldSetsBlocked(
+ new Main(), new Object(), new Object(), new Object());
+ $noinline$testStaticFieldSetsBlocked(new Object(), new Object(), new Object());
+ $noinline$testArraySetsSameRTIBlocked();
+ }
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSets(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSets(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testInstanceFieldSets(Main m, Object o, Object o2, Object o3) {
+ m.inner = o;
+ m.inner2 = o2;
+ m.inner3 = o3;
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSets(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSets(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testStaticFieldSets(Object o, Object o2, Object o3) {
+ inner_static = o;
+ inner_static2 = o2;
+ inner_static3 = o3;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySets(java.lang.Object[], java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySets(java.lang.Object[], java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testArraySets(
+ Object[] arr, Object o, Object o2, Object o3) {
+ arr[0] = o;
+ arr[1] = o2;
+ arr[2] = o3;
+ return arr;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testSwapArray(java.lang.Object[]) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testSwapArray(java.lang.Object[]) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testSwapArray(Object[] arr) {
+ arr[0] = arr[1];
+ arr[1] = arr[2];
+ arr[2] = arr[0];
+ return arr;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTI() disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTI() disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testArraySetsSameRTI() {
+ Object[] arr = new Object[3];
+ arr[0] = inner_static;
+ arr[1] = inner_static2;
+ arr[2] = inner_static3;
+ return arr;
+ }
+
+ /// CHECK-START: Main Main.$noinline$testNullInstanceFieldSets(Main, java.lang.Object) disassembly (after)
+ /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: Main Main.$noinline$testNullInstanceFieldSets(Main, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testNullInstanceFieldSets(Main m, Object o) {
+ m.inner = null;
+ m.inner2 = o;
+ m.inner3 = null;
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testNullStaticFieldSets(java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: void Main.$noinline$testNullStaticFieldSets(java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testNullStaticFieldSets(Object o) {
+ inner_static = null;
+ inner_static2 = o;
+ inner_static3 = null;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testNullArraySets(java.lang.Object[], java.lang.Object) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testNullArraySets(java.lang.Object[], java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Object[] $noinline$testNullArraySets(Object[] arr, Object o) {
+ arr[0] = null;
+ arr[1] = o;
+ arr[2] = null;
+ return arr;
+ }
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsMultipleReceivers(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ // There are two extra card_tables for the initialization of the MultipleObject.
+ /// CHECK: InstanceFieldSet field_name:MultipleObject.inner field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: InstanceFieldSet field_name:MultipleObject.inner field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InstanceFieldSet field_name:MultipleObject.inner2 field_type:Reference write_barrier_kind:DontEmit
+
+ // Each one of the two NewInstance instructions have their own `card_table` reference.
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsMultipleReceivers(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testInstanceFieldSetsMultipleReceivers(
+ Main m, Object o, Object o2, Object o3) throws Error {
+ m.mo = new MultipleObject();
+ m.mo2 = new MultipleObject();
+
+ m.mo.inner = o;
+ // This card table for `m.mo2` can't me removed. Note that in `m.mo2 = new
+ // MultipleObject();` above the receiver is `m`, not `m.mo2.
+ m.mo2.inner = o2;
+ // This card table for `m.mo` can me removed.
+ m.mo.inner2 = o3;
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsMultipleReceivers(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:MultipleObject.inner_static field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsMultipleReceivers(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testStaticFieldSetsMultipleReceivers(
+ Object o, Object o2, Object o3) {
+ MultipleObject.inner_static = o;
+ inner_static2 = o2;
+ inner_static3 = o3;
+ }
+
+ /// CHECK-START: java.lang.Object[][] Main.$noinline$testArraySetsMultipleReceiversSameRTI() disassembly (after)
+ // Initializing the values
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ // Setting the `array_of_arrays`.
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[][] Main.$noinline$testArraySetsMultipleReceiversSameRTI() disassembly (after)
+ // Two array sets can't eliminate the write barrier
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ // One write barrier for the array of arrays' sets
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[][] $noinline$testArraySetsMultipleReceiversSameRTI() {
+ Object[] arr = new Object[3];
+ Object[] other_arr = new Object[3];
+
+ arr[0] = inner_static;
+ other_arr[1] = inner_static2;
+ arr[2] = inner_static3;
+
+ // Return them so that LSE doesn't delete them
+ Object[][] array_of_arrays = {arr, other_arr};
+ return array_of_arrays;
+ }
+
+ private static void $noinline$emptyMethod() {}
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsBlocked(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod
+ /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:EmitWithNullCheck
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsBlocked(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testInstanceFieldSetsBlocked(
+ Main m, Object o, Object o2, Object o3) {
+ m.inner = o;
+ $noinline$emptyMethod();
+ m.inner2 = o2;
+ synchronized (m) {
+ m.inner3 = o3;
+ }
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsBlocked(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:EmitWithNullCheck
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsBlocked(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testStaticFieldSetsBlocked(Object o, Object o2, Object o3) {
+ inner_static = o;
+ $noinline$emptyMethod();
+ inner_static2 = o2;
+ Main m = new Main();
+ synchronized (m) {
+ inner_static3 = o3;
+ }
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTIBlocked() disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTIBlocked() disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testArraySetsSameRTIBlocked() {
+ Object[] arr = new Object[3];
+ arr[0] = inner_static;
+ $noinline$emptyMethod();
+ arr[1] = inner_static2;
+ Main m = new Main();
+ synchronized (m) {
+ arr[2] = inner_static3;
+ }
+ return arr;
+ }
+
+ Object inner;
+ Object inner2;
+ Object inner3;
+
+ MultipleObject mo;
+ MultipleObject mo2;
+
+ static Object inner_static;
+ static Object inner_static2;
+ static Object inner_static3;
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2248-checker-smali-remove-try-until-the-end/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2248-checker-smali-remove-try-until-the-end/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2248-checker-smali-remove-try-until-the-end/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2248-checker-smali-remove-try-until-the-end/expected-stdout.txt
diff --git a/test/2248-checker-smali-remove-try-until-the-end/info.txt b/test/2248-checker-smali-remove-try-until-the-end/info.txt
new file mode 100644
index 0000000..0d7ded0
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/info.txt
@@ -0,0 +1,2 @@
+Smali test checking that we correctly set the domination graph when having a
+try until the end of the method, and the catch doesn't flow to the exit.
diff --git a/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali b/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali
new file mode 100644
index 0000000..26d4de7
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+.class public LB260387991;
+
+.super Ljava/lang/Object;
+
+# When eliminating the unnecessary try and its catch block we turn the
+# TryBoundary instructions into Goto instructions. If one of these
+# instructions is pointing to the exit block, we use its single
+# predecessor instead. If this TryBoundary-turned-into-Goto instruction
+# was the only one pointing to the Exit, we also have to update the dominators.
+
+## CHECK-START: void B260387991.testInfiniteCatch() dead_code_elimination$initial (before)
+## CHECK: TryBoundary
+## CHECK: TryBoundary
+
+## CHECK-START: void B260387991.testInfiniteCatch() dead_code_elimination$initial (after)
+## CHECK-NOT: TryBoundary
+.method public static testInfiniteCatch()V
+ .registers 4
+ const/4 v0, 0x2
+ const/4 v1, 0x4
+ :try_start
+ div-int v0, v1, v0
+ return-void
+ :try_end
+ .catchall {:try_start .. :try_end} :catch_all
+
+ # Infinite catch block which does not lead to the exit block.
+ :catch_all
+ nop
+ goto :catch_all
+.end method
diff --git a/test/2248-checker-smali-remove-try-until-the-end/src/Main.java b/test/2248-checker-smali-remove-try-until-the-end/src/Main.java
new file mode 100644
index 0000000..1ad1a26
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/src/Main.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {}
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2249-checker-return-try-boundary-exit-in-loop/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2249-checker-return-try-boundary-exit-in-loop/expected-stdout.txt
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/info.txt b/test/2249-checker-return-try-boundary-exit-in-loop/info.txt
new file mode 100644
index 0000000..db79b12
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/info.txt
@@ -0,0 +1,3 @@
+Tests that there is a case in which we have a
+ Return->TryBoundary kind:exit->Exit
+chain inside of a loop, and that said TryBoundary has loop information.
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java b/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java
new file mode 100644
index 0000000..070cd9f
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ assertNotNull($noinline$testReturnTryBoundaryExitInLoop(new Object()));
+ }
+
+ public static void assertNotNull(Object o) {
+ if (o == null) {
+ throw new Error("Expected not null!");
+ }
+ }
+
+ // Simple method to have a call inside of the synchronized block.
+ private static Object $noinline$call() {
+ return new Object();
+ }
+
+ // Consistency check: Three try boundary kind:exit. One for the explicit try catch, and two for
+ // the synchronized block (normal, and exceptional path).
+
+ /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK-NOT: TryBoundary kind:exit
+
+ /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+ /// CHECK: Return loop:B2
+
+ /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ private static Object $inline$inner(Object o) {
+ for (int i = 0; i < 4; i++) {
+ try {
+ synchronized (o) {
+ return $noinline$call();
+ }
+ } catch (Error e) {
+ continue;
+ }
+ }
+ return null;
+ }
+
+ // Simple outer to inline `inner`.
+ private static Object $noinline$testReturnTryBoundaryExitInLoop(Object o) {
+ return $inline$inner(o);
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2250-inline-throw-into-try/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2250-inline-throw-into-try/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2250-inline-throw-into-try/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2250-inline-throw-into-try/expected-stdout.txt
diff --git a/test/2250-inline-throw-into-try/info.txt b/test/2250-inline-throw-into-try/info.txt
new file mode 100644
index 0000000..4d4cab5
--- /dev/null
+++ b/test/2250-inline-throw-into-try/info.txt
@@ -0,0 +1 @@
+Tests that we inline methods that end with a throw, inside of try blocks.
diff --git a/test/2250-inline-throw-into-try/src/Main.java b/test/2250-inline-throw-into-try/src/Main.java
new file mode 100644
index 0000000..d562279
--- /dev/null
+++ b/test/2250-inline-throw-into-try/src/Main.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ // Inline methods that sometimes throw.
+ $noinline$assertEquals(-1000, $noinline$testThrowsWithZero(0));
+ $noinline$assertEquals(1, $noinline$testThrowsWithZero(1));
+
+ // Tests that we can correctly inline even when the throw is not caught.
+ try {
+ $noinline$testThrowNotCaught(0);
+ unreachable();
+ } catch (Error expected) {
+ }
+ }
+
+ public static void $noinline$assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static int $noinline$testThrowsWithZero(int value) {
+ try {
+ return $inline$throwsWithZeroOrReturns(value);
+ } catch (Error e) {
+ return -1000;
+ }
+ }
+
+ private static int $inline$throwsWithZeroOrReturns(int value) {
+ if (value == 0) {
+ throw new Error("Zero!");
+ } else {
+ return value;
+ }
+ }
+
+ private static int $noinline$testThrowNotCaught(int value) {
+ try {
+ return $inline$throwsWithZeroOrReturns(value);
+ } catch (Exception e) {
+ return -1000;
+ }
+ }
+
+ private static void unreachable() throws Exception{
+ throw new Exception("Unreachable");
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2251-checker-irreducible-loop-do-not-inline/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2251-checker-irreducible-loop-do-not-inline/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2251-checker-irreducible-loop-do-not-inline/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2251-checker-irreducible-loop-do-not-inline/expected-stdout.txt
diff --git a/test/2251-checker-irreducible-loop-do-not-inline/info.txt b/test/2251-checker-irreducible-loop-do-not-inline/info.txt
new file mode 100644
index 0000000..6feb5a5
--- /dev/null
+++ b/test/2251-checker-irreducible-loop-do-not-inline/info.txt
@@ -0,0 +1,3 @@
+Tests that we don't inline a callee with
+ Return -> TryBoundary ->Exit
+chain if the caller has irreducible loops.
diff --git a/test/2251-checker-irreducible-loop-do-not-inline/smali/IrreducibleLoop.smali b/test/2251-checker-irreducible-loop-do-not-inline/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..700f73c
--- /dev/null
+++ b/test/2251-checker-irreducible-loop-do-not-inline/smali/IrreducibleLoop.smali
@@ -0,0 +1,58 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+# Back-edges in the ascii-art graphs are represented with dash '-'.
+#
+# Check that testDoNotInlineInner has a simple irreducible loop
+#
+# entry
+# / \
+# / \
+# loop_entry \
+# / \- \
+# try_start\- \
+# other_loop_entry
+#
+# Consistency check: we didn't optimize away the irreducible loop
+## CHECK-START: java.lang.Object IrreducibleLoop.testDoNotInlineInner(java.lang.Object) register (after)
+## CHECK: irreducible:true
+#
+# We shouldn't inline `inner`.
+## CHECK-START: java.lang.Object IrreducibleLoop.testDoNotInlineInner(java.lang.Object) inliner (before)
+## CHECK: InvokeStaticOrDirect method_name:Main.inner
+#
+## CHECK-START: java.lang.Object IrreducibleLoop.testDoNotInlineInner(java.lang.Object) inliner (after)
+## CHECK: InvokeStaticOrDirect method_name:Main.inner
+.method public static testDoNotInlineInner(Ljava/lang/Object;)Ljava/lang/Object;
+ .registers 3
+ const/16 v0, 42
+ const/16 v1, 21
+ # Irreducible loop
+ if-eq v1, v0, :other_loop_entry
+ :loop_entry
+ if-ne v1, v0, :continue
+ add-int v0, v0, v0
+ :other_loop_entry
+ add-int v0, v0, v0
+ goto :loop_entry
+
+ :continue
+ invoke-static {p0}, LMain;->inner(Ljava/lang/Object;)Ljava/lang/Object;
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/2251-checker-irreducible-loop-do-not-inline/src/Main.java b/test/2251-checker-irreducible-loop-do-not-inline/src/Main.java
new file mode 100644
index 0000000..94815db
--- /dev/null
+++ b/test/2251-checker-irreducible-loop-do-not-inline/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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 java.lang.reflect.Method;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ Object[] arguments = {new Object()};
+ Object result = Class.forName("IrreducibleLoop")
+ .getMethod("testDoNotInlineInner", Object.class)
+ .invoke(null, arguments);
+ if (result == null) {
+ throw new Exception("Expected non-null result");
+ }
+ }
+
+ // Simple method to have a call inside of the synchronized block.
+ private static Object $noinline$call() {
+ return new Object();
+ }
+
+ // `inner` has a Return -> TryBoundary -> Exit chain, which means that when we inline it we
+ // would need to recompute the loop information.
+
+ // Consistency check: Three try boundary kind:exit. One for the explicit try catch, and two for
+ // the synchronized block (normal, and exceptional path).
+
+ /// CHECK-START: java.lang.Object Main.inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK-NOT: TryBoundary kind:exit
+
+ /// CHECK-START: java.lang.Object Main.inner(java.lang.Object) builder (after)
+ /// CHECK: Return loop:B2
+
+ /// CHECK-START: java.lang.Object Main.inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ public static Object inner(Object o) {
+ for (int i = 0; i < 4; i++) {
+ try {
+ synchronized (o) {
+ return $noinline$call();
+ }
+ } catch (Error e) {
+ continue;
+ }
+ }
+ return null;
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2252-rem-optimization-dividend-divisor/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2252-rem-optimization-dividend-divisor/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2252-rem-optimization-dividend-divisor/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2252-rem-optimization-dividend-divisor/expected-stdout.txt
diff --git a/test/2252-rem-optimization-dividend-divisor/info.txt b/test/2252-rem-optimization-dividend-divisor/info.txt
new file mode 100644
index 0000000..57e8b2e
--- /dev/null
+++ b/test/2252-rem-optimization-dividend-divisor/info.txt
@@ -0,0 +1,2 @@
+Test checking that FindDivWithInputsInBasicBlock works correctly
+if the dividend equals the divisor.
diff --git a/test/2252-rem-optimization-dividend-divisor/src/Main.java b/test/2252-rem-optimization-dividend-divisor/src/Main.java
new file mode 100644
index 0000000..1e1a674
--- /dev/null
+++ b/test/2252-rem-optimization-dividend-divisor/src/Main.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ $noinline$assertEquals(0, $noinline$testRemCaller());
+ }
+
+ public static void $noinline$assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static int $noinline$testRemCaller() {
+ return $inline$remMethod(50);
+ }
+
+ private static int $inline$remMethod(int param) {
+ // We were replacing this Rem with the div below when both the dividend and the
+ // divisor were the same. We shouldn't do that since we didn't find a Div(50, 50).
+ int result = param % 50;
+ return result / 50;
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2253-checker-devirtualize-always-throws/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2253-checker-devirtualize-always-throws/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2253-checker-devirtualize-always-throws/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2253-checker-devirtualize-always-throws/expected-stdout.txt
diff --git a/test/2253-checker-devirtualize-always-throws/info.txt b/test/2253-checker-devirtualize-always-throws/info.txt
new file mode 100644
index 0000000..ea76478
--- /dev/null
+++ b/test/2253-checker-devirtualize-always-throws/info.txt
@@ -0,0 +1,3 @@
+Tests that if we devirtualize a method that throws given the
+parameters, then we should also set the devirtualization as always
+throws.
diff --git a/test/2253-checker-devirtualize-always-throws/src/Main.java b/test/2253-checker-devirtualize-always-throws/src/Main.java
new file mode 100644
index 0000000..b6d2424
--- /dev/null
+++ b/test/2253-checker-devirtualize-always-throws/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+public final class Main {
+ public static void main(String[] args) {
+ try {
+ $noinline$testAlwaysThrowsDevirtualization();
+ System.out.println("Expected to throw error.");
+ } catch (Error expected) {
+ }
+ }
+
+ public void throwsIfParamIsZero(int param) {
+ if (param == 0) {
+ throw new Error("");
+ }
+ }
+
+ /// CHECK-START: void Main.$noinline$testAlwaysThrowsDevirtualization() inliner (before)
+ /// CHECK: InvokeVirtual method_name:Main.throwsIfParamIsZero always_throws:false
+ /// CHECK: ReturnVoid
+
+ /// CHECK-START: void Main.$noinline$testAlwaysThrowsDevirtualization() inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ /// CHECK-START: void Main.$noinline$testAlwaysThrowsDevirtualization() inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.throwsIfParamIsZero always_throws:true
+
+ public static void $noinline$testAlwaysThrowsDevirtualization() {
+ Main m = new Main();
+ m.throwsIfParamIsZero(0);
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2254-checker-not-var-analyzed-pathological/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2254-checker-not-var-analyzed-pathological/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2254-checker-not-var-analyzed-pathological/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2254-checker-not-var-analyzed-pathological/expected-stdout.txt
diff --git a/test/2254-checker-not-var-analyzed-pathological/info.txt b/test/2254-checker-not-var-analyzed-pathological/info.txt
new file mode 100644
index 0000000..616c1e7
--- /dev/null
+++ b/test/2254-checker-not-var-analyzed-pathological/info.txt
@@ -0,0 +1,2 @@
+Make sure that a pathological case in induction var analysis
+doesn't hang up the compiler.
diff --git a/test/2254-checker-not-var-analyzed-pathological/src/Main.java b/test/2254-checker-not-var-analyzed-pathological/src/Main.java
new file mode 100644
index 0000000..484f866
--- /dev/null
+++ b/test/2254-checker-not-var-analyzed-pathological/src/Main.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ $noinline$assertEquals(0, $noinline$pathologicalCase());
+ }
+
+ public static void $noinline$assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ // Empty $noinline$ method so that it doesn't get removed.
+ private static void $noinline$emptyMethod(int val) {}
+
+ // A pathological case which has > 15 loop header phis in a row.
+ /// CHECK-START: int Main.$noinline$pathologicalCase() induction_var_analysis (before)
+ /// CHECK: <<Const0:i\d+>> IntConstant 0
+ /// CHECK: <<Phi1:i\d+>> Phi [<<Const0>>,<<Add1:i\d+>>]
+ /// CHECK: <<Phi2:i\d+>> Phi [<<Phi1>>,<<Add2:i\d+>>]
+ /// CHECK: <<Phi3:i\d+>> Phi [<<Phi2>>,<<Add3:i\d+>>]
+ /// CHECK: <<Phi4:i\d+>> Phi [<<Phi3>>,<<Add4:i\d+>>]
+ /// CHECK: <<Phi5:i\d+>> Phi [<<Phi4>>,<<Add5:i\d+>>]
+ /// CHECK: <<Phi6:i\d+>> Phi [<<Phi5>>,<<Add6:i\d+>>]
+ /// CHECK: <<Phi7:i\d+>> Phi [<<Phi6>>,<<Add7:i\d+>>]
+ /// CHECK: <<Phi8:i\d+>> Phi [<<Phi7>>,<<Add8:i\d+>>]
+ /// CHECK: <<Phi9:i\d+>> Phi [<<Phi8>>,<<Add9:i\d+>>]
+ /// CHECK: <<Phi10:i\d+>> Phi [<<Phi9>>,<<Add10:i\d+>>]
+ /// CHECK: <<Phi11:i\d+>> Phi [<<Phi10>>,<<Add11:i\d+>>]
+ /// CHECK: <<Phi12:i\d+>> Phi [<<Phi11>>,<<Add12:i\d+>>]
+ /// CHECK: <<Phi13:i\d+>> Phi [<<Phi12>>,<<Add13:i\d+>>]
+ /// CHECK: <<Phi14:i\d+>> Phi [<<Phi13>>,<<Add14:i\d+>>]
+ /// CHECK: <<Phi15:i\d+>> Phi [<<Phi14>>,<<Add15:i\d+>>]
+ /// CHECK: <<Phi16:i\d+>> Phi [<<Phi15>>,<<Add16:i\d+>>]
+ private static int $noinline$pathologicalCase() {
+ int value = 0;
+ for (; value < 3; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 5; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 7; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 9; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 11; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 13; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 15; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 17; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 19; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 21; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 23; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 25; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 27; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 29; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 31; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ for (; value < 33; value++) {
+ $noinline$emptyMethod(value);
+ }
+
+ return 0;
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2254-class-value-before-and-after-u/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2254-class-value-before-and-after-u/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2254-class-value-before-and-after-u/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2254-class-value-before-and-after-u/expected-stdout.txt
diff --git a/test/2254-class-value-before-and-after-u/info.txt b/test/2254-class-value-before-and-after-u/info.txt
new file mode 100644
index 0000000..3a8d31f
--- /dev/null
+++ b/test/2254-class-value-before-and-after-u/info.txt
@@ -0,0 +1,2 @@
+java.lang.ClassValue should be visible to U or newer Android, but on older versions
+Class.forName("java.lang.ClassValue") should throw CNFE. See b/259501764.
\ No newline at end of file
diff --git a/test/2254-class-value-before-and-after-u/src-art/Main.java b/test/2254-class-value-before-and-after-u/src-art/Main.java
new file mode 100644
index 0000000..53704d7
--- /dev/null
+++ b/test/2254-class-value-before-and-after-u/src-art/Main.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 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 dalvik.system.VMRuntime;
+
+public class Main {
+ public static void main(String[] args) {
+ VMRuntime.getRuntime().setTargetSdkVersion(0);
+ try {
+ Class classValueClass = Class.forName("java.lang.ClassValue");
+ } catch (ClassNotFoundException ignored) {
+ throw new Error(
+ "java.lang.ClassValue should be available when targetSdkLevel is not set");
+ }
+
+ VMRuntime.getRuntime().setTargetSdkVersion(34);
+
+ try {
+ Class classValueClass = Class.forName("java.lang.ClassValue");
+ } catch (ClassNotFoundException ignored) {
+ throw new Error("java.lang.ClassValue should be available on targetSdkLevel 34");
+ }
+
+ VMRuntime.getRuntime().setTargetSdkVersion(33);
+ try {
+ Class classValueClass = Class.forName("java.lang.ClassValue");
+ throw new Error("Was able to find " + classValueClass + " on targetSdkLevel 33");
+ } catch (ClassNotFoundException expected) {
+ if (!expected.getMessage().contains("java.lang.ClassValue")) {
+ throw new Error("Thrown exception should contain class name, but was: " + expected);
+ }
+ }
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2255-checker-branch-redirection/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2255-checker-branch-redirection/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2255-checker-branch-redirection/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2255-checker-branch-redirection/expected-stdout.txt
diff --git a/test/2255-checker-branch-redirection/info.txt b/test/2255-checker-branch-redirection/info.txt
new file mode 100644
index 0000000..10b71ef
--- /dev/null
+++ b/test/2255-checker-branch-redirection/info.txt
@@ -0,0 +1,2 @@
+Tests that we can redirect branches if the block and its dominator
+have the same condition, or the exact opposite condition.
diff --git a/test/2255-checker-branch-redirection/src/Main.java b/test/2255-checker-branch-redirection/src/Main.java
new file mode 100644
index 0000000..bfc6381
--- /dev/null
+++ b/test/2255-checker-branch-redirection/src/Main.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ assertEquals(40, $noinline$testEliminateIf(20, 40));
+ assertEquals(30, $noinline$testEliminateIf(20, 10));
+ assertEquals(40, $noinline$testEliminateIfTwiceInARow(20, 40));
+ assertEquals(30, $noinline$testEliminateIfTwiceInARow(20, 10));
+ assertEquals(40, $noinline$testEliminateIfThreePredecessors(20, 40));
+ assertEquals(30, $noinline$testEliminateIfThreePredecessors(20, 10));
+ assertEquals(40, $noinline$testEliminateIfOppositeCondition(20, 40));
+ assertEquals(30, $noinline$testEliminateIfOppositeCondition(20, 10));
+ assertEquals(40, $noinline$testEliminateIfParameter(20, 40, 20 < 40));
+ assertEquals(30, $noinline$testEliminateIfParameter(20, 10, 20 < 10));
+ assertEquals(40, $noinline$testEliminateIfParameterReverseCondition(20, 40, 20 < 40));
+ assertEquals(30, $noinline$testEliminateIfParameterReverseCondition(20, 10, 20 < 10));
+ assertEquals(40, $noinline$testEliminateIfParameterOppositeCondition(20, 40, 20 < 40));
+ assertEquals(30, $noinline$testEliminateIfParameterOppositeCondition(20, 10, 20 < 10));
+ assertEquals(40, $noinline$testEliminateIfParameterOppositeCondition_2(20, 40, 20 < 40));
+ assertEquals(30, $noinline$testEliminateIfParameterOppositeCondition_2(20, 10, 20 < 10));
+ }
+
+ private static int $noinline$emptyMethod(int a) {
+ return a;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEliminateIf(int, int) dead_code_elimination$after_gvn (before)
+ /// CHECK: If
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIf(int, int) dead_code_elimination$after_gvn (after)
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIf(int a, int b) {
+ int result = 0;
+ if (a < b) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ $noinline$emptyMethod(a - b);
+ }
+ if (a < b) {
+ result += $noinline$emptyMethod(a * 2);
+ } else {
+ result += $noinline$emptyMethod(b * 3);
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfTwiceInARow(int, int) dead_code_elimination$after_gvn (before)
+ /// CHECK: If
+ /// CHECK: If
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfTwiceInARow(int, int) dead_code_elimination$after_gvn (after)
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIfTwiceInARow(int a, int b) {
+ int result = 0;
+ if (a < b) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ $noinline$emptyMethod(a - b);
+ }
+ if (a < b) {
+ $noinline$emptyMethod(a * 2);
+ } else {
+ $noinline$emptyMethod(b * 3);
+ }
+ if (a < b) {
+ result += $noinline$emptyMethod(40);
+ } else {
+ result += $noinline$emptyMethod(30);
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfThreePredecessors(int, int) dead_code_elimination$after_gvn (before)
+ /// CHECK: If
+ /// CHECK: If
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfThreePredecessors(int, int) dead_code_elimination$after_gvn (after)
+ /// CHECK: If
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIfThreePredecessors(int a, int b) {
+ int result = 0;
+ if (a < b) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ if (b < 5) {
+ $noinline$emptyMethod(a - b);
+ } else {
+ $noinline$emptyMethod(a * b);
+ }
+ }
+ if (a < b) {
+ result += $noinline$emptyMethod(a * 2);
+ } else {
+ result += $noinline$emptyMethod(b * 3);
+ }
+ return result;
+ }
+
+ // Note that we can perform this optimization in dead_code_elimination$initial since we don't
+ // rely on gvn to de-duplicate the values.
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfOppositeCondition(int, int) dead_code_elimination$initial (before)
+ /// CHECK: If
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfOppositeCondition(int, int) dead_code_elimination$initial (after)
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIfOppositeCondition(int a, int b) {
+ int result = 0;
+ if (a < b) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ $noinline$emptyMethod(a - b);
+ }
+ if (a >= b) {
+ result += $noinline$emptyMethod(b * 3);
+ } else {
+ result += $noinline$emptyMethod(a * 2);
+ }
+ return result;
+ }
+
+ // In this scenario, we have a BooleanNot before the If instructions so we have to wait until
+ // the following pass to perform the optimization. The BooleanNot is dead at this time (even
+ // when starting DCE), but RemoveDeadInstructions runs after SimplifyIfs so the optimization
+ // doesn't trigger.
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameter(int, int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: BooleanNot
+ /// CHECK: If
+ /// CHECK: BooleanNot
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameter(int, int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: If
+ /// CHECK: Phi
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameter(int, int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: BooleanNot
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameter(int, int, boolean) dead_code_elimination$after_gvn (before)
+ /// CHECK: If
+ /// CHECK: Phi
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameter(int, int, boolean) dead_code_elimination$after_gvn (after)
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIfParameter(int a, int b, boolean condition) {
+ int result = 0;
+ if (condition) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ $noinline$emptyMethod(a - b);
+ }
+ if (condition) {
+ result += $noinline$emptyMethod(a * 2);
+ } else {
+ result += $noinline$emptyMethod(b * 3);
+ }
+ return result;
+ }
+
+ // Same in the following two cases: we do it in dead_code_elimination$initial since GVN is not
+ // needed.
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterReverseCondition(int, int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: If
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterReverseCondition(int, int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIfParameterReverseCondition(
+ int a, int b, boolean condition) {
+ int result = 0;
+ if (!condition) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ $noinline$emptyMethod(a - b);
+ }
+ if (!condition) {
+ result += $noinline$emptyMethod(b * 3);
+ } else {
+ result += $noinline$emptyMethod(a * 2);
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterOppositeCondition(int, int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: If
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterOppositeCondition(int, int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIfParameterOppositeCondition(
+ int a, int b, boolean condition) {
+ int result = 0;
+ if (condition) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ $noinline$emptyMethod(a - b);
+ }
+ if (!condition) {
+ result += $noinline$emptyMethod(b * 3);
+ } else {
+ result += $noinline$emptyMethod(a * 2);
+ }
+ return result;
+ }
+
+ // In this scenario, we have a BooleanNot before the If instructions so we have to wait until
+ // the following pass to perform the optimization. The BooleanNot is dead at this time (even
+ // when starting DCE), but RemoveDeadInstructions runs after SimplifyIfs so the optimization
+ // doesn't trigger.
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterOppositeCondition_2(int, int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: If
+ /// CHECK: BooleanNot
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterOppositeCondition_2(int, int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: If
+ /// CHECK: Phi
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterOppositeCondition_2(int, int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: BooleanNot
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterOppositeCondition_2(int, int, boolean) dead_code_elimination$after_gvn (before)
+ /// CHECK: If
+ /// CHECK: Phi
+ /// CHECK: If
+
+ /// CHECK-START: int Main.$noinline$testEliminateIfParameterOppositeCondition_2(int, int, boolean) dead_code_elimination$after_gvn (after)
+ /// CHECK: If
+ /// CHECK-NOT: If
+ private static int $noinline$testEliminateIfParameterOppositeCondition_2(
+ int a, int b, boolean condition) {
+ int result = 0;
+ if (!condition) {
+ $noinline$emptyMethod(a + b);
+ } else {
+ $noinline$emptyMethod(a - b);
+ }
+ if (condition) {
+ result += $noinline$emptyMethod(a * 2);
+ } else {
+ result += $noinline$emptyMethod(b * 3);
+ }
+ return result;
+ }
+
+ public static void assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build
deleted file mode 100644
index 6e4a1d6..0000000
--- a/test/303-verification-stress/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2013 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
-
-# Write out a bunch of source files.
-./classes-gen
-
-./default-build "$@"
diff --git a/test/303-verification-stress/build.py b/test/303-verification-stress/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/303-verification-stress/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/303-verification-stress/generate-sources b/test/303-verification-stress/generate-sources
new file mode 100755
index 0000000..2a2cdb6
--- /dev/null
+++ b/test/303-verification-stress/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2013 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
+
+# Write out a bunch of source files.
+./classes-gen
diff --git a/test/304-method-tracing/run b/test/304-method-tracing/run
deleted file mode 100755
index 7bd1895..0000000
--- a/test/304-method-tracing/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Runs the test with method tracing enabled.
-exec ${RUN} "$@" --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin
diff --git a/test/304-method-tracing/run.py b/test/304-method-tracing/run.py
new file mode 100644
index 0000000..2fb72d3
--- /dev/null
+++ b/test/304-method-tracing/run.py
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+
+def run(ctx, args):
+ # Runs the test with method tracing enabled.
+ ctx.default_run(
+ args,
+ runtime_option=[
+ "-Xmethod-trace", "-Xmethod-trace-file:${DEX_LOCATION}/trace.bin"
+ ])
diff --git a/test/370-dex-v37/build b/test/370-dex-v37/build
deleted file mode 100755
index f472428..0000000
--- a/test/370-dex-v37/build
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@"
-
-if [[ $@ != *"--jvm"* ]]; then
- # Change the generated dex file to have a v36 magic number if it is version 35
- if test -f classes.dex && head -c 7 classes.dex | grep -q 035; then
- # place ascii value '037' into the classes.dex file starting at byte 4.
- printf '037' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
- rm -f $TEST_NAME.jar
- zip $TEST_NAME.jar classes.dex
- fi
-fi
diff --git a/test/370-dex-v37/build.py b/test/370-dex-v37/build.py
new file mode 100644
index 0000000..096ee82
--- /dev/null
+++ b/test/370-dex-v37/build.py
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2022 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.
+
+def build(ctx):
+ ctx.default_build()
+ if ctx.jvm:
+ return
+ # Change the generated dex file to have a v37 magic number if it is version 35
+ with open(ctx.test_dir / "classes.dex", "rb+") as f:
+ if f.read(8) == b"dex\n035\x00":
+ f.seek(0)
+ f.write(b"dex\n037\x00")
+ (ctx.test_dir / "370-dex-v37.jar").unlink()
+ ctx.soong_zip([
+ "-o", ctx.test_dir / "370-dex-v37.jar", "-j", "-f",
+ ctx.test_dir / "classes.dex"
+ ])
diff --git a/test/416-optimizing-arith-not/src/Main.java b/test/416-optimizing-arith-not/src/Main.java
index 44c7d3c..6bded7b 100644
--- a/test/416-optimizing-arith-not/src/Main.java
+++ b/test/416-optimizing-arith-not/src/Main.java
@@ -18,62 +18,62 @@
public class Main {
- public static void expectEquals(int expected, int result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ public static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
}
- }
- public static void expectEquals(long expected, long result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ public static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
}
- }
- public static void main(String[] args) throws Exception {
- notInt();
- notLong();
- }
+ public static void main(String[] args) throws Exception {
+ notInt();
+ notLong();
+ }
- private static void notInt() throws Exception {
- expectEquals(1, smaliNotInt(-2));
- expectEquals(0, smaliNotInt(-1));
- expectEquals(-1, smaliNotInt(0));
- expectEquals(-2, smaliNotInt(1));
- expectEquals(2147483647, smaliNotInt(-2147483648)); // -(2^31)
- expectEquals(2147483646, smaliNotInt(-2147483647)); // -(2^31 - 1)
- expectEquals(-2147483647, smaliNotInt(2147483646)); // 2^31 - 2
- expectEquals(-2147483648, smaliNotInt(2147483647)); // 2^31 - 1
- }
+ private static void notInt() throws Exception {
+ expectEquals(1, smaliNotInt(-2));
+ expectEquals(0, smaliNotInt(-1));
+ expectEquals(-1, smaliNotInt(0));
+ expectEquals(-2, smaliNotInt(1));
+ expectEquals(2147483647, smaliNotInt(-2147483648)); // -(2^31)
+ expectEquals(2147483646, smaliNotInt(-2147483647)); // -(2^31 - 1)
+ expectEquals(-2147483647, smaliNotInt(2147483646)); // 2^31 - 2
+ expectEquals(-2147483648, smaliNotInt(2147483647)); // 2^31 - 1
+ }
- private static void notLong() throws Exception {
- expectEquals(1L, smaliNotLong(-2L));
- expectEquals(0L, smaliNotLong(-1L));
- expectEquals(-1L, smaliNotLong(0L));
- expectEquals(-2L, smaliNotLong(1L));
- expectEquals(2147483647L, smaliNotLong(-2147483648L)); // -(2^31)
- expectEquals(2147483646L, smaliNotLong(-2147483647L)); // -(2^31 - 1)
- expectEquals(-2147483647L, smaliNotLong(2147483646L)); // 2^31 - 2
- expectEquals(-2147483648L, smaliNotLong(2147483647L)); // 2^31 - 1
- expectEquals(9223372036854775807L, smaliNotLong(-9223372036854775808L)); // -(2^63)
- expectEquals(9223372036854775806L, smaliNotLong(-9223372036854775807L)); // -(2^63 - 1)
- expectEquals(-9223372036854775807L, smaliNotLong(9223372036854775806L)); // 2^63 - 2
- expectEquals(-9223372036854775808L, smaliNotLong(9223372036854775807L)); // 2^63 - 1
- }
+ private static void notLong() throws Exception {
+ expectEquals(1L, smaliNotLong(-2L));
+ expectEquals(0L, smaliNotLong(-1L));
+ expectEquals(-1L, smaliNotLong(0L));
+ expectEquals(-2L, smaliNotLong(1L));
+ expectEquals(2147483647L, smaliNotLong(-2147483648L)); // -(2^31)
+ expectEquals(2147483646L, smaliNotLong(-2147483647L)); // -(2^31 - 1)
+ expectEquals(-2147483647L, smaliNotLong(2147483646L)); // 2^31 - 2
+ expectEquals(-2147483648L, smaliNotLong(2147483647L)); // 2^31 - 1
+ expectEquals(9223372036854775807L, smaliNotLong(-9223372036854775808L)); // -(2^63)
+ expectEquals(9223372036854775806L, smaliNotLong(-9223372036854775807L)); // -(2^63 - 1)
+ expectEquals(-9223372036854775807L, smaliNotLong(9223372036854775806L)); // 2^63 - 2
+ expectEquals(-9223372036854775808L, smaliNotLong(9223372036854775807L)); // 2^63 - 1
+ }
- // Wrappers around methods located in file not.smali.
+ // Wrappers around methods located in file not.smali.
- private static int smaliNotInt(int a) throws Exception {
- Class<?> c = Class.forName("TestNot");
- Method m = c.getMethod("$opt$NotInt", int.class);
- int result = (Integer)m.invoke(null, a);
- return result;
- }
+ private static int smaliNotInt(int a) throws Exception {
+ Class<?> c = Class.forName("TestNot");
+ Method m = c.getMethod("$opt$NotInt", int.class);
+ int result = (Integer)m.invoke(null, a);
+ return result;
+ }
- private static long smaliNotLong(long a) throws Exception {
- Class<?> c = Class.forName("TestNot");
- Method m = c.getMethod("$opt$NotLong", long.class);
- long result = (Long)m.invoke(null, a);
- return result;
- }
+ private static long smaliNotLong(long a) throws Exception {
+ Class<?> c = Class.forName("TestNot");
+ Method m = c.getMethod("$opt$NotLong", long.class);
+ long result = (Long)m.invoke(null, a);
+ return result;
+ }
}
diff --git a/test/418-const-string/src/Main.java b/test/418-const-string/src/Main.java
index 7c1ffec..3b7d8e2 100644
--- a/test/418-const-string/src/Main.java
+++ b/test/418-const-string/src/Main.java
@@ -15,14 +15,14 @@
*/
public class Main {
- public static void main(String[] args) {
- // First call: may go in slow path.
- System.out.println($opt$ReturnHelloWorld());
- // Second call: no slow path.
- System.out.println($opt$ReturnHelloWorld());
- }
+ public static void main(String[] args) {
+ // First call: may go in slow path.
+ System.out.println($opt$ReturnHelloWorld());
+ // Second call: no slow path.
+ System.out.println($opt$ReturnHelloWorld());
+ }
- public static String $opt$ReturnHelloWorld() {
- return "Hello World";
- }
+ public static String $opt$ReturnHelloWorld() {
+ return "Hello World";
+ }
}
diff --git a/test/419-long-parameter/src/Main.java b/test/419-long-parameter/src/Main.java
index 808b7f6..f83b3f7 100644
--- a/test/419-long-parameter/src/Main.java
+++ b/test/419-long-parameter/src/Main.java
@@ -15,20 +15,20 @@
*/
public class Main {
- public static void main(String[] args) {
- if ($opt$TestCallee(1.0, 2.0, 1L, 2L) != 1L) {
- throw new Error("Unexpected result");
+ public static void main(String[] args) {
+ if ($opt$TestCallee(1.0, 2.0, 1L, 2L) != 1L) {
+ throw new Error("Unexpected result");
+ }
+ if ($opt$TestCaller() != 1L) {
+ throw new Error("Unexpected result");
+ }
}
- if ($opt$TestCaller() != 1L) {
- throw new Error("Unexpected result");
+
+ public static long $opt$TestCallee(double a, double b, long c, long d) {
+ return d - c;
}
- }
- public static long $opt$TestCallee(double a, double b, long c, long d) {
- return d - c;
- }
-
- public static long $opt$TestCaller() {
- return $opt$TestCallee(1.0, 2.0, 1L, 2L);
- }
+ public static long $opt$TestCaller() {
+ return $opt$TestCallee(1.0, 2.0, 1L, 2L);
+ }
}
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 1bdf7b5..168ebbf 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -1577,6 +1577,282 @@
return (double) imm;
}
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (before)
+ /// CHECK-NOT: IntConstant 6
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static int $inline$SpecialCaseForZeroInt(int value) {
+ if (value == 0) {
+ return (value + 2) * 3;
+ }
+ return value;
+ }
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (before)
+ /// CHECK-NOT: LongConstant 6
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (after)
+ /// CHECK-DAG: <<Const:j\d+>> LongConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static long $inline$SpecialCaseForZeroLong(long value) {
+ if (value == 0L) {
+ return (value + 2) * 3;
+ }
+ return value;
+ }
+
+ /// CHECK-START: float Main.$noinline$SpecialCaseForZeroFloat(float) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: float Main.$noinline$SpecialCaseForZeroFloat(float) constant_folding (after)
+ /// CHECK-NOT: FloatConstant 6
+
+ /// CHECK-START: float Main.$noinline$SpecialCaseForZeroFloat(float) constant_folding (after)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+ private static float $noinline$SpecialCaseForZeroFloat(float value) {
+ if (value == 0F) {
+ return (value + 2F) * 3F;
+ }
+ return value;
+ }
+
+ /// CHECK-START: double Main.$noinline$SpecialCaseForZeroDouble(double) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: double Main.$noinline$SpecialCaseForZeroDouble(double) constant_folding (after)
+ /// CHECK-NOT: DoubleConstant 6
+
+ /// CHECK-START: double Main.$noinline$SpecialCaseForZeroDouble(double) constant_folding (after)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+ private static double $noinline$SpecialCaseForZeroDouble(double value) {
+ if (value == 0D) {
+ return (value + 2D) * 3D;
+ }
+ return value;
+ }
+
+ // Note that we have Add instead of sub since internally we do `Add(value, -1)`.
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (after)
+ /// CHECK-NOT: Div
+
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<Const>>]
+ private static int $noinline$NotEqualsPropagationInt(int value) {
+ if (value != 3) {
+ return value;
+ } else {
+ return (value - 1) / 2;
+ }
+ }
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (after)
+ /// CHECK-NOT: Sub
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (after)
+ /// CHECK-NOT: Div
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (after)
+ /// CHECK-DAG: <<Const:j\d+>> LongConstant 1
+ /// CHECK-DAG: Return [<<Const>>]
+ private static long $noinline$NotEqualsPropagationLong(long value) {
+ if (value != 3L) {
+ return value;
+ } else {
+ return (value - 1L) / 2L;
+ }
+ }
+
+ /// CHECK-START: float Main.$noinline$NotEqualsPropagationFloat(float) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: float Main.$noinline$NotEqualsPropagationFloat(float) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static float $noinline$NotEqualsPropagationFloat(float value) {
+ if (value != 3F) {
+ return value;
+ } else {
+ return (value - 1F) / 2F;
+ }
+ }
+
+ /// CHECK-START: double Main.$noinline$NotEqualsPropagationDouble(double) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: double Main.$noinline$NotEqualsPropagationDouble(double) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static double $noinline$NotEqualsPropagationDouble(double value) {
+ if (value != 3D) {
+ return value;
+ } else {
+ return (value - 1D) / 2D;
+ }
+ }
+
+ /// CHECK-START: int Main.$noinline$InlineCaleeWithSpecialCaseForZeroInt(int) inliner (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$noinline$InlineCaleeWithSpecialCaseForZeroInt(int) inliner (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: int Main.$noinline$InlineCaleeWithSpecialCaseForZeroInt(int) inliner (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static int $noinline$InlineCaleeWithSpecialCaseForZeroInt(int value) {
+ if (value == 0) {
+ return $inline$SpecialCaseForZeroInt(value);
+ }
+ return value;
+ }
+
+ /// CHECK-START: long Main.$noinline$InlineCaleeWithSpecialCaseForZeroLong(long) inliner (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: long Main.$noinline$InlineCaleeWithSpecialCaseForZeroLong(long) inliner (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: long Main.$noinline$InlineCaleeWithSpecialCaseForZeroLong(long) inliner (after)
+ /// CHECK-DAG: <<Const:j\d+>> LongConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static long $noinline$InlineCaleeWithSpecialCaseForZeroLong(long value) {
+ if (value == 0L) {
+ return $inline$SpecialCaseForZeroLong(value);
+ }
+ return value;
+ }
+
+ // Check that don't propagate the value == 3 on `if not true` branch, as the `if true` branch also
+ // flows into the same block.
+ /// CHECK-START: int Main.$noinline$NotEqualsImplicitElseInt(int) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: int Main.$noinline$NotEqualsImplicitElseInt(int) constant_folding (after)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Div
+ private static int $noinline$NotEqualsImplicitElseInt(int value) {
+ if (value != 3) {
+ value++;
+ }
+ return (value - 1) / 2;
+ }
+
+ /// CHECK-START: long Main.$noinline$NotEqualsImplicitElseLong(long) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: long Main.$noinline$NotEqualsImplicitElseLong(long) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static long $noinline$NotEqualsImplicitElseLong(long value) {
+ if (value != 3L) {
+ value += 1L;
+ }
+ return (value - 1L) / 2L;
+ }
+
+ /// CHECK-START: float Main.$noinline$NotEqualsImplicitElseFloat(float) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: float Main.$noinline$NotEqualsImplicitElseFloat(float) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static float $noinline$NotEqualsImplicitElseFloat(float value) {
+ if (value != 3F) {
+ value += 1F;
+ }
+ return (value - 1F) / 2F;
+ }
+
+ /// CHECK-START: double Main.$noinline$NotEqualsImplicitElseDouble(double) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: double Main.$noinline$NotEqualsImplicitElseDouble(double) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static double $noinline$NotEqualsImplicitElseDouble(double value) {
+ if (value != 3D) {
+ value += 1D;
+ }
+ return (value - 1D) / 2D;
+ }
+
+ // By propagating the boolean we can elimniate some equality comparisons as we already know their
+ // result. In turn, we also enable DeadCodeElimination to eliminate more code.
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) constant_folding (before)
+ /// CHECK-DAG: Equal
+ /// CHECK-DAG: Equal
+ /// CHECK-DAG: Equal
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) constant_folding (after)
+ /// CHECK: Equal
+ /// CHECK-NOT: Equal
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (before)
+ /// CHECK-DAG: IntConstant 1
+ /// CHECK-DAG: IntConstant 2
+ /// CHECK-DAG: IntConstant 3
+ /// CHECK-DAG: IntConstant 4
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (after)
+ /// CHECK-DAG: IntConstant 1
+ /// CHECK-DAG: IntConstant 4
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: IntConstant 2
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: IntConstant 3
+ private static int $noinline$PropagatingParameterValue(boolean value) {
+ if (value) {
+ return value ? 1 : 2;
+ } else {
+ return value ? 3 : 4;
+ }
+ }
public static void main(String[] args) throws Exception {
assertIntEquals(-42, IntNegation());
@@ -1708,6 +1984,51 @@
assertDoubleEquals(33, ReturnDouble33());
assertDoubleEquals(34, ReturnDouble34());
assertDoubleEquals(99.25, ReturnDouble99P25());
+
+ // Tests for propagating known values due to if clauses.
+
+ // Propagating within the same method. These are marked $inline$ since we used them in
+ // `InlineCaleeWithSpecialCaseForZeroInt`.
+ assertIntEquals(6, $inline$SpecialCaseForZeroInt(0));
+ assertIntEquals(3, $inline$SpecialCaseForZeroInt(3));
+ assertLongEquals(6L, $inline$SpecialCaseForZeroLong(0L));
+ assertLongEquals(3L, $inline$SpecialCaseForZeroLong(3L));
+ // Floats and doubles we do not optimize (here and below). These methods are here to guarantee
+ // that.
+ assertFloatEquals(6F, $noinline$SpecialCaseForZeroFloat(0F));
+ assertFloatEquals(3F, $noinline$SpecialCaseForZeroFloat(3F));
+ assertDoubleEquals(6D, $noinline$SpecialCaseForZeroDouble(0D));
+ assertDoubleEquals(3D, $noinline$SpecialCaseForZeroDouble(3D));
+
+ // Propagating within the same method, with not equals
+ assertIntEquals(0, $noinline$NotEqualsPropagationInt(0));
+ assertIntEquals(1, $noinline$NotEqualsPropagationInt(3));
+ assertLongEquals(0L, $noinline$NotEqualsPropagationLong(0L));
+ assertLongEquals(1L, $noinline$NotEqualsPropagationLong(3L));
+ assertFloatEquals(0F, $noinline$NotEqualsPropagationFloat(0F));
+ assertFloatEquals(1F, $noinline$NotEqualsPropagationFloat(3F));
+ assertDoubleEquals(0D, $noinline$NotEqualsPropagationDouble(0D));
+ assertDoubleEquals(1D, $noinline$NotEqualsPropagationDouble(3D));
+
+ // Propagating so that the inliner can use it.
+ assertIntEquals(6, $noinline$InlineCaleeWithSpecialCaseForZeroInt(0));
+ assertIntEquals(3, $noinline$InlineCaleeWithSpecialCaseForZeroInt(3));
+ assertLongEquals(6L, $noinline$InlineCaleeWithSpecialCaseForZeroLong(0L));
+ assertLongEquals(3L, $noinline$InlineCaleeWithSpecialCaseForZeroLong(3L));
+
+ // Propagating within the same method, with not equals
+ assertIntEquals(0, $noinline$NotEqualsImplicitElseInt(0));
+ assertIntEquals(1, $noinline$NotEqualsImplicitElseInt(3));
+ assertLongEquals(0L, $noinline$NotEqualsImplicitElseLong(0L));
+ assertLongEquals(1L, $noinline$NotEqualsImplicitElseLong(3L));
+ assertFloatEquals(0F, $noinline$NotEqualsImplicitElseFloat(0F));
+ assertFloatEquals(1F, $noinline$NotEqualsImplicitElseFloat(3F));
+ assertDoubleEquals(0D, $noinline$NotEqualsImplicitElseDouble(0D));
+ assertDoubleEquals(1D, $noinline$NotEqualsImplicitElseDouble(3D));
+
+ // Propagating parameters.
+ assertIntEquals(1, $noinline$PropagatingParameterValue(true));
+ assertIntEquals(4, $noinline$PropagatingParameterValue(false));
}
Main() throws ClassNotFoundException {
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 1144366..fbe3586 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -1125,7 +1125,7 @@
/// CHECK-DAG: <<Len:i\d+>> ArrayLength [<<Nul>>] loop:none
/// CHECK-DAG: Equal [<<Len>>,<<Val>>] loop:none
/// CHECK-DAG: <<Idx:i\d+>> Phi loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck [<<Idx>>,<<Len>>] loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck [<<Idx>>,<<Val>>] loop:<<Loop>>
//
/// CHECK-START: void Main.lengthAlias4(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
diff --git a/test/457-regs/run b/test/457-regs/run
deleted file mode 100644
index 2591855..0000000
--- a/test/457-regs/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-${RUN} "$@"
-return_status1=$?
-
-# Force baseline JIT compilation as the test explicitly requests JIT
-# compilation, which by default is 'optimizing'.
-${RUN} "$@" -Xcompiler-option --baseline
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/457-regs/run.py b/test/457-regs/run.py
new file mode 100644
index 0000000..7c18068
--- /dev/null
+++ b/test/457-regs/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Force baseline JIT compilation as the test explicitly requests JIT
+ # compilation, which by default is 'optimizing'.
+ ctx.default_run(args, Xcompiler_option=["--baseline"])
diff --git a/test/458-checker-instruct-simplification/smali/SmaliTests.smali b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
index d987398..f0436d2 100644
--- a/test/458-checker-instruct-simplification/smali/SmaliTests.smali
+++ b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
@@ -415,7 +415,7 @@
## CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
## CHECK-DAG: Return [<<Arg>>]
-## CHECK-START: boolean SmaliTests.$noinline$NotNotBool(boolean) dead_code_elimination$final (after)
+## CHECK-START: boolean SmaliTests.$noinline$NotNotBool(boolean) dead_code_elimination$after_bce (after)
## CHECK-DAG: <<Arg:z\d+>> ParameterValue
## CHECK-DAG: Return [<<Arg>>]
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 22b6dfe..8ab059d 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -2751,6 +2751,49 @@
return (byte) ((value & mask) >> 8);
}
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (before)
+ /// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<IndexPhi:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ // Induction variable:
+ /// CHECK-DAG: Add [<<IndexPhi>>,<<Const2>>] loop:<<Loop>> outer_loop:none
+ // Array Element Addition:
+ /// CHECK-DAG: <<Store1:i\d+>> Add [{{i\d+}},<<Const1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Store2:i\d+>> Add [{{i\d+}},<<Const2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Param>>,<<Const0>>,<<Store2>>] loop:<<Loop>> outer_loop:none
+
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (before)
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (after)
+ /// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<IndexPhi:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ // Induction variable:
+ /// CHECK-DAG: Add [<<IndexPhi>>,<<Const2>>] loop:<<Loop>> outer_loop:none
+ // Array Element Addition:
+ /// CHECK-DAG: <<Store:i\d+>> Add [{{i\d+}},<<Const2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Param>>,<<Const0>>,<<Store>>] loop:<<Loop>> outer_loop:none
+
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (after)
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK-NOT: Add
+ public static int $noinline$deadAddAfterUnrollingAndSimplification(int[] array) {
+ for (int i = 0; i < 50; ++i) {
+ // Array access prevents transformation to closed-form expression
+ array[0]++;
+ }
+ return array[0];
+ }
+
public static void main(String[] args) throws Exception {
Class smaliTests2 = Class.forName("SmaliTests2");
Method $noinline$XorAllOnes = smaliTests2.getMethod("$noinline$XorAllOnes", int.class);
@@ -3058,6 +3101,8 @@
assertIntEquals(-1, $noinline$redundantAndIntToByteShortAndConstant(0x7fffff45));
assertIntEquals(-1, $noinline$redundantAndIntToByteShortAndConstant(0xffffff45));
assertIntEquals(111, $noinline$redundantAndRegressionNotConstant(-1, 0x6f45));
+
+ assertIntEquals(50, $noinline$deadAddAfterUnrollingAndSimplification(new int[] { 0 }));
}
private static boolean $inline$true() { return true; }
diff --git a/test/463-checker-boolean-simplifier/smali/Main2.smali b/test/463-checker-boolean-simplifier/smali/Main2.smali
index 5fc553e..e8ebb23 100644
--- a/test/463-checker-boolean-simplifier/smali/Main2.smali
+++ b/test/463-checker-boolean-simplifier/smali/Main2.smali
@@ -211,6 +211,9 @@
goto :goto_4
.end method
+# This test currently checks that we don't perform select generation due to
+# having multiple phis.
+
## CHECK-START: int Main2.MultiplePhis() select_generator (before)
## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -228,11 +231,11 @@
## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
## CHECK-DAG: <<Const13:i\d+>> IntConstant 13
## CHECK-DAG: <<Const42:i\d+>> IntConstant 42
-## CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Select:i\d+>>]
-## CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>]
+## CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Const13>>,<<Const42>>]
+## CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>,<<Add>>]
## CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>]
## CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>]
-## CHECK-DAG: <<Select>> Select [<<Const13>>,<<Const42>>,<<Cond>>]
+## CHECK-DAG: If [<<Cond>>]
## CHECK-DAG: Return [<<PhiX>>]
# The original java source of this method:
diff --git a/test/465-checker-clinit-gvn/src/Main.java b/test/465-checker-clinit-gvn/src/Main.java
index 9c77acc..213437c 100644
--- a/test/465-checker-clinit-gvn/src/Main.java
+++ b/test/465-checker-clinit-gvn/src/Main.java
@@ -54,11 +54,11 @@
public static int accessTwoStaticsCallInBetween() {
int b = OtherClass.b;
- foo();
+ $noinline$foo();
return b - OtherClass.a;
}
- public static void foo() {
+ public static void $noinline$foo() {
try {
Thread.sleep(0);
} catch (Exception e) {
diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali
index 5290bad..3e7bca9 100644
--- a/test/485-checker-dce-loop-update/smali/TestCase.smali
+++ b/test/485-checker-dce-loop-update/smali/TestCase.smali
@@ -224,7 +224,7 @@
## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
#
# ### Inner loop ###
-## CHECK-DAG: <<PhiZ2:i\d+>> Phi [<<PhiZ1>>,<<XorZ>>] loop:<<HeaderZ:B\d+>>
+## CHECK-DAG: <<PhiZ2:i\d+>> Phi [<<PhiZ1>>,<<Cst0>>] loop:<<HeaderZ:B\d+>>
## CHECK-DAG: <<XorZ>> Xor [<<PhiZ2>>,<<Cst1>>] loop:<<HeaderZ>>
## CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>>
## CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>>
@@ -246,8 +246,8 @@
## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
#
# ### Inner loop ###
-## CHECK-DAG: <<PhiZ:i\d+>> Phi [<<ArgZ>>,<<XorZ:i\d+>>] loop:<<HeaderZ:B\d+>>
-## CHECK-DAG: <<XorZ>> Xor [<<PhiZ>>,<<Cst1>>] loop:<<HeaderZ>>
+## CHECK-DAG: <<PhiZ:i\d+>> Phi [<<ArgZ>>,<<Cst0>>] loop:<<HeaderZ:B\d+>>
+## CHECK-DAG: <<XorZ:i\d+>> Xor [<<PhiZ>>,<<Cst1>>] loop:<<HeaderZ>>
## CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>>
## CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>>
#
diff --git a/test/495-checker-checkcast-tests/src/Main.java b/test/495-checker-checkcast-tests/src/Main.java
index 6011c7c..2c0126a 100644
--- a/test/495-checker-checkcast-tests/src/Main.java
+++ b/test/495-checker-checkcast-tests/src/Main.java
@@ -15,207 +15,207 @@
*/
public class Main {
- public static boolean $inline$classTypeTest(Object o) {
- return ((SubMain)o) == o;
- }
+ public static boolean $inline$classTypeTest(Object o) {
+ return ((SubMain) o) == o;
+ }
- public static boolean $inline$interfaceTypeTest(Object o) {
- return ((Itf)o) == o;
- }
+ public static boolean $inline$interfaceTypeTest(Object o) {
+ return ((Itf) o) == o;
+ }
- public static SubMain subMain;
- public static Main mainField;
- public static Unrelated unrelatedField;
- public static FinalUnrelated finalUnrelatedField;
+ public static SubMain subMain;
+ public static Main mainField;
+ public static Unrelated unrelatedField;
+ public static FinalUnrelated finalUnrelatedField;
- /// CHECK-START: boolean Main.classTypeTestNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean classTypeTestNull() {
- return $inline$classTypeTest(null);
- }
+ /// CHECK-START: boolean Main.classTypeTestNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestNull() {
+ return $inline$classTypeTest(null);
+ }
- /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestExactMain() {
- return $inline$classTypeTest(new Main());
- }
+ /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestExactMain() {
+ return $inline$classTypeTest(new Main());
+ }
- /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean classTypeTestExactSubMain() {
- return $inline$classTypeTest(new SubMain());
- }
+ /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestExactSubMain() {
+ return $inline$classTypeTest(new SubMain());
+ }
- /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean classTypeTestSubMainOrNull() {
- return $inline$classTypeTest(subMain);
- }
+ /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestSubMainOrNull() {
+ return $inline$classTypeTest(subMain);
+ }
- /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestMainOrNull() {
- return $inline$classTypeTest(mainField);
- }
+ /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestMainOrNull() {
+ return $inline$classTypeTest(mainField);
+ }
- /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestUnrelated() {
- return $inline$classTypeTest(unrelatedField);
- }
+ /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestUnrelated() {
+ return $inline$classTypeTest(unrelatedField);
+ }
- /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestFinalUnrelated() {
- return $inline$classTypeTest(finalUnrelatedField);
- }
+ /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestFinalUnrelated() {
+ return $inline$classTypeTest(finalUnrelatedField);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean interfaceTypeTestNull() {
- return $inline$interfaceTypeTest(null);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestNull() {
+ return $inline$interfaceTypeTest(null);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestExactMain() {
- return $inline$interfaceTypeTest(new Main());
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestExactMain() {
+ return $inline$interfaceTypeTest(new Main());
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean interfaceTypeTestExactSubMain() {
- return $inline$interfaceTypeTest(new SubMain());
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestExactSubMain() {
+ return $inline$interfaceTypeTest(new SubMain());
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean interfaceTypeTestSubMainOrNull() {
- return $inline$interfaceTypeTest(subMain);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestSubMainOrNull() {
+ return $inline$interfaceTypeTest(subMain);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestMainOrNull() {
- return $inline$interfaceTypeTest(mainField);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestMainOrNull() {
+ return $inline$interfaceTypeTest(mainField);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestUnrelated() {
- return $inline$interfaceTypeTest(unrelatedField);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestUnrelated() {
+ return $inline$interfaceTypeTest(unrelatedField);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestFinalUnrelated() {
- return $inline$interfaceTypeTest(finalUnrelatedField);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestFinalUnrelated() {
+ return $inline$interfaceTypeTest(finalUnrelatedField);
+ }
- /// CHECK-START: java.lang.String Main.knownTestWithLoadedClass() register (after)
- /// CHECK-NOT: CheckCast
- public static String knownTestWithLoadedClass() {
- return (String)$inline$getString();
- }
+ /// CHECK-START: java.lang.String Main.knownTestWithLoadedClass() register (after)
+ /// CHECK-NOT: CheckCast
+ public static String knownTestWithLoadedClass() {
+ return (String)$inline$getString();
+ }
- /// CHECK-START: Itf Main.knownTestWithUnloadedClass() register (after)
- /// CHECK: CheckCast
- public static Itf knownTestWithUnloadedClass() {
- return (Itf)$inline$getString();
- }
+ /// CHECK-START: Itf Main.knownTestWithUnloadedClass() register (after)
+ /// CHECK: CheckCast
+ public static Itf knownTestWithUnloadedClass() {
+ return (Itf)$inline$getString();
+ }
- public static Object $inline$getString() {
- return new String();
- }
+ public static Object $inline$getString() {
+ return new String();
+ }
- public static Object $inline$getMain() {
- return new Main();
- }
+ public static Object $inline$getMain() {
+ return new Main();
+ }
- /// CHECK-START: void Main.nonNullBoundType() register (after)
- /// CHECK-NOT: NullCheck
- public static void nonNullBoundType() {
- Main main = (Main)$inline$getMain();
- main.getClass();
- }
+ /// CHECK-START: void Main.nonNullBoundType() register (after)
+ /// CHECK-NOT: NullCheck
+ public static void nonNullBoundType() {
+ Main main = (Main)$inline$getMain();
+ main.getClass();
+ }
- public static void main(String[] args) {
- classTypeTestNull();
- try {
- classTypeTestExactMain();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- classTypeTestExactSubMain();
+ public static void main(String[] args) {
+ classTypeTestNull();
+ try {
+ classTypeTestExactMain();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ classTypeTestExactSubMain();
- subMain = null;
- classTypeTestSubMainOrNull();
- subMain = new SubMain();
- classTypeTestSubMainOrNull();
+ subMain = null;
+ classTypeTestSubMainOrNull();
+ subMain = new SubMain();
+ classTypeTestSubMainOrNull();
- mainField = null;
- classTypeTestMainOrNull();
- mainField = new Main();
- try {
- classTypeTestMainOrNull();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- mainField = new SubMain();
- classTypeTestMainOrNull();
+ mainField = null;
+ classTypeTestMainOrNull();
+ mainField = new Main();
+ try {
+ classTypeTestMainOrNull();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ mainField = new SubMain();
+ classTypeTestMainOrNull();
- unrelatedField = null;
- classTypeTestUnrelated();
- unrelatedField = new Unrelated();
- try {
- classTypeTestUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
+ unrelatedField = null;
+ classTypeTestUnrelated();
+ unrelatedField = new Unrelated();
+ try {
+ classTypeTestUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
- finalUnrelatedField = null;
- classTypeTestFinalUnrelated();
- finalUnrelatedField = new FinalUnrelated();
- try {
- classTypeTestFinalUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
+ finalUnrelatedField = null;
+ classTypeTestFinalUnrelated();
+ finalUnrelatedField = new FinalUnrelated();
+ try {
+ classTypeTestFinalUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
- interfaceTypeTestNull();
- try {
- interfaceTypeTestExactMain();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- interfaceTypeTestExactSubMain();
+ interfaceTypeTestNull();
+ try {
+ interfaceTypeTestExactMain();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ interfaceTypeTestExactSubMain();
- subMain = null;
- interfaceTypeTestSubMainOrNull();
- subMain = new SubMain();
- interfaceTypeTestSubMainOrNull();
+ subMain = null;
+ interfaceTypeTestSubMainOrNull();
+ subMain = new SubMain();
+ interfaceTypeTestSubMainOrNull();
- mainField = null;
- interfaceTypeTestMainOrNull();
- mainField = new Main();
- try {
- interfaceTypeTestMainOrNull();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- mainField = new SubMain();
- interfaceTypeTestMainOrNull();
+ mainField = null;
+ interfaceTypeTestMainOrNull();
+ mainField = new Main();
+ try {
+ interfaceTypeTestMainOrNull();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ mainField = new SubMain();
+ interfaceTypeTestMainOrNull();
- unrelatedField = null;
- interfaceTypeTestUnrelated();
- unrelatedField = new Unrelated();
- try {
- interfaceTypeTestUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
+ unrelatedField = null;
+ interfaceTypeTestUnrelated();
+ unrelatedField = new Unrelated();
+ try {
+ interfaceTypeTestUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
- finalUnrelatedField = null;
- interfaceTypeTestFinalUnrelated();
- finalUnrelatedField = new FinalUnrelated();
- try {
- interfaceTypeTestFinalUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- }
+ finalUnrelatedField = null;
+ interfaceTypeTestFinalUnrelated();
+ finalUnrelatedField = new FinalUnrelated();
+ try {
+ interfaceTypeTestFinalUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ }
}
interface Itf {
diff --git a/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java b/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java
index e97b4e3..fe0582e 100644
--- a/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java
+++ b/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java
@@ -15,12 +15,12 @@
*/
public class FirstSeenByMyClassLoader {
- public static void $inline$bar() {
- }
+ public static void $inline$bar() {
+ }
- public static void $noinline$bar() {
- try {
- System.out.println("In $noinline$bar");
- } catch (Throwable t) { /* Ignore */ }
- }
+ public static void $noinline$bar() {
+ try {
+ System.out.println("In $noinline$bar");
+ } catch (Throwable t) { /* Ignore */ }
+ }
}
diff --git a/test/496-checker-inlining-class-loader/src/Main.java b/test/496-checker-inlining-class-loader/src/Main.java
index 4fe4723..feb8fdc 100644
--- a/test/496-checker-inlining-class-loader/src/Main.java
+++ b/test/496-checker-inlining-class-loader/src/Main.java
@@ -20,115 +20,116 @@
import java.util.List;
class MyClassLoader extends ClassLoader {
- MyClassLoader() throws Exception {
- super(MyClassLoader.class.getClassLoader());
+ MyClassLoader() throws Exception {
+ super(MyClassLoader.class.getClassLoader());
- // Some magic to get access to the pathList field of BaseDexClassLoader.
- ClassLoader loader = getClass().getClassLoader();
- Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
- Field f = baseDexClassLoader.getDeclaredField("pathList");
- f.setAccessible(true);
- Object pathList = f.get(loader);
+ // Some magic to get access to the pathList field of BaseDexClassLoader.
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+ Field f = baseDexClassLoader.getDeclaredField("pathList");
+ f.setAccessible(true);
+ Object pathList = f.get(loader);
- // Some magic to get access to the dexField field of pathList.
- // Need to make a copy of the dex elements since we don't want an app image with pre-resolved
- // things.
- f = pathList.getClass().getDeclaredField("dexElements");
- f.setAccessible(true);
- Object[] dexElements = (Object[]) f.get(pathList);
- f = dexElements[0].getClass().getDeclaredField("dexFile");
- f.setAccessible(true);
- for (Object element : dexElements) {
- Object dexFile = f.get(element);
- // Make copy.
- Field fileNameField = dexFile.getClass().getDeclaredField("mFileName");
- fileNameField.setAccessible(true);
- dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance(
- fileNameField.get(dexFile)));
- }
- }
-
- ArrayList<Object> dexFiles = new ArrayList<Object>();
- Field dexFileField;
-
- protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
- // Other classes may also get loaded, ignore those.
- if (className.equals("LoadedByMyClassLoader") || className.equals("FirstSeenByMyClassLoader")) {
- System.out.println("Request for " + className);
- }
-
- // We're only going to handle LoadedByMyClassLoader.
- if (className != "LoadedByMyClassLoader") {
- return getParent().loadClass(className);
- }
-
- // Mimic what DexPathList.findClass is doing.
- try {
- for (Object dexFile : dexFiles) {
- Method method = dexFile.getClass().getDeclaredMethod(
- "loadClassBinaryName", String.class, ClassLoader.class, List.class);
-
- if (dexFile != null) {
- Class<?> clazz = (Class<?>)method.invoke(dexFile, className, this, null);
- if (clazz != null) {
- return clazz;
- }
+ // Some magic to get access to the dexField field of pathList.
+ // Need to make a copy of the dex elements since we don't want an app image
+ // with pre-resolved things.
+ f = pathList.getClass().getDeclaredField("dexElements");
+ f.setAccessible(true);
+ Object[] dexElements = (Object[]) f.get(pathList);
+ f = dexElements[0].getClass().getDeclaredField("dexFile");
+ f.setAccessible(true);
+ for (Object element : dexElements) {
+ Object dexFile = f.get(element);
+ // Make copy.
+ Field fileNameField = dexFile.getClass().getDeclaredField("mFileName");
+ fileNameField.setAccessible(true);
+ dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance(
+ fileNameField.get(dexFile)));
}
- }
- } catch (Exception e) { /* Ignore */ }
- return null;
- }
+ }
+
+ ArrayList<Object> dexFiles = new ArrayList<Object>();
+ Field dexFileField;
+
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ // Other classes may also get loaded, ignore those.
+ if (className.equals("LoadedByMyClassLoader")
+ || className.equals("FirstSeenByMyClassLoader")) {
+ System.out.println("Request for " + className);
+ }
+
+ // We're only going to handle LoadedByMyClassLoader.
+ if (className != "LoadedByMyClassLoader") {
+ return getParent().loadClass(className);
+ }
+
+ // Mimic what DexPathList.findClass is doing.
+ try {
+ for (Object dexFile : dexFiles) {
+ Method method = dexFile.getClass().getDeclaredMethod(
+ "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+ if (dexFile != null) {
+ Class<?> clazz = (Class<?>) method.invoke(dexFile, className, this, null);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+ } catch (Exception e) { /* Ignore */ }
+ return null;
+ }
}
class LoadedByMyClassLoader {
- /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before)
- /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
- /// CHECK-NEXT: ClinitCheck
- /// CHECK-NEXT: InvokeStaticOrDirect
- /// CHECK-NEXT: LoadClass class_name:java.lang.System
- /// CHECK-NEXT: ClinitCheck
- /// CHECK-NEXT: StaticFieldGet
- /// CHECK-NEXT: LoadString
- /// CHECK-NEXT: NullCheck
- /// CHECK-NEXT: InvokeVirtual
+ /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before)
+ /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
+ /// CHECK-NEXT: ClinitCheck
+ /// CHECK-NEXT: InvokeStaticOrDirect
+ /// CHECK-NEXT: LoadClass class_name:java.lang.System
+ /// CHECK-NEXT: ClinitCheck
+ /// CHECK-NEXT: StaticFieldGet
+ /// CHECK-NEXT: LoadString
+ /// CHECK-NEXT: NullCheck
+ /// CHECK-NEXT: InvokeVirtual
- /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after)
- /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
- /// CHECK-NEXT: ClinitCheck
- /* We inlined FirstSeenByMyClassLoader.$inline$bar */
- /// CHECK-NEXT: LoadClass class_name:java.lang.System
- /// CHECK-NEXT: ClinitCheck
- /// CHECK-NEXT: StaticFieldGet
- /// CHECK-NEXT: LoadString
- /// CHECK-NEXT: NullCheck
- /// CHECK-NEXT: InvokeVirtual
+ /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after)
+ /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
+ /// CHECK-NEXT: ClinitCheck
+ /* We inlined FirstSeenByMyClassLoader.$inline$bar */
+ /// CHECK-NEXT: LoadClass class_name:java.lang.System
+ /// CHECK-NEXT: ClinitCheck
+ /// CHECK-NEXT: StaticFieldGet
+ /// CHECK-NEXT: LoadString
+ /// CHECK-NEXT: NullCheck
+ /// CHECK-NEXT: InvokeVirtual
- /// CHECK-START: void LoadedByMyClassLoader.bar() register (before)
- /* Load and initialize FirstSeenByMyClassLoader */
- /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader gen_clinit_check:true
- /* Load and initialize System */
- // There may be HX86ComputeBaseMethodAddress here.
- /// CHECK: LoadClass class_name:java.lang.System
- // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass.
- // (The merging checks for environment match but HLoadClass/kBootImageAddress
- // used for non-PIC mode does not have an environment at all.)
- /// CHECK: StaticFieldGet
- // There may be HX86ComputeBaseMethodAddress here.
- /// CHECK: LoadString
- /// CHECK-NEXT: NullCheck
- /// CHECK-NEXT: InvokeVirtual
- public static void bar() {
- FirstSeenByMyClassLoader.$inline$bar();
- System.out.println("In between the two calls.");
- FirstSeenByMyClassLoader.$noinline$bar();
- }
+ /// CHECK-START: void LoadedByMyClassLoader.bar() register (before)
+ /* Load and initialize FirstSeenByMyClassLoader */
+ /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader gen_clinit_check:true
+ /* Load and initialize System */
+ // There may be HX86ComputeBaseMethodAddress here.
+ /// CHECK: LoadClass class_name:java.lang.System
+ // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass.
+ // (The merging checks for environment match but HLoadClass/kBootImageAddress
+ // used for non-PIC mode does not have an environment at all.)
+ /// CHECK: StaticFieldGet
+ // There may be HX86ComputeBaseMethodAddress here.
+ /// CHECK: LoadString
+ /// CHECK-NEXT: NullCheck
+ /// CHECK-NEXT: InvokeVirtual
+ public static void bar() {
+ FirstSeenByMyClassLoader.$inline$bar();
+ System.out.println("In between the two calls.");
+ FirstSeenByMyClassLoader.$noinline$bar();
+ }
}
public class Main {
- public static void main(String[] args) throws Exception {
- MyClassLoader o = new MyClassLoader();
- Class<?> foo = o.loadClass("LoadedByMyClassLoader");
- Method m = foo.getDeclaredMethod("bar");
- m.invoke(null);
- }
+ public static void main(String[] args) throws Exception {
+ MyClassLoader o = new MyClassLoader();
+ Class<?> foo = o.loadClass("LoadedByMyClassLoader");
+ Method m = foo.getDeclaredMethod("bar");
+ m.invoke(null);
+ }
}
diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc
index 36ec4eb..ddbcef5 100644
--- a/test/497-inlining-and-class-loader/clear_dex_cache.cc
+++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc
@@ -34,7 +34,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
size_t num_methods = dex_cache->NumResolvedMethods();
- mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods();
+ auto* methods = dex_cache->GetResolvedMethods();
CHECK_EQ(num_methods != 0u, methods != nullptr);
if (num_methods == 0u) {
return nullptr;
@@ -48,7 +48,7 @@
CHECK(array != nullptr);
ObjPtr<mirror::Array> decoded_array = soa.Decode<mirror::Array>(array);
for (size_t i = 0; i != num_methods; ++i) {
- auto pair = mirror::DexCache::GetNativePair(methods, i);
+ auto pair = methods->GetNativePair(i);
uint32_t index = pair.index;
ArtMethod* method = pair.object;
if (sizeof(void*) == 4) {
@@ -69,7 +69,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
size_t num_methods = dex_cache->NumResolvedMethods();
- mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods();
+ auto* methods = dex_cache->GetResolvedMethods();
CHECK_EQ(num_methods != 0u, methods != nullptr);
ObjPtr<mirror::Array> old = soa.Decode<mirror::Array>(old_cache);
CHECK_EQ(methods != nullptr, old != nullptr);
@@ -86,8 +86,8 @@
index = dchecked_integral_cast<uint32_t>(long_array->Get(2u * i));
method = reinterpret_cast64<ArtMethod*>(long_array->Get(2u * i + 1u));
}
- mirror::MethodDexCachePair pair(method, index);
- mirror::DexCache::SetNativePair(methods, i, pair);
+ mirror::NativeDexCachePair<ArtMethod> pair(method, index);
+ methods->SetNativePair(i, pair);
}
}
diff --git a/test/497-inlining-and-class-loader/src/Level1.java b/test/497-inlining-and-class-loader/src/Level1.java
index 977af83..18f79ce 100644
--- a/test/497-inlining-and-class-loader/src/Level1.java
+++ b/test/497-inlining-and-class-loader/src/Level1.java
@@ -15,13 +15,13 @@
*/
public class Level1 {
- public static void $inline$bar() {
- Level2.$inline$bar();
- }
+ public static void $inline$bar() {
+ Level2.$inline$bar();
+ }
}
class Level2 {
- public static void $inline$bar() {
- Main.$noinline$bar();
- }
+ public static void $inline$bar() {
+ Main.$noinline$bar();
+ }
}
diff --git a/test/497-inlining-and-class-loader/src/Main.java b/test/497-inlining-and-class-loader/src/Main.java
index 01b4bcd..66a3f6e 100644
--- a/test/497-inlining-and-class-loader/src/Main.java
+++ b/test/497-inlining-and-class-loader/src/Main.java
@@ -19,112 +19,112 @@
import java.util.List;
class MyClassLoader extends ClassLoader {
- MyClassLoader() throws Exception {
- super(MyClassLoader.class.getClassLoader());
+ MyClassLoader() throws Exception {
+ super(MyClassLoader.class.getClassLoader());
- // Some magic to get access to the pathList field of BaseDexClassLoader.
- ClassLoader loader = getClass().getClassLoader();
- Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
- Field f = baseDexClassLoader.getDeclaredField("pathList");
- f.setAccessible(true);
- Object pathList = f.get(loader);
+ // Some magic to get access to the pathList field of BaseDexClassLoader.
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+ Field f = baseDexClassLoader.getDeclaredField("pathList");
+ f.setAccessible(true);
+ Object pathList = f.get(loader);
- // Some magic to get access to the dexField field of pathList.
- f = pathList.getClass().getDeclaredField("dexElements");
- f.setAccessible(true);
- dexElements = (Object[]) f.get(pathList);
- dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
- dexFileField.setAccessible(true);
- }
-
- Object[] dexElements;
- Field dexFileField;
-
- static ClassLoader level1ClassLoader;
-
- protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
- if (this != level1ClassLoader) {
- if (className.equals("Level1")) {
- return level1ClassLoader.loadClass(className);
- } else if (className.equals("Level2")) {
- throw new ClassNotFoundException("None of my methods require Level2!");
- } else if (!className.equals("LoadedByMyClassLoader")) {
- // We're only going to handle LoadedByMyClassLoader.
- return getParent().loadClass(className);
- }
- } else {
- if (className != "Level1" && className != "Level2") {
- return getParent().loadClass(className);
- }
+ // Some magic to get access to the dexField field of pathList.
+ f = pathList.getClass().getDeclaredField("dexElements");
+ f.setAccessible(true);
+ dexElements = (Object[]) f.get(pathList);
+ dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
+ dexFileField.setAccessible(true);
}
- // Mimic what DexPathList.findClass is doing.
- try {
- for (Object element : dexElements) {
- Object dex = dexFileField.get(element);
- Method method = dex.getClass().getDeclaredMethod(
- "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+ Object[] dexElements;
+ Field dexFileField;
- if (dex != null) {
- Class<?> clazz = (Class<?>)method.invoke(dex, className, this, null);
- if (clazz != null) {
- return clazz;
- }
+ static ClassLoader level1ClassLoader;
+
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ if (this != level1ClassLoader) {
+ if (className.equals("Level1")) {
+ return level1ClassLoader.loadClass(className);
+ } else if (className.equals("Level2")) {
+ throw new ClassNotFoundException("None of my methods require Level2!");
+ } else if (!className.equals("LoadedByMyClassLoader")) {
+ // We're only going to handle LoadedByMyClassLoader.
+ return getParent().loadClass(className);
+ }
+ } else {
+ if (className != "Level1" && className != "Level2") {
+ return getParent().loadClass(className);
+ }
}
- }
- } catch (Exception e) { /* Ignore */ }
- return null;
- }
+
+ // Mimic what DexPathList.findClass is doing.
+ try {
+ for (Object element : dexElements) {
+ Object dex = dexFileField.get(element);
+ Method method = dex.getClass().getDeclaredMethod(
+ "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+ if (dex != null) {
+ Class<?> clazz = (Class<?>) method.invoke(dex, className, this, null);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+ } catch (Exception e) { /* Ignore */ }
+ return null;
+ }
}
class LoadedByMyClassLoader {
- public static void bar() {
- Level1.$inline$bar();
- }
+ public static void bar() {
+ Level1.$inline$bar();
+ }
}
class Main {
- public static void main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
- // Clone resolved methods, to restore the original version just
- // before we walk the stack in $noinline$bar.
- savedResolvedMethods = cloneResolvedMethods(Main.class);
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Clone resolved methods, to restore the original version just
+ // before we walk the stack in $noinline$bar.
+ savedResolvedMethods = cloneResolvedMethods(Main.class);
- MyClassLoader o = new MyClassLoader();
- MyClassLoader.level1ClassLoader = new MyClassLoader();
- Class<?> foo = o.loadClass("LoadedByMyClassLoader");
- Method m = foo.getDeclaredMethod("bar");
- try {
- m.invoke(null);
- } catch (Error e) { /* Ignore */ }
- }
+ MyClassLoader o = new MyClassLoader();
+ MyClassLoader.level1ClassLoader = new MyClassLoader();
+ Class<?> foo = o.loadClass("LoadedByMyClassLoader");
+ Method m = foo.getDeclaredMethod("bar");
+ try {
+ m.invoke(null);
+ } catch (Error e) { /* Ignore */ }
+ }
- public static void $inline$bar() {
- }
+ public static void $inline$bar() {
+ }
- public static void $noinline$bar() {
- try {
- // Be evil and clear all dex cache entries.
- Field f = Class.class.getDeclaredField("dexCache");
- f.setAccessible(true);
- Object dexCache = f.get(Main.class);
- f = dexCache.getClass().getDeclaredField("resolvedTypes");
- f.setAccessible(true);
- Object[] array = (Object[]) f.get(dexCache);
- for (int i = 0; i < array.length; i++) {
- array[i] = null;
- }
- restoreResolvedMethods(Main.class, savedResolvedMethods);
- } catch (Throwable t) { /* Ignore */ }
+ public static void $noinline$bar() {
+ try {
+ // Be evil and clear all dex cache entries.
+ Field f = Class.class.getDeclaredField("dexCache");
+ f.setAccessible(true);
+ Object dexCache = f.get(Main.class);
+ f = dexCache.getClass().getDeclaredField("resolvedTypes");
+ f.setAccessible(true);
+ Object[] array = (Object[]) f.get(dexCache);
+ for (int i = 0; i < array.length; i++) {
+ array[i] = null;
+ }
+ restoreResolvedMethods(Main.class, savedResolvedMethods);
+ } catch (Throwable t) { /* Ignore */ }
- // This will walk the stack, trying to resolve methods in it.
- // Because we cleared dex cache entries, we will have to find
- // classes again, which require to use the correct class loader
- // in the presence of inlining.
- new Exception().printStackTrace(System.out);
- }
- static Object savedResolvedMethods;
+ // This will walk the stack, trying to resolve methods in it.
+ // Because we cleared dex cache entries, we will have to find
+ // classes again, which require to use the correct class loader
+ // in the presence of inlining.
+ new Exception().printStackTrace(System.out);
+ }
+ static Object savedResolvedMethods;
- static native Object cloneResolvedMethods(Class<?> cls);
- static native void restoreResolvedMethods(Class<?> cls, Object saved);
+ static native Object cloneResolvedMethods(Class<?> cls);
+ static native void restoreResolvedMethods(Class<?> cls, Object saved);
}
diff --git a/test/498-type-propagation/src/Main.java b/test/498-type-propagation/src/Main.java
index b20794c..ceb35eb 100644
--- a/test/498-type-propagation/src/Main.java
+++ b/test/498-type-propagation/src/Main.java
@@ -17,11 +17,11 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("TypePropagation");
- Method m = c.getMethod("method", int[].class);
- int[] array = new int[7];
- Object[] arguments = { array };
- m.invoke(null, arguments);
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("TypePropagation");
+ Method m = c.getMethod("method", int[].class);
+ int[] array = new int[7];
+ Object[] arguments = { array };
+ m.invoke(null, arguments);
+ }
}
diff --git a/test/499-bce-phi-array-length/src/Main.java b/test/499-bce-phi-array-length/src/Main.java
index e917bc1..4e99771 100644
--- a/test/499-bce-phi-array-length/src/Main.java
+++ b/test/499-bce-phi-array-length/src/Main.java
@@ -15,50 +15,50 @@
*/
public class Main {
- public static int foo(int start, int[] array) {
- int result = 0;
- // We will create HDeoptimize nodes for this first loop, and a phi
- // for the array length which will only be used within the loop.
- for (int i = start; i < 3; i++) {
- result += array[i];
- for (int j = 0; j < 2; ++j) {
- // The HBoundsCheck for this array access will be updated to access
- // the array length phi created for the deoptimization checks of the
- // first loop. This crashed the compiler which used to DCHECK an array
- // length in a bounds check cannot be a phi.
- result += array[j];
- }
- }
- return result;
- }
-
- public static int bar(int start, int[] array) {
- int result = 0;
- for (int i = start; i < 3; i++) {
- result += array[i];
- for (int j = 0; j < 2; ++j) {
- result += array[j];
- // The following operations would lead to BCE wanting to add another
- // deoptimization, but it crashed assuming the input of a `HBoundsCheck`
- // must be a `HArrayLength`.
- result += array[0];
- result += array[1];
- result += array[2];
- }
- }
- return result;
- }
-
- public static void main(String[] args) {
- int[] a = new int[] { 1, 2, 3, 4, 5 };
- int result = foo(1, a);
- if (result != 11) {
- throw new Error("Got " + result + ", expected " + 11);
+ public static int foo(int start, int[] array) {
+ int result = 0;
+ // We will create HDeoptimize nodes for this first loop, and a phi
+ // for the array length which will only be used within the loop.
+ for (int i = start; i < 3; i++) {
+ result += array[i];
+ for (int j = 0; j < 2; ++j) {
+ // The HBoundsCheck for this array access will be updated to access
+ // the array length phi created for the deoptimization checks of the
+ // first loop. This crashed the compiler which used to DCHECK an array
+ // length in a bounds check cannot be a phi.
+ result += array[j];
+ }
+ }
+ return result;
}
- result = bar(1, a);
- if (result != 35) {
- throw new Error("Got " + result + ", expected " + 35);
+ public static int bar(int start, int[] array) {
+ int result = 0;
+ for (int i = start; i < 3; i++) {
+ result += array[i];
+ for (int j = 0; j < 2; ++j) {
+ result += array[j];
+ // The following operations would lead to BCE wanting to add another
+ // deoptimization, but it crashed assuming the input of a `HBoundsCheck`
+ // must be a `HArrayLength`.
+ result += array[0];
+ result += array[1];
+ result += array[2];
+ }
+ }
+ return result;
}
- }
+
+ public static void main(String[] args) {
+ int[] a = new int[] { 1, 2, 3, 4, 5 };
+ int result = foo(1, a);
+ if (result != 11) {
+ throw new Error("Got " + result + ", expected " + 11);
+ }
+
+ result = bar(1, a);
+ if (result != 35) {
+ throw new Error("Got " + result + ", expected " + 35);
+ }
+ }
}
diff --git a/test/515-dce-dominator/src/Main.java b/test/515-dce-dominator/src/Main.java
index 8c6ce75..f36b90c 100644
--- a/test/515-dce-dominator/src/Main.java
+++ b/test/515-dce-dominator/src/Main.java
@@ -17,10 +17,10 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("Dominator");
- Method m = c.getMethod("method", int.class);
- Object[] arguments = { 5 };
- m.invoke(null, arguments);
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("Dominator");
+ Method m = c.getMethod("method", int.class);
+ Object[] arguments = { 5 };
+ m.invoke(null, arguments);
+ }
}
diff --git a/test/516-dead-move-result/src/Main.java b/test/516-dead-move-result/src/Main.java
index 8f0891a..c2f34c7 100644
--- a/test/516-dead-move-result/src/Main.java
+++ b/test/516-dead-move-result/src/Main.java
@@ -17,10 +17,10 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("MoveResult");
- Method m = c.getMethod("method");
- Object[] arguments = { };
- m.invoke(null, arguments);
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("MoveResult");
+ Method m = c.getMethod("method");
+ Object[] arguments = { };
+ m.invoke(null, arguments);
+ }
}
diff --git a/test/517-checker-builder-fallthrough/src/Main.java b/test/517-checker-builder-fallthrough/src/Main.java
index 14170f5..5bec073 100644
--- a/test/517-checker-builder-fallthrough/src/Main.java
+++ b/test/517-checker-builder-fallthrough/src/Main.java
@@ -18,15 +18,15 @@
public class Main {
- public static int runTest(int input) throws Exception {
- Class<?> c = Class.forName("TestCase");
- Method m = c.getMethod("testCase", int.class);
- return (Integer) m.invoke(null, input);
- }
-
- public static void main(String[] args) throws Exception {
- if (runTest(42) != 42) {
- throw new Error("Expected 42");
+ public static int runTest(int input) throws Exception {
+ Class<?> c = Class.forName("TestCase");
+ Method m = c.getMethod("testCase", int.class);
+ return (Integer) m.invoke(null, input);
}
- }
+
+ public static void main(String[] args) throws Exception {
+ if (runTest(42) != 42) {
+ throw new Error("Expected 42");
+ }
+ }
}
diff --git a/test/518-null-array-get/src/Main.java b/test/518-null-array-get/src/Main.java
index 7090194..12e2442 100644
--- a/test/518-null-array-get/src/Main.java
+++ b/test/518-null-array-get/src/Main.java
@@ -18,37 +18,37 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- checkLoad("NullArrayFailInt2Object", true);
- checkLoad("NullArrayFailObject2Int", true);
- checkLoad("NullArraySuccessInt", false);
- checkLoad("NullArraySuccessInt2Float", false);
- checkLoad("NullArraySuccessShort", false);
- checkLoad("NullArraySuccessRef", false);
- }
-
- private static void checkLoad(String className, boolean expectError) throws Exception {
- Class<?> c;
- try {
- c = Class.forName(className);
- if (expectError) {
- throw new RuntimeException("Expected error for " + className);
- }
- Method m = c.getMethod("method");
- try {
- m.invoke(null);
- throw new RuntimeException("Expected an InvocationTargetException");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof NullPointerException)) {
- throw new RuntimeException("Expected a NullPointerException");
- }
- System.out.println(className);
- }
- } catch (VerifyError e) {
- if (!expectError) {
- throw new RuntimeException(e);
- }
- System.out.println(className);
+ public static void main(String[] args) throws Exception {
+ checkLoad("NullArrayFailInt2Object", true);
+ checkLoad("NullArrayFailObject2Int", true);
+ checkLoad("NullArraySuccessInt", false);
+ checkLoad("NullArraySuccessInt2Float", false);
+ checkLoad("NullArraySuccessShort", false);
+ checkLoad("NullArraySuccessRef", false);
}
- }
+
+ private static void checkLoad(String className, boolean expectError) throws Exception {
+ Class<?> c;
+ try {
+ c = Class.forName(className);
+ if (expectError) {
+ throw new RuntimeException("Expected error for " + className);
+ }
+ Method m = c.getMethod("method");
+ try {
+ m.invoke(null);
+ throw new RuntimeException("Expected an InvocationTargetException");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof NullPointerException)) {
+ throw new RuntimeException("Expected a NullPointerException");
+ }
+ System.out.println(className);
+ }
+ } catch (VerifyError e) {
+ if (!expectError) {
+ throw new RuntimeException(e);
+ }
+ System.out.println(className);
+ }
+ }
}
diff --git a/test/519-bound-load-class/src/Main.java b/test/519-bound-load-class/src/Main.java
index cddeb09..c507aec 100644
--- a/test/519-bound-load-class/src/Main.java
+++ b/test/519-bound-load-class/src/Main.java
@@ -15,25 +15,25 @@
*/
public class Main {
- public static void main(String[] args) {
- testInstanceOf();
- try {
- testNull();
- throw new Error("Expected ClassClastException");
- } catch (ClassCastException e) { /* ignore */ }
- }
-
- public static void testInstanceOf() {
- Object o = Main.class;
- if (o instanceof Main) {
- System.out.println((Main)o);
+ public static void main(String[] args) {
+ testInstanceOf();
+ try {
+ testNull();
+ throw new Error("Expected ClassClastException");
+ } catch (ClassCastException e) { /* ignore */ }
}
- }
- public static void testNull() {
- Object o = Main.class;
- if (o != null) {
- System.out.println((Main)o);
+ public static void testInstanceOf() {
+ Object o = Main.class;
+ if (o instanceof Main) {
+ System.out.println((Main) o);
+ }
}
- }
+
+ public static void testNull() {
+ Object o = Main.class;
+ if (o != null) {
+ System.out.println((Main) o);
+ }
+ }
}
diff --git a/test/521-checker-array-set-null/src/Main.java b/test/521-checker-array-set-null/src/Main.java
index f166b92..12a1985 100644
--- a/test/521-checker-array-set-null/src/Main.java
+++ b/test/521-checker-array-set-null/src/Main.java
@@ -16,26 +16,64 @@
public class Main {
public static void main(String[] args) {
- testWithNull(new Object[2]);
- testWithUnknown(new Object[2], new Object());
- testWithSame(new Object[2]);
+ $noinline$testWithNull(new Object[2]);
+ $noinline$testWithUnknown(new Object[2], new Object());
+ $noinline$testWithSame(new Object[2]);
+ $noinline$testWithSameRTI();
}
- /// CHECK-START: void Main.testWithNull(java.lang.Object[]) disassembly (after)
- /// CHECK: ArraySet needs_type_check:false
- public static void testWithNull(Object[] o) {
+ // Known null can eliminate the type check in early stages.
+
+ /// CHECK-START: void Main.$noinline$testWithNull(java.lang.Object[]) instruction_simplifier (before)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+
+ /// CHECK-START: void Main.$noinline$testWithNull(java.lang.Object[]) instruction_simplifier (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+
+ /// CHECK-START: void Main.$noinline$testWithNull(java.lang.Object[]) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+ public static void $noinline$testWithNull(Object[] o) {
o[0] = null;
}
- /// CHECK-START: void Main.testWithUnknown(java.lang.Object[], java.lang.Object) disassembly (after)
- /// CHECK: ArraySet needs_type_check:true
- public static void testWithUnknown(Object[] o, Object obj) {
+ /// CHECK-START: void Main.$noinline$testWithUnknown(java.lang.Object[], java.lang.Object) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+ public static void $noinline$testWithUnknown(Object[] o, Object obj) {
o[0] = obj;
}
- /// CHECK-START: void Main.testWithSame(java.lang.Object[]) disassembly (after)
- /// CHECK: ArraySet needs_type_check:false
- public static void testWithSame(Object[] o) {
+ // After GVN we know that we are setting values from the same array so there's no need for a type
+ // check.
+
+ /// CHECK-START: void Main.$noinline$testWithSame(java.lang.Object[]) instruction_simplifier$after_gvn (before)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+
+ /// CHECK-START: void Main.$noinline$testWithSame(java.lang.Object[]) instruction_simplifier$after_gvn (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+
+ /// CHECK-START: void Main.$noinline$testWithSame(java.lang.Object[]) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+ public static void $noinline$testWithSame(Object[] o) {
o[0] = o[1];
}
+
+ // We know that the array and the static Object have the same RTI in early stages. No need for a
+ // type check.
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testWithSameRTI() instruction_simplifier (before)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testWithSameRTI() instruction_simplifier (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testWithSameRTI() disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+ public static Object[] $noinline$testWithSameRTI() {
+ Object[] arr = new Object[1];
+ arr[0] = static_obj;
+ // Return so that LSE doesn't eliminate the ArraySet.
+ return arr;
+ }
+
+ static Object static_obj;
}
diff --git a/test/521-regression-integer-field-set/src/Main.java b/test/521-regression-integer-field-set/src/Main.java
index 9924e09..90890a8 100644
--- a/test/521-regression-integer-field-set/src/Main.java
+++ b/test/521-regression-integer-field-set/src/Main.java
@@ -31,24 +31,12 @@
assertIntEquals(456789, s);
}
- private static boolean doThrow = false;
-
private void $noinline$SetInstanceField() {
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
-
// Set a value than does not fit in a 16-bit (signed) integer.
i = 123456;
}
private static void $noinline$SetStaticField() {
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
-
// Set a value than does not fit in a 16-bit (signed) integer.
s = 456789;
}
diff --git a/test/526-checker-caller-callee-regs/src/Main.java b/test/526-checker-caller-callee-regs/src/Main.java
index f402c2c..9bd08ff 100644
--- a/test/526-checker-caller-callee-regs/src/Main.java
+++ b/test/526-checker-caller-callee-regs/src/Main.java
@@ -22,12 +22,8 @@
}
}
- static boolean doThrow = false;
-
// This function always returns 1.
- // We use 'throw' to prevent the function from being inlined.
public static int $opt$noinline$function_call(int arg) {
- if (doThrow) throw new Error();
return 1 % arg;
}
diff --git a/test/526-long-regalloc/src/Main.java b/test/526-long-regalloc/src/Main.java
index e8b3096..489db05 100644
--- a/test/526-long-regalloc/src/Main.java
+++ b/test/526-long-regalloc/src/Main.java
@@ -54,12 +54,9 @@
}
public static long $noinline$bar() {
- if (doThrow) throw new Error();
return 42;
}
- public static boolean doThrow = false;
-
public static int myField1 = 0;
public static int myField2 = 0;
public static int myField3 = 0;
diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java
index f39b5e2..cc1dc6b 100644
--- a/test/527-checker-array-access-split/src/Main.java
+++ b/test/527-checker-array-access-split/src/Main.java
@@ -593,12 +593,16 @@
/// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
/// CHECK: <<IntAddr2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
/// CHECK: ArrayGet [<<IntAddr2>>,{{i\d+}}]
- /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] needs_type_check:false can_trigger_gc:false
/// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
/// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
//
+ /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (after)
+ /// CHECK: IntermediateAddress
+ /// CHECK: IntermediateAddress
+ /// CHECK: IntermediateAddress
/// CHECK-NOT: IntermediateAddress
/// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) GVN$after_arch (after)
@@ -608,12 +612,13 @@
/// CHECK: <<IntAddr1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
/// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
/// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
- /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
- /// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
- /// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] needs_type_check:false can_trigger_gc:false
+ /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
//
+ /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) GVN$after_arch (after)
+ /// CHECK: IntermediateAddress
/// CHECK-NOT: IntermediateAddress
public final static int checkObjectArrayGet(int index, Integer[] a, Integer[] b) {
Integer five = Integer.valueOf(5);
diff --git a/test/529-long-split/src/Main.java b/test/529-long-split/src/Main.java
index dc52d88..3493f77 100644
--- a/test/529-long-split/src/Main.java
+++ b/test/529-long-split/src/Main.java
@@ -167,14 +167,11 @@
}
public static void $noinline$doCall() {
- if (doThrow) throw new Error();
}
public static void $noinline$doCall(long e) {
- if (doThrow) throw new Error();
}
- public static boolean doThrow;
public static int myField1;
public static int myField2;
public static int myField3;
diff --git a/test/530-checker-lse-simd/src/Main.java b/test/530-checker-lse-simd/src/Main.java
index d6049d0..619ac28 100644
--- a/test/530-checker-lse-simd/src/Main.java
+++ b/test/530-checker-lse-simd/src/Main.java
@@ -231,7 +231,6 @@
/// CHECK: BoundsCheck loop:none
/// CHECK: ArrayGet
/// CHECK: Add
- /// CHECK: ArrayLength
//
/// CHECK: VecLoad loop:{{B\d+}}
/// CHECK: VecStore
@@ -244,7 +243,6 @@
/// CHECK-START-ARM64: double[] Main.$noinline$test06(int) load_store_elimination (after)
/// CHECK: BoundsCheck loop:none
/// CHECK: Add
- /// CHECK: ArrayLength
//
/// CHECK: VecLoad loop:{{B\d+}}
/// CHECK: VecAdd
diff --git a/test/530-checker-lse-try-catch/src/Main.java b/test/530-checker-lse-try-catch/src/Main.java
index 56ef8bd..3f4fa25 100644
--- a/test/530-checker-lse-try-catch/src/Main.java
+++ b/test/530-checker-lse-try-catch/src/Main.java
@@ -30,7 +30,19 @@
assertEquals(1, $noinline$testTryCatchBlocking(new Point(), boolean_throw));
assertEquals(1, $noinline$testTryCatchPhi(new Point(), boolean_throw));
assertEquals(2, $noinline$testTryCatchPhiWithTwoCatches(new Point(), new int[0]));
- assertEquals(1, $noinline$testKeepStoreInsideTryCatch());
+ assertEquals(1, $noinline$testKeepStoreInsideTry());
+ assertEquals(10, $noinline$testDontKeepStoreInsideCatch(new int[]{10}));
+ assertEquals(30, $noinline$testDontKeepStoreInsideCatch(new int[]{}));
+ assertEquals(10, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{10}));
+ assertEquals(30, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{}));
+ assertEquals(40, $noinline$testDontKeepStoreInsideFinally(new int[]{10}));
+ try {
+ assertEquals(30, $noinline$testDontKeepStoreInsideFinally(new int[]{}));
+ throw new Error("Unreachable");
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
+ assertEquals(10, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{10}));
+ assertEquals(100030, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{}));
assertEquals(150, $noinline$test40());
}
@@ -205,17 +217,17 @@
// is observable.
// Consistency check to make sure the try/catch wasn't removed by an earlier pass.
- /// CHECK-START: int Main.$noinline$testKeepStoreInsideTryCatch() load_store_elimination (after)
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after)
/// CHECK-DAG: TryBoundary kind:entry
- /// CHECK-START: int Main.$noinline$testKeepStoreInsideTryCatch() load_store_elimination (before)
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (before)
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
- /// CHECK-START: int Main.$noinline$testKeepStoreInsideTryCatch() load_store_elimination (after)
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after)
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
- private static int $noinline$testKeepStoreInsideTryCatch() {
+ private static int $noinline$testKeepStoreInsideTry() {
Main main = new Main();
main.sumForKeepStoreInsideTryCatch = 0;
try {
@@ -228,6 +240,118 @@
}
}
+ private static int $noinline$returnValue(int value) {
+ return value;
+ }
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ private static int $noinline$testDontKeepStoreInsideCatch(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ value = array[0];
+ } catch (Exception e) {
+ // These sets can be eliminated even though we have invokes since this catch is not part of an
+ // outer try.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ }
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ private static int $noinline$testKeepStoreInsideCatchWithOuterTry(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ try {
+ value = array[0];
+ } catch (Exception e) {
+ // These sets can't be eliminated since this catch is part of a outer try.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ }
+ } catch (Exception e) {
+ value = 100000;
+ }
+
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
+ // Note that there are four `InstanceFieldSet` instead of two since we split the `finally` block
+ // into the normal path, and the exceptional path.
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ private static int $noinline$testDontKeepStoreInsideFinally(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ value = array[0];
+ } finally {
+ // These sets can be eliminated even though we have invokes since this catch is not part of an
+ // outer try.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ }
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
+ // Checks that we are able to do LSE inside of catches which are outside of try blocks.
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ // This store potentially can be eliminated too, but our phi creation logic doesn't realize it can
+ // create a Phi for `main.sumForKeepStoreInsideTryCatch` and skip a store+load.
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ private static int $noinline$testDontKeepStoreInsideOuterCatch(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ value = array[0];
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // These sets and gets are not considered to be part of a try so they are free to be
+ // eliminated.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ try {
+ value = array[0];
+ } catch (ArrayIndexOutOfBoundsException expectedToo) {
+ value = 100000;
+ }
+ }
+
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
/// CHECK-START: int Main.$noinline$test40() load_store_elimination (before)
/// CHECK: ArraySet
/// CHECK: DivZeroCheck
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 5d55d3e..8754449 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -217,12 +217,9 @@
/// CHECK-DAG: Return
/// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after)
- /// CHECK: NullCheck
- /// CHECK: NullCheck
- /// CHECK-NOT: NullCheck
+ /// CHECK-NOT: InstanceFieldGet
/// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after)
- /// CHECK-NOT: InstanceFieldGet
/// CHECK-NOT: Phi
// Set and merge the same value in two branches.
@@ -554,16 +551,15 @@
}
/// CHECK-START: void Main.test21(TestClass) load_store_elimination (before)
- /// CHECK: NewInstance
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldGet
+ /// CHECK-DAG: NewInstance
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+ /// CHECK-DAG: InstanceFieldGet
/// CHECK-START: void Main.test21(TestClass) load_store_elimination (after)
/// CHECK-DAG: InstanceFieldSet
- /// CHECK-DAG: Phi
/// CHECK-START: void Main.test21(TestClass) load_store_elimination (after)
/// CHECK-NOT: NewInstance
@@ -1853,14 +1849,22 @@
/// CHECK: ArraySet
/// CHECK: ArrayGet
- /// CHECK-START: int Main.testAllocationEliminationOfArray2() load_store_elimination (after)
+ /// CHECK-START-{ARM64,X86,X86_64}: int Main.testAllocationEliminationOfArray2() load_store_elimination (after)
+ /// CHECK-NOT: NewArray
+ /// CHECK-NOT: ArraySet
+ /// CHECK-NOT: ArraySet
+ /// CHECK-NOT: ArrayGet
+
+ // The loop optimization doesn't happen in ARM which leads to LSE not being able to optimize this
+ // case.
+ /// CHECK-START-ARM: int Main.testAllocationEliminationOfArray2() load_store_elimination (after)
/// CHECK: NewArray
/// CHECK: ArraySet
/// CHECK: ArraySet
/// CHECK: ArrayGet
private static int testAllocationEliminationOfArray2() {
- // Cannot eliminate array allocation since array is accessed with non-constant
- // index (only 3 elements to prevent vectorization of the reduction).
+ // Array can be eliminated because LSE can reduce the array accesses into
+ // integer constants.
int[] array = new int[3];
array[1] = 4;
array[2] = 7;
@@ -1924,6 +1928,36 @@
return array[1];
}
+ /// CHECK-START: int Main.testAllocationEliminationOfArray6(boolean) load_store_elimination (before)
+ /// CHECK: NewArray
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK: ArrayGet
+
+ /// CHECK-START: int Main.testAllocationEliminationOfArray6(boolean) load_store_elimination (after)
+ /// CHECK: NewArray
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK: ArrayGet
+ private static int testAllocationEliminationOfArray6(boolean prevent_loop_opt) {
+ // Cannot eliminate array allocation since array is accessed with non-constant
+ // index (only 3 elements to prevent vectorization of the reduction).
+ int[] array = new int[3];
+ array[1] = 4;
+ array[2] = 7;
+ int sum = 0;
+ for (int e : array) {
+ sum += e;
+
+ // Prevent the loop from being optimized away before LSE. This should
+ // never be false.
+ if (!prevent_loop_opt) {
+ return -1;
+ }
+ }
+ return sum;
+ }
+
/// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before)
/// CHECK-DAG: NewInstance
/// CHECK-DAG: InstanceFieldSet field_name:TestClass.i
@@ -4213,6 +4247,7 @@
} catch (NegativeArraySizeException e) {
System.out.println("Got NegativeArraySizeException.");
}
+ assertIntEquals(testAllocationEliminationOfArray6(true), 11);
assertIntEquals(testStoreStore().i, 41);
assertIntEquals(testStoreStore().j, 43);
diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java
index f909af3..d97de2a 100644
--- a/test/530-checker-peel-unroll/src/Main.java
+++ b/test/530-checker-peel-unroll/src/Main.java
@@ -1015,7 +1015,7 @@
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none
@@ -1030,21 +1030,21 @@
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: GreaterThanOrEqual
/// CHECK-NOT: GreaterThanOrEqual
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: ArrayGet
/// CHECK: ArrayGet
/// CHECK-NOT: ArrayGet
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: ArraySet
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
@@ -1069,7 +1069,7 @@
/// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none
/// CHECK-DAG: <<ConstNull:l\d+>> NullConstant loop:none
/// CHECK-DAG: <<Eq:z\d+>> Equal [<<Param>>,<<ConstNull>>] loop:none
@@ -1081,7 +1081,7 @@
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
// There's a 3rd `if` due to bounds checks.
- /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
@@ -1118,7 +1118,7 @@
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
- /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none
@@ -1135,13 +1135,13 @@
/// CHECK-DAG: <<IndAdd1:i\d+>> Add [<<Phi1>>,<<Const1>>] loop:<<Loop1>> outer_loop:<<Loop0>>
/// CHECK-DAG: <<IndAdd0:i\d+>> Add [<<Phi0>>,<<Const1>>] loop:<<Loop0>> outer_loop:none
- /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: ArraySet
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
@@ -1170,7 +1170,7 @@
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:i\d+>> ParameterValue loop:none
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<Check:z\d+>> NotEqual [<<Param>>,<<Const0>>] loop:none
@@ -1181,14 +1181,14 @@
// Check that the loop has no instruction except SuspendCheck and Goto (indefinite loop).
/// CHECK-NOT: loop:<<Loop>> outer_loop:none
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK-NOT: Phi
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK-NOT: Add
private static final int peelingHoistOneControl(int x) {
int i = 0;
@@ -1204,12 +1204,12 @@
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
- /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
// One `if` inside the loop (the one no longer invariant), two outside of it.
- /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
@@ -1230,12 +1230,12 @@
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
- /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
// One `if` inside the loop (the one no longer invariant), three outside of it.
- /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
diff --git a/test/530-checker-regression-reftyp-final/src/Main.java b/test/530-checker-regression-reftyp-final/src/Main.java
index f86b515..69d99e6 100644
--- a/test/530-checker-regression-reftyp-final/src/Main.java
+++ b/test/530-checker-regression-reftyp-final/src/Main.java
@@ -58,9 +58,6 @@
}
public static Object[] $noinline$getArray() {
- if (doThrow) throw new Error();
return new MyClassB[2];
}
-
- static boolean doThrow = false;
}
diff --git a/test/534-checker-bce-deoptimization/src/Main.java b/test/534-checker-bce-deoptimization/src/Main.java
index c4e4cbf..45dcbce 100644
--- a/test/534-checker-bce-deoptimization/src/Main.java
+++ b/test/534-checker-bce-deoptimization/src/Main.java
@@ -89,7 +89,6 @@
/// CHECK-NOT: BoundsCheck
public static void $noinline$FloatFill(float f1, float f2, float[] array, int n) {
- if (doThrow) { throw new Error(); }
for (int i = 0; i < n; ++i) {
array[i] = ((i & 1) == 1) ? f1 : f2;
f1 += 1.5f;
@@ -118,14 +117,11 @@
/// CHECK-NOT: BoundsCheck
public static void $noinline$DoubleFill(double d1, double d2, double[] array, int n) {
- if (doThrow) { throw new Error(); }
for (int i = 0; i < n; ++i) {
array[i] = ((i & 1) == 1) ? d1 : d2;
d1 += 1.5;
d2 += 2.25;
}
}
-
- public static boolean doThrow = false;
}
diff --git a/test/536-checker-needs-access-check/Android.bp b/test/536-checker-needs-access-check/Android.bp
new file mode 100644
index 0000000..ccf524e
--- /dev/null
+++ b/test/536-checker-needs-access-check/Android.bp
@@ -0,0 +1,53 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `536-checker-needs-access-check`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-536-checker-needs-access-check-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-536-checker-needs-access-check",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-536-checker-needs-access-check-src"
+ ],
+ data: [
+ ":art-run-test-536-checker-needs-access-check-expected-stdout",
+ ":art-run-test-536-checker-needs-access-check-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-536-checker-needs-access-check-expected-stdout",
+ out: ["art-run-test-536-checker-needs-access-check-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-536-checker-needs-access-check-expected-stderr",
+ out: ["art-run-test-536-checker-needs-access-check-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java b/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java
index 646cc7e..6fd99ef 100644
--- a/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java
+++ b/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java
@@ -31,7 +31,7 @@
Class<?> klass = null;
try {
klass = GetInaccessibleClass.$inline$get();
- throw new Error("Unreachable");
+ System.out.println("Unreachable");
} catch (IllegalAccessError expected) {}
return klass;
}
diff --git a/test/537-checker-inline-and-unverified/Android.bp b/test/537-checker-inline-and-unverified/Android.bp
new file mode 100644
index 0000000..8078e92
--- /dev/null
+++ b/test/537-checker-inline-and-unverified/Android.bp
@@ -0,0 +1,53 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `537-checker-inline-and-unverified`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-537-checker-inline-and-unverified-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-537-checker-inline-and-unverified",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-537-checker-inline-and-unverified-src"
+ ],
+ data: [
+ ":art-run-test-537-checker-inline-and-unverified-expected-stdout",
+ ":art-run-test-537-checker-inline-and-unverified-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-537-checker-inline-and-unverified-expected-stdout",
+ out: ["art-run-test-537-checker-inline-and-unverified-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-537-checker-inline-and-unverified-expected-stderr",
+ out: ["art-run-test-537-checker-inline-and-unverified-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/542-bitfield-rotates/src/Main.java b/test/542-bitfield-rotates/src/Main.java
index f2bc153..4308439 100644
--- a/test/542-bitfield-rotates/src/Main.java
+++ b/test/542-bitfield-rotates/src/Main.java
@@ -48,12 +48,7 @@
test_Long_left_constant_v();
}
- public static boolean doThrow = false;
-
public static int $noinline$rotate_int_right_reg_v_csubv(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << (32 - distance));
}
@@ -74,9 +69,6 @@
}
public static long $noinline$rotate_long_right_reg_v_csubv(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << (64 - distance));
}
@@ -97,9 +89,6 @@
}
public static int $noinline$rotate_int_left_reg_csubv_v(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> (32 - distance)) | (value << distance);
}
@@ -120,9 +109,6 @@
}
public static long $noinline$rotate_long_left_reg_csubv_v(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> (64 - distance)) | (value << distance);
}
@@ -143,9 +129,6 @@
}
public static int $noinline$rotate_int_right_reg_v_negv(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << -distance);
}
@@ -166,9 +149,6 @@
}
public static long $noinline$rotate_long_right_reg_v_negv(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << -distance);
}
@@ -189,9 +169,6 @@
}
public static int $noinline$rotate_int_left_reg_negv_v(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -distance) | (value << distance);
}
@@ -212,9 +189,6 @@
}
public static long $noinline$rotate_long_left_reg_negv_v(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -distance) | (value << distance);
}
@@ -235,30 +209,18 @@
}
public static int $noinline$rotate_int_right_constant_0(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 0) | (value << 0);
}
public static int $noinline$rotate_int_right_constant_1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 1) | (value << -1);
}
public static int $noinline$rotate_int_right_constant_m1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -1) | (value << 1);
}
public static int $noinline$rotate_int_right_constant_16(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 16) | (value << -16);
}
@@ -270,51 +232,30 @@
}
public static long $noinline$rotate_long_right_constant_0(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 0) | (value << 0);
}
public static long $noinline$rotate_long_right_constant_1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 1) | (value << -1);
}
public static long $noinline$rotate_long_right_constant_m1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -1) | (value << 1);
}
public static long $noinline$rotate_long_right_constant_16(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 16) | (value << -16);
}
public static long $noinline$rotate_long_right_constant_32(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 32) | (value << -32);
}
public static long $noinline$rotate_long_right_constant_48(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 48) | (value << -48);
}
public static long $noinline$rotate_long_right_constant_64(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 64) | (value << -64);
}
@@ -328,30 +269,18 @@
}
public static int $noinline$rotate_int_left_constant_0(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 0) | (value >>> 0);
}
public static int $noinline$rotate_int_left_constant_1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 1) | (value >>> -1);
}
public static int $noinline$rotate_int_left_constant_m1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << -1) | (value >>> 1);
}
public static int $noinline$rotate_int_left_constant_16(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 16) | (value >>> -16);
}
@@ -363,51 +292,30 @@
}
public static long $noinline$rotate_long_left_constant_0(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 0) | (value >>> 0);
}
public static long $noinline$rotate_long_left_constant_1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 1) | (value >>> -1);
}
public static long $noinline$rotate_long_left_constant_m1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << -1) | (value >>> 1);
}
public static long $noinline$rotate_long_left_constant_16(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 16) | (value >>> -16);
}
public static long $noinline$rotate_long_left_constant_32(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 32) | (value >>> -32);
}
public static long $noinline$rotate_long_left_constant_48(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 48) | (value >>> -48);
}
public static long $noinline$rotate_long_left_constant_64(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 64) | (value >>> -64);
}
diff --git a/test/542-inline-trycatch/src/Main.java b/test/542-inline-trycatch/src/Main.java
index 5a6e06f..5e33ed6 100644
--- a/test/542-inline-trycatch/src/Main.java
+++ b/test/542-inline-trycatch/src/Main.java
@@ -33,17 +33,6 @@
return is_hex ? Integer.parseInt(str, 16) : Integer.parseInt(str);
}
- // We expect methods with try/catch to not be inlined. Inlined try/catch
- // blocks are not supported at the moment.
-
- private static int $noinline$TryCatch(String str) {
- try {
- return Integer.parseInt(str);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
-
public static void testSingleBlockFromTry() {
int val = 0;
@@ -117,49 +106,11 @@
assertEquals(32, val);
}
- public static void testTryCatchFromTry() {
- int val = 0;
-
- try {
- val = $noinline$TryCatch("42");
- } catch (NumberFormatException ex) {
- unreachable();
- }
- assertEquals(42, val);
-
- try {
- val = $noinline$TryCatch("xyz");
- } catch (NumberFormatException ex) {
- unreachable();
- }
- assertEquals(-1, val);
- }
-
- public static void testTryCatchFromCatch() {
- int val = 0;
-
- try {
- throwException();
- } catch (Exception ex) {
- val = $noinline$TryCatch("42");
- }
- assertEquals(42, val);
-
- try {
- throwException();
- } catch (Exception ex) {
- val = $noinline$TryCatch("xyz");
- }
- assertEquals(-1, val);
- }
-
public static void main(String[] args) {
testSingleBlockFromTry();
testSingleBlockFromCatch();
testMultipleBlocksFromTry();
testMultipleBlocksFromCatch();
- testTryCatchFromTry();
- testTryCatchFromCatch();
}
private static void assertEquals(int expected, int actual) {
diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali
index 7ad9ba8..15eaabb 100644
--- a/test/543-checker-dce-trycatch/smali/TestCase.smali
+++ b/test/543-checker-dce-trycatch/smali/TestCase.smali
@@ -206,6 +206,7 @@
## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination$after_inlining (before)
## CHECK-DAG: <<Arg0:i\d+>> ParameterValue
## CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+## CHECK-DAG: <<Const0x0:i\d+>> IntConstant 0
## CHECK-DAG: <<Const0xa:i\d+>> IntConstant 10
## CHECK-DAG: <<Const0xb:i\d+>> IntConstant 11
## CHECK-DAG: <<Const0xc:i\d+>> IntConstant 12
@@ -215,7 +216,7 @@
## CHECK-DAG: <<Const0x10:i\d+>> IntConstant 16
## CHECK-DAG: <<Const0x11:i\d+>> IntConstant 17
## CHECK-DAG: <<Add:i\d+>> Add [<<Arg0>>,<<Arg1>>]
-## CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Const0xf>>] reg:3 is_catch_phi:false
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0x0>>,<<Const0xf>>] reg:3 is_catch_phi:false
## CHECK-DAG: Phi [<<Const0xa>>,<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
## CHECK-DAG: Phi [<<Add>>,<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
## CHECK-DAG: Phi [<<Phi>>,<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
@@ -248,7 +249,8 @@
if-eqz v3, :define_phi
const v3, 0xf
:define_phi
- # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi)
+ # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi).
+ # Note that the Add has to be equal to 0 since we do `if-eqz v3`
div-int/2addr p0, v2
:else
diff --git a/test/543-env-long-ref/env_long_ref.cc b/test/543-env-long-ref/env_long_ref.cc
index 1c30d46..7084714 100644
--- a/test/543-env-long-ref/env_long_ref.cc
+++ b/test/543-env-long-ref/env_long_ref.cc
@@ -36,7 +36,8 @@
found = true;
// For optimized non-debuggable code do not expect dex register info to be present.
if (stack_visitor->GetCurrentShadowFrame() == nullptr &&
- !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetCurrentQuickFramePc())) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetOuterMethod(),
+ stack_visitor->GetCurrentQuickFramePc())) {
return true;
}
uint32_t stack_value = 0;
diff --git a/test/551-checker-clinit/src/Main.java b/test/551-checker-clinit/src/Main.java
index 0eea800..77a9825 100644
--- a/test/551-checker-clinit/src/Main.java
+++ b/test/551-checker-clinit/src/Main.java
@@ -17,14 +17,14 @@
public class Main {
public static void main(String[] args) {}
- public static int foo = 42;
+ public static long foo = 42;
// Primitive array initialization is trivial for purposes of the ClinitCheck. It cannot
// leak instances of erroneous classes or initialize subclasses of erroneous classes.
- public static int[] array1 = new int[] { 1, 2, 3 };
- public static int[] array2;
+ public static long[] array1 = new long[] { 1, 2, 3 };
+ public static long[] array2;
static {
- int[] a = new int[4];
+ long[] a = new long[4];
a[0] = 42;
array2 = a;
}
@@ -46,35 +46,35 @@
/// CHECK-NOT: ClinitCheck
public void invokeSuperClass() {
// No Class initialization check as Main.<clinit> is trivial. b/62478025
- int a = Main.foo;
+ long a = Main.foo;
}
/// CHECK-START: void Sub.invokeItself() builder (after)
/// CHECK-NOT: ClinitCheck
public void invokeItself() {
// No Class initialization check as Sub.<clinit> and Main.<clinit> are trivial. b/62478025
- int a = foo;
+ long a = foo;
}
/// CHECK-START: void Sub.invokeSubClass() builder (after)
/// CHECK: ClinitCheck
public void invokeSubClass() {
- int a = SubSub.foo;
+ long a = SubSub.foo;
}
- public static int foo = 42;
+ public static long foo = 42;
}
class SubSub {
public static void bar() {
- int a = Main.foo;
+ long a = Main.foo;
}
- public static int foo = 42;
+ public static long foo = 42;
}
class NonTrivial {
- public static int staticFoo = 42;
- public int instanceFoo;
+ public static long staticFoo = 42;
+ public long instanceFoo;
static {
System.out.println("NonTrivial.<clinit>");
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 15ff8a6..b017ab0 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -34,10 +34,7 @@
}
}
- public static boolean doThrow = false;
-
private static int $noinline$foo(int x) {
- if (doThrow) { throw new Error(); }
return x;
}
@@ -135,8 +132,6 @@
/// CHECK: LoadString load_kind:BootImageRelRo
public static String $noinline$getBootImageString() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// Empty string is known to be in the boot image.
return "";
}
@@ -152,8 +147,6 @@
/// CHECK-DAG: LoadString load_kind:BssEntry
public static String $noinline$getNonBootImageString() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// This string is not in the boot image.
return "non-boot-image-string";
}
@@ -162,8 +155,6 @@
/// CHECK: LoadClass load_kind:BootImageRelRo class_name:java.lang.String
public static Class<?> $noinline$getStringClass() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// String class is known to be in the boot image.
return String.class;
}
@@ -179,8 +170,6 @@
/// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other
public static Class<?> $noinline$getOtherClass() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// Other class is not in the boot image.
return Other.class;
}
diff --git a/test/553-invoke-super/src/SuperClass.java b/test/553-invoke-super/src/SuperClass.java
index 36ce093..c3e4a4e 100644
--- a/test/553-invoke-super/src/SuperClass.java
+++ b/test/553-invoke-super/src/SuperClass.java
@@ -15,12 +15,7 @@
*/
public class SuperClass {
- boolean doThrow = false;
-
public int $noinline$returnInt() {
- if (doThrow) {
- throw new Error();
- }
return 42;
}
}
diff --git a/test/559-checker-irreducible-loop/expected-stdout.txt b/test/559-checker-irreducible-loop/expected-stdout.txt
index b64be7a..992fd87 100644
--- a/test/559-checker-irreducible-loop/expected-stdout.txt
+++ b/test/559-checker-irreducible-loop/expected-stdout.txt
@@ -5,3 +5,5 @@
class Main
42
-42
+1
+-1
diff --git a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
index a30a11a..a88422f 100644
--- a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
+++ b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -140,24 +140,23 @@
# other_loop_entry
# i1 = phi(p0, i0)
#
-## CHECK-START: int IrreducibleLoop.liveness(int) liveness (after)
+## CHECK-START: int IrreducibleLoop.liveness(int, int) liveness (after)
## CHECK-DAG: <<Arg:i\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopPhiUse:\d+>>)}
## CHECK-DAG: <<LoopPhi:i\d+>> Phi [<<Arg>>,<<PhiInLoop:i\d+>>] liveness:<<ArgLoopPhiUse>> ranges:{[<<ArgLoopPhiUse>>,<<PhiInLoopUse:\d+>>)}
## CHECK-DAG: <<PhiInLoop>> Phi [<<Arg>>,<<LoopPhi>>] liveness:<<PhiInLoopUse>> ranges:{[<<PhiInLoopUse>>,<<BackEdgeLifetimeEnd:\d+>>)}
## CHECK: Return liveness:<<ReturnLiveness:\d+>>
## CHECK-EVAL: <<ReturnLiveness>> == <<BackEdgeLifetimeEnd>> + 2
-.method public static liveness(I)I
+.method public static liveness(II)I
.registers 2
- const/16 v0, 42
- if-eq p0, v0, :other_loop_entry
+ if-eq p0, p1, :other_loop_entry
:loop_entry
- add-int v0, v0, p0
- if-ne v1, v0, :exit
+ add-int p1, p1, p0
+ if-ne v0, p1, :exit
:other_loop_entry
- add-int v0, v0, v0
+ add-int p1, p1, p1
goto :loop_entry
:exit
- return v0
+ return p1
.end method
# Check that we don't GVN across irreducible loops:
@@ -547,3 +546,57 @@
:exit
return-void
.end method
+
+## CHECK-START: int IrreducibleLoop.testDoNotInlineIrreducible(int) inliner (before)
+## CHECK: InvokeStaticOrDirect method_name:IrreducibleLoop.doNotInlineIrreducible
+#
+## CHECK-START: int IrreducibleLoop.testDoNotInlineIrreducible(int) inliner (after)
+## CHECK: InvokeStaticOrDirect method_name:IrreducibleLoop.doNotInlineIrreducible
+.method public static testDoNotInlineIrreducible(I)I
+ .registers 2
+ invoke-static {p0}, LIrreducibleLoop;->doNotInlineIrreducible(I)I
+ move-result v0
+ return v0
+.end method
+
+# Check that doNotInlineIrreducible has a simple irreducible loop
+#
+# entry
+# / \
+# / \
+# loop_entry \
+# / \- \
+# try_start\- \
+# other_loop_entry
+#
+## CHECK-START: int IrreducibleLoop.doNotInlineIrreducible(int) register (after)
+## CHECK: irreducible:true
+#
+# Check that we didn't optimized away the try.
+## CHECK-START: int IrreducibleLoop.doNotInlineIrreducible(int) register (after)
+## CHECK: TryBoundary kind:exit
+.method public static doNotInlineIrreducible(I)I
+ .registers 3
+ const/16 v0, 42
+ const/16 v1, 21
+ # Irreducible loop
+ if-eq v1, v0, :other_loop_entry
+ :loop_entry
+ if-ne v1, v0, :try_start
+ add-int v0, v0, v0
+ :other_loop_entry
+ add-int v0, v0, v0
+ goto :loop_entry
+
+ :try_start
+ # We make this division to make sure the try doesn't get optimized out
+ div-int v0, v0, p0
+ return v0
+ :try_end
+ .catchall {:try_start .. :try_end} :catch_all
+
+ :catch_all
+ # This is only reachable if the parameter is 0
+ const/4 v0, -0x1
+ return v0
+.end method
diff --git a/test/559-checker-irreducible-loop/src/Main.java b/test/559-checker-irreducible-loop/src/Main.java
index 97165ec..16b8415 100644
--- a/test/559-checker-irreducible-loop/src/Main.java
+++ b/test/559-checker-irreducible-loop/src/Main.java
@@ -38,8 +38,8 @@
}
{
- Method m = c.getMethod("liveness", int.class);
- Object[] arguments = { 42 };
+ Method m = c.getMethod("liveness", int.class, int.class);
+ Object[] arguments = { 42, 42 };
System.out.println(m.invoke(null, arguments));
}
@@ -60,6 +60,18 @@
Object[] arguments = { 42 };
System.out.println(m.invoke(null, arguments));
}
+
+ {
+ Method m = c.getMethod("testDoNotInlineIrreducible", int.class);
+ Object[] arguments = { 42 };
+ System.out.println(m.invoke(null, arguments));
+ }
+
+ {
+ Method m = c.getMethod("testDoNotInlineIrreducible", int.class);
+ Object[] arguments = { 0 };
+ System.out.println(m.invoke(null, arguments));
+ }
}
int myField;
diff --git a/test/561-divrem/src/Main.java b/test/561-divrem/src/Main.java
index 082783d..ec125e9 100644
--- a/test/561-divrem/src/Main.java
+++ b/test/561-divrem/src/Main.java
@@ -72,32 +72,18 @@
}
public static int $noinline$divInt(int value) {
- if (doThrow) {
- throw new Error("");
- }
return value / Integer.MIN_VALUE;
}
public static int $noinline$remInt(int value) {
- if (doThrow) {
- throw new Error("");
- }
return value % Integer.MIN_VALUE;
}
public static long $noinline$divLong(long value) {
- if (doThrow) {
- throw new Error("");
- }
return value / Long.MIN_VALUE;
}
public static long $noinline$remLong(long value) {
- if (doThrow) {
- throw new Error("");
- }
return value % Long.MIN_VALUE;
}
-
- static boolean doThrow = false;
}
diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali
index 4721eca..c6561d5 100644
--- a/test/563-checker-fakestring/smali/TestCase.smali
+++ b/test/563-checker-fakestring/smali/TestCase.smali
@@ -310,7 +310,7 @@
## CHECK-NOT: NewInstance
## CHECK-DAG: <<Invoke1:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init>
## CHECK-DAG: <<Invoke2:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init>
-## CHECK-DAG: <<Phi:l\d+>> Phi [<<Invoke2>>,<<Invoke1>>]
+## CHECK-DAG: <<Phi:l\d+>> Phi [<<Invoke1>>,<<Invoke2>>]
## CHECK-DAG: Return [<<Phi>>]
.method public static loopAndStringInitAndPhi([BZ)Ljava/lang/String;
.registers 4
diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java
index 7e775b3..e2773c8 100644
--- a/test/563-checker-fakestring/src/Main.java
+++ b/test/563-checker-fakestring/src/Main.java
@@ -34,6 +34,11 @@
}
}
+ // Create an empty int[] to force loading the int[] class before compiling
+ // TestCase.deoptimizeNewInstance.
+ // This makes sure the compiler can properly type int[] and not bail.
+ static int[] emptyArray = new int[0];
+
public static void main(String[] args) throws Throwable {
System.loadLibrary(args[0]);
Class<?> c = Class.forName("TestCase");
@@ -160,10 +165,7 @@
}
}
- public static boolean doThrow = false;
-
public static Object $noinline$HiddenNull() {
- if (doThrow) { throw new Error(); }
return null;
}
}
diff --git a/test/564-checker-bitcount/src/Main.java b/test/564-checker-bitcount/src/Main.java
index e022d9d..460a31a 100644
--- a/test/564-checker-bitcount/src/Main.java
+++ b/test/564-checker-bitcount/src/Main.java
@@ -25,7 +25,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountBoolean(boolean x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x ? 1 : 0);
}
@@ -33,7 +32,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountByte(byte x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -41,7 +39,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountShort(short x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -49,7 +46,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountChar(char x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -57,7 +53,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountInt(int x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -65,7 +60,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountLong(long x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Long.bitCount(x);
}
@@ -201,6 +195,4 @@
throw new Error("Expected: " + expected + ", found: " + result);
}
}
-
- private static boolean doThrow = false;
}
diff --git a/test/564-checker-inline-loop/src/Main.java b/test/564-checker-inline-loop/src/Main.java
index 6929913..41eca35 100644
--- a/test/564-checker-inline-loop/src/Main.java
+++ b/test/564-checker-inline-loop/src/Main.java
@@ -21,9 +21,6 @@
/// CHECK-DAG: Return [<<Invoke>>]
/// CHECK-START: int Main.inlineLoop() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- /// CHECK-START: int Main.inlineLoop() inliner (after)
/// CHECK-DAG: <<Constant:i\d+>> IntConstant 42
/// CHECK-DAG: Return [<<Constant>>]
@@ -31,31 +28,31 @@
/// CHECK: Goto loop:{{B\d+}}
public static int inlineLoop() {
- return loopMethod();
+ return $inline$loopMethod();
}
/// CHECK-START: void Main.inlineWithinLoop() inliner (before)
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: void Main.inlineWithinLoop() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
/// CHECK-START: void Main.inlineWithinLoop() licm (after)
/// CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none
/// CHECK-DAG: Goto outer_loop:<<OuterLoop>>
public static void inlineWithinLoop() {
while (doLoop) {
- loopMethod();
+ $inline$loopMethod();
}
}
- public static int loopMethod() {
- while (doLoop) {}
+ public static int $inline$loopMethod() {
+ // We use `otherDoLoop` here so we don't propagate the knowledge that `doLoop` is true when
+ // inlining from `inlineWithinLoop`.
+ while (otherDoLoop) {}
return 42;
}
public static boolean doLoop = false;
+ public static boolean otherDoLoop = false;
public static void main(String[] args) {
inlineLoop();
diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java
index 17a8613..4abf66d 100644
--- a/test/565-checker-condition-liveness/src/Main.java
+++ b/test/565-checker-condition-liveness/src/Main.java
@@ -33,8 +33,8 @@
/// CHECK-START-{ARM,ARM64}: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[23,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25,33]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
@@ -43,10 +43,10 @@
/// CHECK-DAG: TryBoundary
/// CHECK-START-{ARM,ARM64}-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
- /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25]
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25,33]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
/// CHECK-DAG: ArrayLength liveness:22
@@ -56,8 +56,8 @@
// X86 and X86_64 generate at use site the ArrayLength, meaning only the BoundsCheck will have environment uses.
/// CHECK-START-{X86,X86_64}: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[25,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25,33]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25,25]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
@@ -66,10 +66,10 @@
/// CHECK-DAG: TryBoundary
/// CHECK-START-{X86,X86_64}-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
- /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25,25]
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25,25,33]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
/// CHECK-DAG: ArrayLength liveness:22
diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java
index e215ab0..7b44580 100644
--- a/test/566-checker-codegen-select/src/Main.java
+++ b/test/566-checker-codegen-select/src/Main.java
@@ -21,7 +21,6 @@
/// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
public long $noinline$longSelect(long param) {
- if (doThrow) { throw new Error(); }
long val_true = longB;
long val_false = longC;
return (param > longA) ? val_true : val_false;
@@ -46,7 +45,6 @@
/// CHECK: cmovle/ngq
public long $noinline$longSelect_Constant(long param) {
- if (doThrow) { throw new Error(); }
long val_true = longB;
long val_false = longC;
return (param > 3L) ? val_true : val_false;
@@ -60,7 +58,6 @@
/// CHECK: cmovl/nge
public int $noinline$intSelect_Constant(int param) {
- if (doThrow) { throw new Error(); }
int val_true = intB;
int val_false = intC;
return (param >= 3) ? val_true : val_false;
@@ -88,8 +85,6 @@
}
}
- public boolean doThrow = false;
-
public long longA = 3L;
public long longB = 5L;
public long longC = 7L;
diff --git a/test/566-polymorphic-inlining/run b/test/566-polymorphic-inlining/run
deleted file mode 100644
index 2919f46..0000000
--- a/test/566-polymorphic-inlining/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# -Xjitinitialsize:32M to prevent profiling info creation failure.
-exec ${RUN} \
- --runtime-option -Xjitinitialsize:32M \
- "${@}"
diff --git a/test/566-polymorphic-inlining/run.py b/test/566-polymorphic-inlining/run.py
new file mode 100644
index 0000000..0bfd5dc
--- /dev/null
+++ b/test/566-polymorphic-inlining/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # -Xjitinitialsize:32M to prevent profiling info creation failure.
+ ctx.default_run(args, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
index 595a056..691d48f 100644
--- a/test/566-polymorphic-inlining/src/Main.java
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -118,7 +118,6 @@
}
public static void $noinline$testInlineToSameTarget(Main m) {
- if (doThrow) throw new Error("");
m.increment();
}
@@ -137,7 +136,6 @@
counter++;
}
public static int counter = 0;
- public static boolean doThrow = false;
}
class Subclass extends Main {
diff --git a/test/567-checker-builder-intrinsics/src/TestFpAbs.java b/test/567-checker-builder-intrinsics/src/TestFpAbs.java
index e6c338d..f36432d 100644
--- a/test/567-checker-builder-intrinsics/src/TestFpAbs.java
+++ b/test/567-checker-builder-intrinsics/src/TestFpAbs.java
@@ -29,8 +29,6 @@
private static final int SPQUIET = 1 << 22;
private static final long DPQUIET = 1L << 51;
- public static boolean doThrow = false;
-
/// CHECK-START: float TestFpAbs.$opt$noinline$absSP(float) builder (after)
/// CHECK-DAG: <<Result:f\d+>> Abs
/// CHECK-DAG: Return [<<Result>>]
diff --git a/test/567-checker-builder-intrinsics/src/TestMinMax.java b/test/567-checker-builder-intrinsics/src/TestMinMax.java
index 0e88517..7207006 100644
--- a/test/567-checker-builder-intrinsics/src/TestMinMax.java
+++ b/test/567-checker-builder-intrinsics/src/TestMinMax.java
@@ -564,6 +564,16 @@
return x;
}
+ /// CHECK-START: int TestMinMax.minmax3(int) select_generator (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Par>>,<<M100>>]
+ /// CHECK-DAG: <<Sel1:i\d+>> Select [<<M100>>,<<Par>>,<<Cnd2>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<P100>>,<<Sel1>>,<<Cnd1>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
+
/// CHECK-START: int TestMinMax.minmax3(int) instruction_simplifier$after_gvn (after)
/// CHECK-DAG: <<Par:i\d+>> ParameterValue
/// CHECK-DAG: <<P100:i\d+>> IntConstant 100
@@ -578,6 +588,16 @@
return (x > 100) ? 100 : ((x < -100) ? -100 : x);
}
+ /// CHECK-START: int TestMinMax.minmax4(int) select_generator (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Cnd1:z\d+>> GreaterThanOrEqual [<<Par>>,<<M100>>]
+ /// CHECK-DAG: <<Cnd2:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Sel1:i\d+>> Select [<<P100>>,<<Par>>,<<Cnd2>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<M100>>,<<Sel1>>,<<Cnd1>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
+
/// CHECK-START: int TestMinMax.minmax4(int) instruction_simplifier$after_gvn (after)
/// CHECK-DAG: <<Par:i\d+>> ParameterValue
/// CHECK-DAG: <<P100:i\d+>> IntConstant 100
diff --git a/test/569-checker-pattern-replacement/run b/test/569-checker-pattern-replacement/run
deleted file mode 100755
index 8ab6527..0000000
--- a/test/569-checker-pattern-replacement/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-exec ${RUN} "$@" \
- -Xcompiler-option --no-inline-from="core-oj,569-checker-pattern-replacement.jar!classes2.dex"
diff --git a/test/569-checker-pattern-replacement/run.py b/test/569-checker-pattern-replacement/run.py
new file mode 100644
index 0000000..d763e54
--- /dev/null
+++ b/test/569-checker-pattern-replacement/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ Xcompiler_option=[
+ "--no-inline-from=core-oj,569-checker-pattern-replacement.jar!classes2.dex"
+ ])
diff --git a/test/570-checker-osr-locals/run b/test/570-checker-osr-locals/run
deleted file mode 100755
index 6cef13f..0000000
--- a/test/570-checker-osr-locals/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-# Ensure this test is not subject to code collection.
-exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M
diff --git a/test/570-checker-osr-locals/run.py b/test/570-checker-osr-locals/run.py
new file mode 100644
index 0000000..d876de2
--- /dev/null
+++ b/test/570-checker-osr-locals/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ # Ensure this test is not subject to code collection.
+ ctx.default_run(args, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/570-checker-osr/run b/test/570-checker-osr/run
deleted file mode 100755
index 24d69b4..0000000
--- a/test/570-checker-osr/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Ensure this test is not subject to code collection.
-exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M
diff --git a/test/570-checker-osr/run.py b/test/570-checker-osr/run.py
new file mode 100644
index 0000000..8109c62
--- /dev/null
+++ b/test/570-checker-osr/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Ensure this test is not subject to code collection.
+ ctx.default_run(args, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/570-checker-osr/smali/Osr.smali b/test/570-checker-osr/smali/Osr.smali
index 6592b7b..857cf64 100644
--- a/test/570-checker-osr/smali/Osr.smali
+++ b/test/570-checker-osr/smali/Osr.smali
@@ -19,7 +19,7 @@
# Check that blocks only havig nops are not merged when they are loop headers.
# This ensures we can do on-stack replacement for branches to those nop blocks.
-## CHECK-START: int Osr.simpleLoop(int, int) dead_code_elimination$final (after)
+## CHECK-START: int Osr.simpleLoop(int, int) dead_code_elimination$after_bce (after)
## CHECK-DAG: SuspendCheck loop:<<OuterLoop:B\d+>> outer_loop:none
## CHECK-DAG: SuspendCheck loop:{{B\d+}} outer_loop:<<OuterLoop>>
.method public static simpleLoop(II)I
diff --git a/test/574-irreducible-and-constant-area/run b/test/574-irreducible-and-constant-area/run
deleted file mode 100755
index ffdbcc9..0000000
--- a/test/574-irreducible-and-constant-area/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Don't do relocation, as this affects this test.
-exec ${RUN} "$@" --no-relocate
diff --git a/test/574-irreducible-and-constant-area/run.py b/test/574-irreducible-and-constant-area/run.py
new file mode 100644
index 0000000..3c04d9e
--- /dev/null
+++ b/test/574-irreducible-and-constant-area/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Don't do relocation, as this affects this test.
+ ctx.default_run(args, relocate=False)
diff --git a/test/578-polymorphic-inlining/src/Main.java b/test/578-polymorphic-inlining/src/Main.java
index 22d33d0..4798922 100644
--- a/test/578-polymorphic-inlining/src/Main.java
+++ b/test/578-polymorphic-inlining/src/Main.java
@@ -44,10 +44,7 @@
}
public static void $noinline$foo() {
- if (doThrow) throw new Error("");
}
-
- public static boolean doThrow;
}
class SubMain extends Main {
diff --git a/test/590-checker-arr-set-null-regression/src/Main.java b/test/590-checker-arr-set-null-regression/src/Main.java
index 792ee4e..ad47716 100644
--- a/test/590-checker-arr-set-null-regression/src/Main.java
+++ b/test/590-checker-arr-set-null-regression/src/Main.java
@@ -33,7 +33,7 @@
/// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>]
/// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>]
- /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true can_trigger_gc:true
/// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) instruction_simplifier (after)
/// CHECK-NOT: CheckCast
@@ -47,7 +47,7 @@
/// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>]
/// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>]
- /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true can_trigger_gc:true
/// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (after)
/// CHECK: <<Array:l\d+>> ParameterValue
@@ -55,7 +55,7 @@
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Class:l\d+>> LoadClass
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<Array>>]
- /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<Array>>,<<Index>>,<<Null>>] needs_type_check:false
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<Array>>,<<Index>>,<<Null>>] needs_type_check:false can_trigger_gc:false
static void testArraySetCheckCastNull(Element[] elements) {
Object object = null;
diff --git a/test/593-checker-long-2-float-regression/src/Main.java b/test/593-checker-long-2-float-regression/src/Main.java
index b31cbde..d55d0fd 100644
--- a/test/593-checker-long-2-float-regression/src/Main.java
+++ b/test/593-checker-long-2-float-regression/src/Main.java
@@ -16,7 +16,6 @@
public class Main {
- static boolean doThrow = false;
static long longValue;
public static void assertEquals(float expected, float result) {
@@ -35,10 +34,9 @@
/// CHECK-DAG: Return [<<Convert>>]
static float $noinline$longToFloat() {
- if (doThrow) { throw new Error(); }
longValue = $inline$returnConst();
// This call prevents D8 from replacing the result of the sget instruction
- // in line 43 by the result of the call to $inline$returnConst() in line 39.
+ // in line 41 by the result of the call to $inline$returnConst() in line 39.
$inline$preventRedundantFieldLoadEliminationInD8();
return (float) longValue;
}
diff --git a/test/594-load-string-regression/src/Main.java b/test/594-load-string-regression/src/Main.java
index 0b9f7b5..5903130 100644
--- a/test/594-load-string-regression/src/Main.java
+++ b/test/594-load-string-regression/src/Main.java
@@ -15,8 +15,6 @@
*/
public class Main {
- static boolean doThrow = false;
-
// Note: We're not doing checker tests as we cannot do them specifically for a non-PIC
// configuration. The check here would be "prepare_for_register_allocation (before)"
// CHECK: LoadClass
@@ -28,8 +26,6 @@
// CHECK-NEXT: NewInstance
// but the order of instructions for non-PIC mode is different.
public static int $noinline$test() {
- if (doThrow) { throw new Error(); }
-
int r = 0x12345678;
do {
// LICM pulls the LoadClass and ClinitCheck out of the loop, leaves NewInstance in the loop.
@@ -67,11 +63,7 @@
}
class Helper {
- static boolean doThrow = false;
-
public void $noinline$printString(String s) {
- if (doThrow) { throw new Error(); }
-
System.out.println("String: \"" + s + "\"");
}
}
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
deleted file mode 100644
index 851be09..0000000
--- a/test/595-profile-saving/run
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Use
-# --compiler-filter=quicken to make sure that the test is not compiled AOT
-# and to make sure the test is not compiled when loaded (by PathClassLoader)
-# -Xjitsaveprofilinginfo to enable profile saving
-# -Xusejit:false to disable jit and only test profiles.
-# -Xjitinitialsize:32M to prevent profiling info creation failure.
-exec ${RUN} \
- -Xcompiler-option --compiler-filter=quicken \
- --runtime-option '-Xcompiler-option --compiler-filter=quicken' \
- --runtime-option -Xjitinitialsize:32M \
- --runtime-option -Xjitsaveprofilinginfo \
- --runtime-option -Xusejit:false \
- --runtime-option -Xps-profile-boot-class-path \
- "${@}"
diff --git a/test/595-profile-saving/run.py b/test/595-profile-saving/run.py
new file mode 100644
index 0000000..96de281
--- /dev/null
+++ b/test/595-profile-saving/run.py
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Use
+ # --compiler-filter=quicken to make sure that the test is not compiled AOT
+ # and to make sure the test is not compiled when loaded (by PathClassLoader)
+ # -Xjitsaveprofilinginfo to enable profile saving
+ # -Xusejit:false to disable jit and only test profiles.
+ # -Xjitinitialsize:32M to prevent profiling info creation failure.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--compiler-filter=quicken"],
+ runtime_option=[
+ "-Xcompiler-option --compiler-filter=quicken",
+ "-Xjitinitialsize:32M",
+ "-Xjitsaveprofilinginfo",
+ "-Xusejit:false",
+ "-Xps-profile-boot-class-path",
+ ])
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
index 5b1a448..37a8e6c 100644
--- a/test/595-profile-saving/src/Main.java
+++ b/test/595-profile-saving/src/Main.java
@@ -39,8 +39,12 @@
File.class, Method.class);
testAddMethodToProfile(file, appMethod);
+ // Delete the file to check that the runtime can save the profile even if the file doesn't
+ // exist.
+ file.delete();
+
// Test that the profile saves a boot class path method with a profiling info.
- Method bootMethod = File.class.getDeclaredMethod("delete");
+ Method bootMethod = File.class.getDeclaredMethod("exists");
if (bootMethod.getDeclaringClass().getClassLoader() != Object.class.getClassLoader()) {
System.out.println("Class loader does not match boot class");
}
diff --git a/test/596-app-images/run b/test/596-app-images/run
deleted file mode 100644
index dbdcd1c..0000000
--- a/test/596-app-images/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# We need a profile to tell dex2oat to include classes in the final app image
-exec ${RUN} --profile $@
diff --git a/test/596-app-images/run.py b/test/596-app-images/run.py
new file mode 100644
index 0000000..402fbb5
--- /dev/null
+++ b/test/596-app-images/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ # We need a profile to tell dex2oat to include classes in the final app image
+ ctx.default_run(args, profile=True)
diff --git a/test/596-checker-dead-phi/smali/IrreducibleLoop.smali b/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
index bab2ba9..9f822bf 100644
--- a/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
+++ b/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
@@ -20,18 +20,19 @@
# not adjacent. This revealed a bug in our SSA builder, where a dead loop phi would
# be replaced by its incoming input during SsaRedundantPhiElimination.
-# Check that the outer loop suspend check environment only has the parameter vreg.
-## CHECK-START: int IrreducibleLoop.liveness(int) builder (after)
-## CHECK-DAG: <<Phi:i\d+>> Phi reg:4 loop:{{B\d+}} irreducible:false
-## CHECK-DAG: SuspendCheck env:[[_,_,_,_,<<Phi>>]] loop:{{B\d+}} irreducible:false
+# Check that the outer loop suspend check environment only has the two parameter vregs.
+## CHECK-START: int IrreducibleLoop.liveness(int, int) builder (after)
+## CHECK-DAG: <<Phi1:i\d+>> Phi reg:3 loop:{{B\d+}} irreducible:false
+## CHECK-DAG: <<Phi2:i\d+>> Phi reg:4 loop:{{B\d+}} irreducible:false
+## CHECK-DAG: SuspendCheck env:[[_,_,_,<<Phi1>>,<<Phi2>>]] loop:{{B\d+}} irreducible:false
# Check that the linear order has non-adjacent loop blocks.
-## CHECK-START: int IrreducibleLoop.liveness(int) liveness (after)
+## CHECK-START: int IrreducibleLoop.liveness(int, int) liveness (after)
## CHECK-DAG: Mul liveness:<<LPreEntry2:\d+>>
## CHECK-DAG: Add liveness:<<LBackEdge1:\d+>>
## CHECK-EVAL: <<LBackEdge1>> < <<LPreEntry2>>
-.method public static liveness(I)I
+.method public static liveness(II)I
.registers 5
const-string v1, "MyString"
@@ -50,8 +51,9 @@
if-ne v2, v3, :pre_header2
:pre_entry2
- # Add a marker on the irreducible loop entry.
- mul-int/2addr p0, p0
+ # Add a marker on the irreducible loop entry. Here we use p1 because p0 is a
+ # known constant and we eliminate the Mul otherwise.
+ mul-int/2addr p1, p1
goto :back_edge2
:back_edge2
@@ -61,8 +63,9 @@
if-eqz p0, :back_edge2
:back_edge1
- # Add a marker on the outer loop back edge.
- add-int/2addr p0, p0
+ # Add a marker on the outer loop back edge. Here we use p1 because p0 is a
+ # known constant and we eliminate the Add otherwise.
+ add-int/2addr p1, p1
# Set a wide register, to have v1 undefined at the back edge.
const-wide/16 v0, 0x1
goto :header1
diff --git a/test/596-checker-dead-phi/src/Main.java b/test/596-checker-dead-phi/src/Main.java
index f3a55df..ec2fbe5 100644
--- a/test/596-checker-dead-phi/src/Main.java
+++ b/test/596-checker-dead-phi/src/Main.java
@@ -17,13 +17,13 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("IrreducibleLoop");
- // Note that we don't actually enter the loops in the 'liveness'
- // method, so this is just a verification that that part of the code we
- // generated for that method is correct.
- Method m = c.getMethod("liveness", int.class);
- Object[] arguments = { 42 };
- System.out.println(m.invoke(null, arguments));
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ // Note that we don't actually enter the loops in the 'liveness'
+ // method, so this is just a verification that that part of the code we
+ // generated for that method is correct.
+ Method m = c.getMethod("liveness", int.class, int.class);
+ Object[] arguments = {42, 12};
+ System.out.println(m.invoke(null, arguments));
+ }
}
diff --git a/test/597-app-images-same-classloader/run b/test/597-app-images-same-classloader/run
deleted file mode 100644
index 496273f..0000000
--- a/test/597-app-images-same-classloader/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# We need a profile to tell dex2oat to include classes in the final app image
-exec ${RUN} --profile --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]" $@
diff --git a/test/597-app-images-same-classloader/run.py b/test/597-app-images-same-classloader/run.py
new file mode 100644
index 0000000..a335777
--- /dev/null
+++ b/test/597-app-images-same-classloader/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ # We need a profile to tell dex2oat to include classes in the final app image
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(args, profile=True, secondary_class_loader_context=pcl)
diff --git a/test/597-deopt-busy-loop/run b/test/597-deopt-busy-loop/run
deleted file mode 100644
index bc04498..0000000
--- a/test/597-deopt-busy-loop/run
+++ /dev/null
@@ -1,18 +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.
-
-# We want to run in debuggable mode and compiled.
-exec ${RUN} --jit -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-busy-loop/run.py b/test/597-deopt-busy-loop/run.py
new file mode 100644
index 0000000..6107702
--- /dev/null
+++ b/test/597-deopt-busy-loop/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # We want to run in debuggable mode and compiled.
+ ctx.default_run(args, jit=True, Xcompiler_option=["--debuggable"])
diff --git a/test/597-deopt-busy-loop/src/FloatLoop.java b/test/597-deopt-busy-loop/src/FloatLoop.java
index 57667a6..7aadce4 100644
--- a/test/597-deopt-busy-loop/src/FloatLoop.java
+++ b/test/597-deopt-busy-loop/src/FloatLoop.java
@@ -51,6 +51,10 @@
}
}
+ // Create an empty int[] to force loading the int[] class before compiling $noinline$busyLoop.
+ // This makes sure the compiler can properly type int[] and not bail.
+ static int[] emptyArray = new int[0];
+
public void $noinline$busyLoop() {
Main.assertIsManaged();
diff --git a/test/597-deopt-invoke-stub/run b/test/597-deopt-invoke-stub/run
deleted file mode 100644
index 990c30e..0000000
--- a/test/597-deopt-invoke-stub/run
+++ /dev/null
@@ -1,21 +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.
-
-# In order to test deoptimizing at quick-to-interpreter bridge,
-# we want to run in debuggable mode with jit compilation.
-# We also bump up the jit threshold to 10000 to make sure that the method
-# that should be interpreted is not compiled.
-exec ${RUN} "${@}" --jit --runtime-option -Xjitthreshold:10000 -Xcompiler-option --debuggable
diff --git a/test/597-deopt-invoke-stub/run.py b/test/597-deopt-invoke-stub/run.py
new file mode 100644
index 0000000..b52c177
--- /dev/null
+++ b/test/597-deopt-invoke-stub/run.py
@@ -0,0 +1,27 @@
+#!/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.
+
+
+def run(ctx, args):
+ # In order to test deoptimizing at quick-to-interpreter bridge,
+ # we want to run in debuggable mode with jit compilation.
+ # We also bump up the jit threshold to 10000 to make sure that the method
+ # that should be interpreted is not compiled.
+ ctx.default_run(
+ args,
+ jit=True,
+ runtime_option=["-Xjitthreshold:10000"],
+ Xcompiler_option=["--debuggable"])
diff --git a/test/597-deopt-new-string/run b/test/597-deopt-new-string/run
deleted file mode 100644
index 9776ab3..0000000
--- a/test/597-deopt-new-string/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# We want to run in debuggable mode which keeps the call into StringFactory.newEmptyString().
-exec ${RUN} -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-new-string/run.py b/test/597-deopt-new-string/run.py
new file mode 100644
index 0000000..fa958dc
--- /dev/null
+++ b/test/597-deopt-new-string/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # We want to run in debuggable mode which keeps the call into StringFactory.newEmptyString().
+ ctx.default_run(args, Xcompiler_option=["--debuggable"])
diff --git a/test/598-checker-irreducible-dominance/src/Main.java b/test/598-checker-irreducible-dominance/src/Main.java
index 4b1f490..cd38d3e 100644
--- a/test/598-checker-irreducible-dominance/src/Main.java
+++ b/test/598-checker-irreducible-dominance/src/Main.java
@@ -15,8 +15,8 @@
*/
public class Main {
- public static void main(String[] args) {
- // Nothing to run. This regression test merely makes sure the smali test
- // case successfully compiles.
- }
+ public static void main(String[] args) {
+ // Nothing to run. This regression test merely makes sure the smali test
+ // case successfully compiles.
+ }
}
diff --git a/test/599-checker-irreducible-loop/src/Main.java b/test/599-checker-irreducible-loop/src/Main.java
index 643e3a1..8a428a0 100644
--- a/test/599-checker-irreducible-loop/src/Main.java
+++ b/test/599-checker-irreducible-loop/src/Main.java
@@ -17,11 +17,11 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("IrreducibleLoop");
- Method m = c.getMethod("test", int.class);
- Object[] arguments = { 42 };
- // Invoke the code just for validation purposes.
- System.out.println(m.invoke(null, arguments));
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ Method m = c.getMethod("test", int.class);
+ Object[] arguments = { 42 };
+ // Invoke the code just for validation purposes.
+ System.out.println(m.invoke(null, arguments));
+ }
}
diff --git a/test/612-jit-dex-cache/build.py b/test/612-jit-dex-cache/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/612-jit-dex-cache/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/612-jit-dex-cache/test-metadata.json b/test/612-jit-dex-cache/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/612-jit-dex-cache/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/613-inlining-dex-cache/build.py b/test/613-inlining-dex-cache/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/613-inlining-dex-cache/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/613-inlining-dex-cache/run b/test/613-inlining-dex-cache/run
deleted file mode 100644
index 9c1e7aa..0000000
--- a/test/613-inlining-dex-cache/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-flags="$@"
-# We need the dex files pre-verified to avoid running the verifier
-# at runtime which will update the dex cache.
-exec ${RUN} ${flags/verify-at-runtime/interpret-only}
diff --git a/test/613-inlining-dex-cache/run.py b/test/613-inlining-dex-cache/run.py
new file mode 100644
index 0000000..f0b061b
--- /dev/null
+++ b/test/613-inlining-dex-cache/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
diff --git a/test/613-inlining-dex-cache/test-metadata.json b/test/613-inlining-dex-cache/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/613-inlining-dex-cache/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/616-cha-abstract/run b/test/616-cha-abstract/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-abstract/run
+++ /dev/null
@@ -1,18 +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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-abstract/run.py b/test/616-cha-abstract/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-abstract/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java
index c8093e6..1a06337 100644
--- a/test/616-cha-abstract/src/Main.java
+++ b/test/616-cha-abstract/src/Main.java
@@ -15,145 +15,145 @@
*/
abstract class Base {
- abstract void foo(int i);
+ abstract void foo(int i);
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main1 extends Base {
- void foo(int i) {
- if (i != 1) {
- printError("error1");
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
}
class Main2 extends Main1 {
- void foo(int i) {
- if (i != 2) {
- printError("error2");
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
+ static Base sMain1;
+ static Base sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
+ }
+
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
- if (isInterpreted()) {
- sIsOptimizing = false;
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ testOverride(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testOverride(false, true, false);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride(true, false, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", false);
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- ensureJitCompiled(Main.class, "testOverride");
- testOverride(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
- }
-
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testOverride(false, true, false);
- }
- }.start();
-
- // This will create Main2 instance in the middle of testOverride().
- testOverride(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", false);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-interface-default/build b/test/616-cha-interface-default/build
deleted file mode 100644
index d9654f8..0000000
--- a/test/616-cha-interface-default/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-build "$@" --experimental default-methods
diff --git a/test/616-cha-interface-default/build.py b/test/616-cha-interface-default/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/616-cha-interface-default/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/616-cha-interface-default/run b/test/616-cha-interface-default/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-interface-default/run
+++ /dev/null
@@ -1,18 +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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-interface-default/run.py b/test/616-cha-interface-default/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-interface-default/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-interface-default/src-multidex/Base.java b/test/616-cha-interface-default/src-multidex/Base.java
index 2cbcb50..e3222a1 100644
--- a/test/616-cha-interface-default/src-multidex/Base.java
+++ b/test/616-cha-interface-default/src-multidex/Base.java
@@ -15,27 +15,27 @@
*/
interface Base {
- default public int foo(int i) {
- if (i != 1) {
- return -2;
+ default public int foo(int i) {
+ if (i != 1) {
+ return -2;
+ }
+ return i + 10;
}
- return i + 10;
- }
- // Test default method that's not inlined.
- default public int $noinline$bar() {
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- return -1;
- }
+ // Test default method that's not inlined.
+ default public int $noinline$bar() {
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ return -1;
+ }
- default void printError(String msg) {
- System.out.println(msg);
- }
+ default void printError(String msg) {
+ System.out.println(msg);
+ }
}
diff --git a/test/616-cha-interface-default/src/Main.java b/test/616-cha-interface-default/src/Main.java
index ce02cf0..40b815a 100644
--- a/test/616-cha-interface-default/src/Main.java
+++ b/test/616-cha-interface-default/src/Main.java
@@ -18,159 +18,159 @@
}
class Main2 extends Main1 {
- public void foobar() {}
+ public void foobar() {}
}
class Main3 implements Base {
- public int foo(int i) {
- if (i != 3) {
- printError("error3");
+ public int foo(int i) {
+ if (i != 3) {
+ printError("error3");
+ }
+ return -(i + 10);
}
- return -(i + 10);
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
- static Base sMain3;
+ static Base sMain1;
+ static Base sMain2;
+ static Base sMain3;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- static int getValue(Class<?> cls) {
- if (cls == Main1.class || cls == Main2.class) {
- return 1;
- }
- return 3;
- }
-
- // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Base.foo() and be inlined.
- // After Helper.createMain3() which links in Main3, live testImplement() on stack
- // should be deoptimized.
- static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain3 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
- System.out.println("11 expected.");
- }
- if (sMain1.$noinline$bar() != -1) {
- System.out.println("-1 expected.");
- }
- if (sMain2.foo(getValue(sMain2.getClass())) != 11) {
- System.out.println("11 expected.");
- }
-
- if (createMain3) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain3 = Helper.createMain3();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main3 is linked by
- // calling Helper.createMain3(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
- System.out.println("11 expected.");
- }
- if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main3 is created.
- assertIsInterpreted();
+ static int getValue(Class<?> cls) {
+ if (cls == Main1.class || cls == Main2.class) {
+ return 1;
+ }
+ return 3;
}
- if (sMain3 != null) {
- if (sMain3.foo(getValue(sMain3.getClass())) != -13) {
- System.out.println("-13 expected.");
- }
- }
- }
+ // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Base.foo() and be inlined.
+ // After Helper.createMain3() which links in Main3, live testImplement() on stack
+ // should be deoptimized.
+ static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
+ }
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that implements a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ if (createMain3 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
- if (isInterpreted()) {
- sIsOptimizing = false;
+ if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
+ System.out.println("11 expected.");
+ }
+ if (sMain1.$noinline$bar() != -1) {
+ System.out.println("-1 expected.");
+ }
+ if (sMain2.foo(getValue(sMain2.getClass())) != 11) {
+ System.out.println("11 expected.");
+ }
+
+ if (createMain3) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain3 = Helper.createMain3();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main3 is linked by
+ // calling Helper.createMain3(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
+ System.out.println("11 expected.");
+ }
+ if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main3 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain3 != null) {
+ if (sMain3.foo(getValue(sMain3.getClass())) != -13) {
+ System.out.println("-13 expected.");
+ }
+ }
}
- // sMain1 is an instance of Main1.
- // sMain2 is an instance of Main2.
- // Neither Main1 nor Main2 override default method Base.foo().
- // Main3 hasn't bee loaded yet.
- sMain1 = new Main1();
- sMain2 = new Main2();
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that implements a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- ensureJitCompiled(Main.class, "testImplement");
- testImplement(false, false, true);
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main3 is verified ahead-of-time so it's linked in already.
+ // sMain1 is an instance of Main1.
+ // sMain2 is an instance of Main2.
+ // Neither Main1 nor Main2 override default method Base.foo().
+ // Main3 hasn't bee loaded yet.
+ sMain1 = new Main1();
+ sMain2 = new Main2();
+
+ ensureJitCompiled(Main.class, "testImplement");
+ testImplement(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main3 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testImplement(false, true, false);
+ }
+ }.start();
+
+ // This will create Main3 instance in the middle of testImplement().
+ testImplement(true, false, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", true);
+ assertSingleImplementation(sMain3.getClass(), "foo", true);
}
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testImplement(false, true, false);
- }
- }.start();
-
- // This will create Main3 instance in the middle of testImplement().
- testImplement(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", true);
- assertSingleImplementation(sMain3.getClass(), "foo", true);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain3() in another class to avoid class loading due to verifier.
class Helper {
- static Base createMain3() {
- return new Main3();
- }
+ static Base createMain3() {
+ return new Main3();
+ }
}
diff --git a/test/616-cha-interface/run b/test/616-cha-interface/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-interface/run
+++ /dev/null
@@ -1,18 +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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-interface/run.py b/test/616-cha-interface/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-interface/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-interface/src/Main.java b/test/616-cha-interface/src/Main.java
index c55ed6e..0c21ea0 100644
--- a/test/616-cha-interface/src/Main.java
+++ b/test/616-cha-interface/src/Main.java
@@ -15,159 +15,159 @@
*/
interface Base {
- void foo(int i);
- void $noinline$bar();
+ void foo(int i);
+ void $noinline$bar();
}
class Main1 implements Base {
- public void foo(int i) {
- if (i != 1) {
- printError("error1");
+ public void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
- // Test rewriting invoke-interface into invoke-virtual when inlining fails.
- public void $noinline$bar() {
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- }
+ // Test rewriting invoke-interface into invoke-virtual when inlining fails.
+ public void $noinline$bar() {
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ }
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main2 extends Main1 {
- public void foo(int i) {
- if (i != 2) {
- printError("error2");
+ public void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
+ static Base sMain1;
+ static Base sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testImplement() on stack
- // should be deoptimized.
- static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- sMain1.$noinline$bar();
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testImplement() on stack
+ // should be deoptimized.
+ static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
+ }
+
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ sMain1.$noinline$bar();
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
- if (isInterpreted()) {
- sIsOptimizing = false;
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testImplement");
+ testImplement(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testImplement(false, true, false);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testImplement().
+ testImplement(true, false, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", false);
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- ensureJitCompiled(Main.class, "testImplement");
- testImplement(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
- }
-
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testImplement(false, true, false);
- }
- }.start();
-
- // This will create Main2 instance in the middle of testImplement().
- testImplement(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", false);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-miranda/run b/test/616-cha-miranda/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-miranda/run
+++ /dev/null
@@ -1,18 +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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-miranda/run.py b/test/616-cha-miranda/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-miranda/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-miranda/src/Main.java b/test/616-cha-miranda/src/Main.java
index 323e92e..eede844 100644
--- a/test/616-cha-miranda/src/Main.java
+++ b/test/616-cha-miranda/src/Main.java
@@ -15,149 +15,142 @@
*/
interface Iface {
- public void foo(int i);
+ public void foo(int i);
}
abstract class Base implements Iface {
- // Iface.foo(int) will be added as a miranda method.
+ // Iface.foo(int) will be added as a miranda method.
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main1 extends Base {
- public void foo(int i) {
- if (i != 1) {
- printError("error1");
+ public void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
}
class Main2 extends Main1 {
- public void foo(int i) {
- if (i != 2) {
- printError("error2");
+ public void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
+ static Base sMain1;
+ static Base sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride(boolean createMain2, boolean wait) {
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ sIsOptimizing = isAotCompiled(Main.class, "testOverride");
+ sHasJIT = hasJit();
- if (isInterpreted()) {
- sIsOptimizing = false;
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testOverride");
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testOverride(false, true);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride(true, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", false);
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- ensureJitCompiled(Main.class, "testOverride");
- testOverride(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
- }
-
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testOverride(false, true, false);
- }
- }.start();
-
- // This will create Main2 instance in the middle of testOverride().
- testOverride(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", false);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native boolean hasJit();
+ private native static boolean isAotCompiled(Class<?> cls, String methodName);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-native/src/Main.java b/test/616-cha-native/src/Main.java
index 53a463c..145cc42 100644
--- a/test/616-cha-native/src/Main.java
+++ b/test/616-cha-native/src/Main.java
@@ -15,19 +15,19 @@
*/
abstract class A {
- public abstract void foo();
+ public abstract void foo();
}
class B extends A {
- public native void foo();
+ public native void foo();
}
class C extends B {
- public void foo() {}
+ public void foo() {}
}
public class Main {
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- }
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ }
}
diff --git a/test/616-cha-proxy-method-inline/run b/test/616-cha-proxy-method-inline/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-proxy-method-inline/run
+++ /dev/null
@@ -1,18 +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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-proxy-method-inline/run.py b/test/616-cha-proxy-method-inline/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-proxy-method-inline/src-multidex/Foo.java b/test/616-cha-proxy-method-inline/src-multidex/Foo.java
index 9deca3e..135214d 100644
--- a/test/616-cha-proxy-method-inline/src-multidex/Foo.java
+++ b/test/616-cha-proxy-method-inline/src-multidex/Foo.java
@@ -15,5 +15,5 @@
*/
interface Foo {
- public Object bar(Object obj);
+ public Object bar(Object obj);
}
diff --git a/test/616-cha-proxy-method-inline/src/Main.java b/test/616-cha-proxy-method-inline/src/Main.java
index be7bc82..627c15c 100644
--- a/test/616-cha-proxy-method-inline/src/Main.java
+++ b/test/616-cha-proxy-method-inline/src/Main.java
@@ -18,53 +18,51 @@
import java.lang.reflect.InvocationTargetException;
class DebugProxy implements java.lang.reflect.InvocationHandler {
- private Object obj;
- static Class<?>[] interfaces = {Foo.class};
+ private Object obj;
+ static Class<?>[] interfaces = {Foo.class};
- public static Object newInstance(Object obj) {
- return java.lang.reflect.Proxy.newProxyInstance(
- Foo.class.getClassLoader(),
- interfaces,
- new DebugProxy(obj));
- }
-
- private DebugProxy(Object obj) {
- this.obj = obj;
- }
-
- public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
- Object result;
- if (obj == null) {
- return null;
+ public static Object newInstance(Object obj) {
+ return java.lang.reflect.Proxy.newProxyInstance(
+ Foo.class.getClassLoader(), interfaces, new DebugProxy(obj));
}
- try {
- System.out.println("before invoking method " + m.getName());
- result = m.invoke(obj, args);
- } catch (InvocationTargetException e) {
- throw e.getTargetException();
- } catch (Exception e) {
- throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
- } finally {
- System.out.println("after invoking method " + m.getName());
+
+ private DebugProxy(Object obj) {
+ this.obj = obj;
}
- return result;
- }
+
+ public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
+ Object result;
+ if (obj == null) {
+ return null;
+ }
+ try {
+ System.out.println("before invoking method " + m.getName());
+ result = m.invoke(obj, args);
+ } catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ } catch (Exception e) {
+ throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
+ } finally {
+ System.out.println("after invoking method " + m.getName());
+ }
+ return result;
+ }
}
public class Main {
- public static void call(Foo foo) {
- if (foo == null) {
- return;
+ public static void call(Foo foo) {
+ if (foo == null) {
+ return;
+ }
+ foo.bar(null);
}
- foo.bar(null);
- }
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- Foo foo = (Foo)DebugProxy.newInstance(null);
- ensureJitCompiled(Main.class, "call");
- call(foo);
- }
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ Foo foo = (Foo)DebugProxy.newInstance(null);
+ ensureJitCompiled(Main.class, "call");
+ call(foo);
+ }
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
}
diff --git a/test/616-cha-regression-proxy-method/src/Main.java b/test/616-cha-regression-proxy-method/src/Main.java
index 176f80b..14bc7a3 100644
--- a/test/616-cha-regression-proxy-method/src/Main.java
+++ b/test/616-cha-regression-proxy-method/src/Main.java
@@ -19,113 +19,113 @@
import java.lang.reflect.Proxy;
class Main1 {
- void foo(int i) {
- if (i != 1) {
- printError("error1");
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main2 extends Main1 {
- void foo(int i) {
- if (i != 2) {
- printError("error2");
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
class Proxied implements Runnable {
- public void run() {
- synchronized(Main.class) {
- Main.sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
- }
+ public void run() {
+ synchronized(Main.class) {
+ Main.sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
}
- }
}
class MyInvocationHandler implements InvocationHandler {
- private final Proxied proxied;
+ private final Proxied proxied;
- public MyInvocationHandler(Proxied proxied) {
- this.proxied = proxied;
- }
+ public MyInvocationHandler(Proxied proxied) {
+ this.proxied = proxied;
+ }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- return method.invoke(proxied, args);
- }
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return method.invoke(proxied, args);
+ }
}
public class Main {
- static Main1 sMain1;
- static Main1 sMain2;
- static volatile boolean sOtherThreadStarted;
+ static Main1 sMain1;
+ static Main1 sMain2;
+ static volatile boolean sOtherThreadStarted;
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride() {
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride() {
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ // Also create a proxy method such that a proxy method's frame is visited
+ // during stack walking.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ // Create another thread that calls a proxy method.
+ new Thread() {
+ public void run() {
+ Runnable proxy = (Runnable)Proxy.newProxyInstance(
+ Proxied.class.getClassLoader(),
+ new Class[] { Runnable.class },
+ new MyInvocationHandler(new Proxied()));
+ proxy.run();
+ }
+ }.start();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride();
}
- }
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- // Also create a proxy method such that a proxy method's frame is visited
- // during stack walking.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- // Create another thread that calls a proxy method.
- new Thread() {
- public void run() {
- Runnable proxy = (Runnable)Proxy.newProxyInstance(
- Proxied.class.getClassLoader(),
- new Class[] { Runnable.class },
- new MyInvocationHandler(new Proxied()));
- proxy.run();
- }
- }.start();
-
- ensureJitCompiled(Main.class, "testOverride");
- // This will create Main2 instance in the middle of testOverride().
- testOverride();
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-unloading/run b/test/616-cha-unloading/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-unloading/run
+++ /dev/null
@@ -1,18 +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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-unloading/run.py b/test/616-cha-unloading/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-unloading/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha/run b/test/616-cha/run
deleted file mode 100644
index 9c64c7d..0000000
--- a/test/616-cha/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha/run.py b/test/616-cha/run.py
new file mode 100644
index 0000000..38164a3
--- /dev/null
+++ b/test/616-cha/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index 39f47fa..d2d4bcf 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -15,239 +15,239 @@
*/
class Main1 {
- String getName() {
- return "Main1";
- }
-
- void printError(String msg) {
- System.out.println(msg);
- }
-
- void foo(int i) {
- if (i != 1) {
- printError("error1");
+ String getName() {
+ return "Main1";
}
- }
- int getValue1() {
- return 1;
- }
- int getValue2() {
- return 2;
- }
- int getValue3() {
- return 3;
- }
- int getValue4() {
- return 4;
- }
- int getValue5() {
- return 5;
- }
- int getValue6() {
- return 6;
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
+
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
+ }
+
+ int getValue1() {
+ return 1;
+ }
+ int getValue2() {
+ return 2;
+ }
+ int getValue3() {
+ return 3;
+ }
+ int getValue4() {
+ return 4;
+ }
+ int getValue5() {
+ return 5;
+ }
+ int getValue6() {
+ return 6;
+ }
}
class Main2 extends Main1 {
- String getName() {
- return "Main2";
- }
-
- void foo(int i) {
- if (i != 2) {
- printError("error2");
+ String getName() {
+ return "Main2";
}
- }
+
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
+ }
}
class Main3 extends Main1 {
- String getName() {
- return "Main3";
- }
+ String getName() {
+ return "Main3";
+ }
}
public class Main {
- static Main1 sMain1;
- static Main1 sMain2;
+ static Main1 sMain1;
+ static Main1 sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
}
- }
+
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ static Main1[] sArray;
+
+ static long calcValue(Main1 m) {
+ return m.getValue1()
+ + m.getValue2() * 2
+ + m.getValue3() * 3
+ + m.getValue4() * 4
+ + m.getValue5() * 5
+ + m.getValue6() * 6;
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
-
- static Main1[] sArray;
-
- static long calcValue(Main1 m) {
- return m.getValue1()
- + m.getValue2() * 2
- + m.getValue3() * 3
- + m.getValue4() * 4
- + m.getValue5() * 5
- + m.getValue6() * 6;
- }
-
- static long testNoOverrideLoop(int count) {
- long sum = 0;
- for (int i=0; i<count; i++) {
- sum += calcValue(sArray[0]);
- sum += calcValue(sArray[1]);
- sum += calcValue(sArray[2]);
- }
- return sum;
- }
-
- static void testNoOverride() {
- sArray = new Main1[3];
- sArray[0] = new Main1();
- sArray[1] = Helper.createMain2();
- sArray[2] = Helper.createMain3();
- long sum = 0;
- // Loop enough to get methods JITed.
- for (int i=0; i<100; i++) {
- testNoOverrideLoop(1);
- }
- ensureJitCompiled(Main.class, "testNoOverrideLoop");
- ensureJitCompiled(Main.class, "calcValue");
-
- long t1 = System.currentTimeMillis();
- sum = testNoOverrideLoop(100000);
- long t2 = System.currentTimeMillis();
- if (sum != 27300000L) {
- System.out.println("Unexpected result.");
- }
- }
-
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- // Also test pure non-overriding case, which is more for checking generated
- // code form.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
-
- // CHeck some boot-image methods.
-
- // We would want to have this, but currently setting single-implementation in the boot image
- // does not work well with app images. b/34193647
- final boolean ARRAYLIST_SIZE_EXPECTED = false;
- assertSingleImplementation(java.util.ArrayList.class, "size", ARRAYLIST_SIZE_EXPECTED);
-
- // java.util.LinkedHashMap overrides get().
- assertSingleImplementation(java.util.HashMap.class, "get", false);
-
- // We don't set single-implementation modifier bit for final classes or methods
- // since we can devirtualize without CHA for those cases. However hasSingleImplementation()
- // should return true for those cases.
- assertSingleImplementation(java.lang.String.class, "charAt", true);
- assertSingleImplementation(java.lang.Thread.class, "join", true);
-
- if (isInterpreted()) {
- sIsOptimizing = false;
+ static long testNoOverrideLoop(int count) {
+ long sum = 0;
+ for (int i=0; i<count; i++) {
+ sum += calcValue(sArray[0]);
+ sum += calcValue(sArray[1]);
+ sum += calcValue(sArray[2]);
+ }
+ return sum;
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
+ static void testNoOverride() {
+ sArray = new Main1[3];
+ sArray[0] = new Main1();
+ sArray[1] = Helper.createMain2();
+ sArray[2] = Helper.createMain3();
+ long sum = 0;
+ // Loop enough to get methods JITed.
+ for (int i=0; i<100; i++) {
+ testNoOverrideLoop(1);
+ }
+ ensureJitCompiled(Main.class, "testNoOverrideLoop");
+ ensureJitCompiled(Main.class, "calcValue");
- ensureJitCompiled(Main.class, "testOverride");
- testOverride(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
+ long t1 = System.currentTimeMillis();
+ sum = testNoOverrideLoop(100000);
+ long t2 = System.currentTimeMillis();
+ if (sum != 27300000L) {
+ System.out.println("Unexpected result.");
+ }
}
- assertSingleImplementation(Main1.class, "getValue1", true);
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testOverride(false, true, false);
- }
- }.start();
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
+ }
+ }
- // This will create Main2 instance in the middle of testOverride().
- testOverride(true, false, false);
- assertSingleImplementation(Main1.class, "foo", false);
- assertSingleImplementation(Main1.class, "getValue1", true);
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ // Also test pure non-overriding case, which is more for checking generated
+ // code form.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- testNoOverride();
- }
+ // CHeck some boot-image methods.
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ // We would want to have this, but currently setting single-implementation in the boot image
+ // does not work well with app images. b/34193647
+ final boolean ARRAYLIST_SIZE_EXPECTED = false;
+ assertSingleImplementation(java.util.ArrayList.class, "size", ARRAYLIST_SIZE_EXPECTED);
+
+ // java.util.LinkedHashMap overrides get().
+ assertSingleImplementation(java.util.HashMap.class, "get", false);
+
+ // We don't set single-implementation modifier bit for final classes or methods
+ // since we can devirtualize without CHA for those cases. However hasSingleImplementation()
+ // should return true for those cases.
+ assertSingleImplementation(java.lang.String.class, "charAt", true);
+ assertSingleImplementation(java.lang.Thread.class, "join", true);
+
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
+
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ testOverride(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+ assertSingleImplementation(Main1.class, "getValue1", true);
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testOverride(false, true, false);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride(true, false, false);
+ assertSingleImplementation(Main1.class, "foo", false);
+ assertSingleImplementation(Main1.class, "getValue1", true);
+
+ testNoOverride();
+ }
+
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
- static Main1 createMain3() {
- return new Main3();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
+ static Main1 createMain3() {
+ return new Main3();
+ }
}
diff --git a/test/617-clinit-oome/src/Main.java b/test/617-clinit-oome/src/Main.java
index bab344f..005f0df 100644
--- a/test/617-clinit-oome/src/Main.java
+++ b/test/617-clinit-oome/src/Main.java
@@ -15,43 +15,43 @@
*/
public class Main {
- private static int exhaustJavaHeap(Object[] data, int index, int size) {
- Runtime.getRuntime().gc();
- while (size > 0) {
+ private static int exhaustJavaHeap(Object[] data, int index, int size) {
+ Runtime.getRuntime().gc();
+ while (size > 0) {
+ try {
+ data[index] = new byte[size];
+ index++;
+ } catch (OutOfMemoryError e) {
+ size /= 2;
+ }
+ }
+ return index;
+ }
+
+ public static void main(String[] args) {
+ Class klass = Other.class;
+ Object[] data = new Object[100000];
try {
- data[index] = new byte[size];
- index++;
+ System.out.println("Filling heap");
+
+ // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
+ // OOME to prevent GC thrashing, even if later allocations may succeed.
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
+
+ int index = 0;
+ int initial_size = 256 * 1024 * 1024;
+ // Repeat to ensure there is no space left on the heap.
+ index = exhaustJavaHeap(data, index, initial_size);
+ index = exhaustJavaHeap(data, index, /*size*/ 4);
+ index = exhaustJavaHeap(data, index, /*size*/ 4);
+
+ // Initialize now that the heap is full.
+ Other.print();
} catch (OutOfMemoryError e) {
- size /= 2;
+ } catch (Exception e) {
+ System.out.println(e);
}
}
- return index;
- }
-
- public static void main(String[] args) {
- Class klass = Other.class;
- Object[] data = new Object[100000];
- try {
- System.out.println("Filling heap");
-
- // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
- // OOME to prevent GC thrashing, even if later allocations may succeed.
- Runtime.getRuntime().gc();
- System.runFinalization();
- // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
-
- int index = 0;
- int initial_size = 256 * 1024 * 1024;
- // Repeat to ensure there is no space left on the heap.
- index = exhaustJavaHeap(data, index, initial_size);
- index = exhaustJavaHeap(data, index, /*size*/ 4);
- index = exhaustJavaHeap(data, index, /*size*/ 4);
-
- // Initialize now that the heap is full.
- Other.print();
- } catch (OutOfMemoryError e) {
- } catch (Exception e) {
- System.out.println(e);
- }
- }
}
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index dd76e41..d15de18 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -19,930 +19,1027 @@
*/
public class Main {
- static int[] a = new int[10];
+ static int[] a = new int[10];
- static int[] novec = new int[20]; // to prevent vectorization
+ static int[] novec = new int[20]; // to prevent vectorization
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadSingleLoop() {
- for (int i = 0; i < 4; i++) {
- }
- }
-
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadSingleLoopN(int n) {
- for (int i = 0; i < n; i++) {
- }
- }
-
- /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (after)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- static void potentialInfiniteLoop(int n) {
- for (int i = 0; i <= n; i++) { // loops forever when n = MAX_INT
- }
- }
-
- /// CHECK-START: void Main.deadNestedLoops() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop>>
- //
- /// CHECK-START: void Main.deadNestedLoops() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadNestedLoops() {
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- }
- }
- }
-
- /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
- /// CHECK-DAG: Phi loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop3>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadNestedAndFollowingLoops() {
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- for (int k = 0; k < 4; k++) {
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadSingleLoop() {
+ for (int i = 0; i < 4; i++) {
}
- for (int k = 0; k < 4; k++) {
+ }
+
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadSingleLoopN(int n) {
+ for (int i = 0; i < n; i++) {
}
- }
- for (int j = 0; j < 4; j++) {
- for (int k = 0; k < 4; k++) {
+ }
+
+ /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ static void potentialInfiniteLoop(int n) {
+ for (int i = 0; i <= n; i++) { // loops forever when n = MAX_INT
}
- }
- }
- for (int i = 0; i < 4; i++) {
- }
- }
-
- /// CHECK-START: void Main.deadConditional(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadConditional(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- public static void deadConditional(int n) {
- int k = 0;
- int m = 0;
- for (int i = 0; i < n; i++) {
- if (i == 3)
- k = i;
- else
- m = i;
- }
- }
-
- /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- public static void deadConditionalCycle(int n) {
- int k = 0;
- int m = 0;
- for (int i = 0; i < n; i++) {
- if (i == 3)
- k--;
- else
- m++;
- }
- }
-
-
- /// CHECK-START: void Main.deadInduction() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadInduction() loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- static void deadInduction() {
- int dead = 0;
- for (int i = 0; i < a.length; i++) {
- a[i] = novec[2 * i] + 1;
- dead += 5;
- }
- }
-
- /// CHECK-START: void Main.deadManyInduction() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadManyInduction() loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- static void deadManyInduction() {
- int dead1 = 0, dead2 = 1, dead3 = 3;
- for (int i = 0; i < a.length; i++) {
- dead1 += 5;
- a[i] = novec[2 * i] + 2;
- dead2 += 10;
- dead3 += 100;
- }
- }
-
- /// CHECK-START: void Main.deadSequence() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadSequence() loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- static void deadSequence() {
- int dead = 0;
- for (int i = 0; i < a.length; i++) {
- a[i] = novec[2 * i] + 3;
- // Increment value defined inside loop,
- // but sequence itself not used anywhere.
- dead += i;
- }
- }
-
- /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-NOT: BoundsCheck
- //
- /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none
- static void deadCycleWithException(int k) {
- int dead = 0;
- for (int i = 0; i < a.length; i++) {
- a[i] = novec[2 * i] + 4;
- // Increment value of dead cycle may throw exception. Dynamic
- // BCE takes care of the bounds check though, which enables
- // removing the ArrayGet after removing the dead cycle.
- dead += a[k];
- }
- }
-
- /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormInductionUp() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 12395 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormInductionUp() {
- int closed = 12345;
- for (int i = 0; i < 10; i++) {
- closed += 5;
- }
- return closed; // only needs last value
- }
-
- /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormInductionInAndDown(int) instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
- /// CHECK-DAG: <<Int:i\d+>> IntConstant -50 loop:none
- /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Par>>] loop:none
- /// CHECK-DAG: Return [<<Add>>] loop:none
- static int closedFormInductionInAndDown(int closed) {
- for (int i = 0; i < 10; i++) {
- closed -= 5;
- }
- return closed; // only needs last value
- }
-
- /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Select loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: Select
- //
- /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 81 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormInductionTrivialIf() {
- int closed = 11;
- for (int i = 0; i < 10; i++) {
- // Trivial if becomes trivial select at HIR level.
- // Make sure this is still recognized as induction.
- if (i < 5) {
- closed += 7;
- } else {
- closed += 7;
- }
- }
- return closed; // only needs last value
- }
-
- /// CHECK-START: int Main.closedFormNested() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormNested() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormNested() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 100 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormNested() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- for (int j = 0; j < 10; j++) {
- closed++;
- }
- }
- return closed; // only needs last-value
- }
-
- /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormNestedAlt() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 15082 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormNestedAlt() {
- int closed = 12345;
- for (int i = 0; i < 17; i++) {
- for (int j = 0; j < 23; j++) {
- closed += 7;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: taken test around closed form?
- static int closedFormInductionUpN(int n) {
- int closed = 12345;
- for (int i = 0; i < n; i++) {
- closed += 5;
- }
- return closed; // only needs last value
- }
-
- // TODO: taken test around closed form?
- static int closedFormInductionInAndDownN(int closed, int n) {
- for (int i = 0; i < n; i++) {
- closed -= 5;
- }
- return closed; // only needs last value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedN(int n) {
- int closed = 0;
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < 10; j++) {
- closed++;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedNAlt(int n) {
- int closed = 12345;
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < 23; j++) {
- closed += 7;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedMN(int m, int n) {
- int closed = 0;
- for (int i = 0; i < m; i++) {
- for (int j = 0; j < n; j++) {
- closed++;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedMNAlt(int m, int n) {
- int closed = 12345;
- for (int i = 0; i < m; i++) {
- for (int j = 0; j < n; j++) {
- closed += 7;
- }
- }
- return closed; // only needs last-value
- }
-
- /// CHECK-START: int Main.mainIndexReturned() loop_optimization (before)
- /// CHECK-DAG: <<Phi:i\d+>> Phi loop:{{B\d+}} outer_loop:none
- /// CHECK-DAG: Return [<<Phi>>] loop:none
- //
- /// CHECK-START: int Main.mainIndexReturned() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.mainIndexReturned() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int mainIndexReturned() {
- int i;
- for (i = 0; i < 10; i++);
- return i;
- }
-
- /// CHECK-START: int Main.periodicReturned9() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.periodicReturned9() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.periodicReturned9() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 1 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int periodicReturned9() {
- int k = 0;
- for (int i = 0; i < 9; i++) {
- k = 1 - k;
- }
- return k;
- }
-
- /// CHECK-START: int Main.periodicReturned10() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.periodicReturned10() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.periodicReturned10() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int periodicReturned10() {
- int k = 0;
- for (int i = 0; i < 10; i++) {
- k = 1 - k;
- }
- return k;
- }
-
- /// CHECK-START: int Main.getSum21() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: int Main.getSum21() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.getSum21() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 21 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int getSum21() {
- int k = 0;
- int sum = 0;
- for (int i = 0; i < 6; i++) {
- k++;
- sum += k;
- }
- return sum;
- }
-
- // Ensure double induction does not "overshoot" the subscript range.
- private static int getIncr2(int[] arr) {
- for (int i = 0; i < 12; ) {
- arr[i++] = 30;
- arr[i++] = 29;
- }
- int sum = 0;
- for (int i = 0; i < 12; i++) {
- sum += arr[i];
- }
- return sum;
- }
-
- // TODO: handle as closed/empty eventually?
- static int mainIndexReturnedN(int n) {
- int i;
- for (i = 0; i < n; i++);
- return i;
- }
-
- // TODO: handle as closed/empty eventually?
- static int mainIndexShort1(short s) {
- int i = 0;
- for (i = 0; i < s; i++) { }
- return i;
- }
-
- // TODO: handle as closed/empty eventually?
- static int mainIndexShort2(short s) {
- int i = 0;
- for (i = 0; s > i; i++) { }
- return i;
- }
-
- /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- static int periodicReturnedN(int n) {
- int k = 0;
- for (int i = 0; i < n; i++) {
- k = 1 - k;
- }
- return k;
- }
-
- // If ever replaced by closed form, last value should be correct!
- private static int getSumN(int n) {
- int k = 0;
- int sum = 0;
- for (int i = 0; i < n; i++) {
- k++;
- sum += k;
- }
- return sum;
- }
-
- // If ever replaced by closed form, last value should be correct!
- private static int closedTwice() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed++;
- }
- // Closed form of first loop defines trip count of second loop.
- int other_closed = 0;
- for (int i = 0; i < closed; i++) {
- other_closed++;
- }
- return other_closed;
- }
-
- /// CHECK-START: int Main.closedFeed() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi3>>] loop:none
- /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
- //
- /// CHECK-START: int Main.closedFeed() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFeed() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 20 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int closedFeed() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed++;
- }
- // Closed form of first loop feeds into initial value of second loop,
- // used when generating closed form for the latter.
- for (int i = 0; i < 10; i++) {
- closed++;
- }
- return closed;
- }
-
- /// CHECK-START: int Main.closedLargeUp() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedLargeUp() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedLargeUp() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant -10 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int closedLargeUp() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed += 0x7fffffff;
- }
- return closed;
- }
-
- /// CHECK-START: int Main.closedLargeDown() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedLargeDown() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedLargeDown() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int closedLargeDown() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed -= 0x7fffffff;
- }
- return closed;
- }
-
- /// CHECK-START: int Main.waterFall() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop3:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop4:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi5:i\d+>> Phi loop:<<Loop5:B\d+>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi5>>] loop:none
- //
- /// CHECK-START: int Main.waterFall() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.waterFall() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 50 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int waterFall() {
- int i = 0;
- for (; i < 10; i++);
- for (; i < 20; i++);
- for (; i < 30; i++);
- for (; i < 40; i++);
- for (; i < 50; i++);
- return i; // this should become just 50
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom1() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static boolean periodicBoolIdiom1() {
- boolean x = true;
- for (int i = 0; i < 7; i++) {
- x = !x;
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom2() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static boolean periodicBoolIdiom2() {
- boolean x = true;
- for (int i = 0; i < 7; i++) {
- x = (x != true);
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom3() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static boolean periodicBoolIdiom3() {
- boolean x = true;
- for (int i = 0; i < 7; i++) {
- x = (x == false);
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after)
- /// CHECK-NOT: Phi
- private static boolean periodicBoolIdiom1N(boolean x, int n) {
- for (int i = 0; i < n; i++) {
- x = !x;
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after)
- /// CHECK-NOT: Phi
- private static boolean periodicBoolIdiom2N(boolean x, int n) {
- for (int i = 0; i < n; i++) {
- x = (x != true);
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after)
- /// CHECK-NOT: Phi
- private static boolean periodicBoolIdiom3N(boolean x, int n) {
- for (int i = 0; i < n; i++) {
- x = (x == false);
- }
- return x;
- }
-
- /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
- /// CHECK-DAG: <<Float:f\d+>> FloatConstant 2 loop:none
- /// CHECK-DAG: Return [<<Float>>] loop:none
- private static float periodicFloat10() {
- float r = 4.5f;
- float s = 2.0f;
- float t = -1.0f;
- for (int i = 0; i < 10; i++) {
- float tmp = t; t = r; r = s; s = tmp;
- }
- return r;
- }
-
- /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
- /// CHECK-DAG: <<Float:f\d+>> FloatConstant -1 loop:none
- /// CHECK-DAG: Return [<<Float>>] loop:none
- private static float periodicFloat11() {
- float r = 4.5f;
- float s = 2.0f;
- float t = -1.0f;
- for (int i = 0; i < 11; i++) {
- float tmp = t; t = r; r = s; s = tmp;
- }
- return r;
- }
-
- /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
- /// CHECK-DAG: <<Float:f\d+>> FloatConstant 4.5 loop:none
- /// CHECK-DAG: Return [<<Float>>] loop:none
- private static float periodicFloat12() {
- float r = 4.5f;
- float s = 2.0f;
- float t = -1.0f;
- for (int i = 0; i < 12; i++) {
- float tmp = t; t = r; r = s; s = tmp;
- }
- return r;
- }
-
- private static int exceptionExitBeforeAdd() {
- int k = 0;
- try {
- for (int i = 0; i < 10; i++) {
- a[i] = 0;
- k += 10; // increment last
- }
- } catch(Exception e) {
- // Flag error by returning current
- // value of k negated.
- return -k-1;
- }
- return k;
- }
-
- private static int exceptionExitAfterAdd() {
- int k = 0;
- try {
- for (int i = 0; i < 10; i++) {
- k += 10; // increment first
- a[i] = 0;
- }
- } catch(Exception e) {
- // Flag error by returning current
- // value of k negated.
- return -k-1;
- }
- return k;
- }
-
- public static void main(String[] args) {
- deadSingleLoop();
- deadSingleLoopN(4);
- potentialInfiniteLoop(4);
- deadNestedLoops();
- deadNestedAndFollowingLoops();
- deadConditional(4);
- deadConditionalCycle(4);
-
- deadInduction();
- for (int i = 0; i < a.length; i++) {
- expectEquals(1, a[i]);
- }
- deadManyInduction();
- for (int i = 0; i < a.length; i++) {
- expectEquals(2, a[i]);
- }
- deadSequence();
- for (int i = 0; i < a.length; i++) {
- expectEquals(3, a[i]);
- }
- try {
- deadCycleWithException(-1);
- throw new Error("Expected: IOOB exception");
- } catch (IndexOutOfBoundsException e) {
- }
- for (int i = 0; i < a.length; i++) {
- expectEquals(i == 0 ? 4 : 3, a[i]);
- }
- deadCycleWithException(0);
- for (int i = 0; i < a.length; i++) {
- expectEquals(4, a[i]);
}
- expectEquals(12395, closedFormInductionUp());
- expectEquals(12295, closedFormInductionInAndDown(12345));
- expectEquals(81, closedFormInductionTrivialIf());
- expectEquals(10 * 10, closedFormNested());
- expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
- for (int n = -4; n < 10; n++) {
- int tc = (n <= 0) ? 0 : n;
- expectEquals(12345 + tc * 5, closedFormInductionUpN(n));
- expectEquals(12345 - tc * 5, closedFormInductionInAndDownN(12345, n));
- expectEquals(tc * 10, closedFormNestedN(n));
- expectEquals(12345 + tc * 23 * 7, closedFormNestedNAlt(n));
- expectEquals(tc * (tc + 1), closedFormNestedMN(n, n + 1));
- expectEquals(12345 + tc * (tc + 1) * 7, closedFormNestedMNAlt(n, n + 1));
+ /// CHECK-START: void Main.deadNestedLoops() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop>>
+ //
+ /// CHECK-START: void Main.deadNestedLoops() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadNestedLoops() {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ }
+ }
}
- expectEquals(10, mainIndexReturned());
- expectEquals(1, periodicReturned9());
- expectEquals(0, periodicReturned10());
- expectEquals(21, getSum21());
- expectEquals(354, getIncr2(new int[12]));
- for (int n = -4; n < 4; n++) {
- int tc = (n <= 0) ? 0 : n;
- expectEquals(tc, mainIndexReturnedN(n));
- expectEquals(tc, mainIndexShort1((short) n));
- expectEquals(tc, mainIndexShort2((short) n));
- expectEquals(tc & 1, periodicReturnedN(n));
- expectEquals((tc * (tc + 1)) / 2, getSumN(n));
+ /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
+ /// CHECK-DAG: Phi loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop3>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadNestedAndFollowingLoops() {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 4; k++) {
+ }
+ for (int k = 0; k < 4; k++) {
+ }
+ }
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 4; k++) {
+ }
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ }
}
- expectEquals(10, closedTwice());
- expectEquals(20, closedFeed());
- expectEquals(-10, closedLargeUp());
- expectEquals(10, closedLargeDown());
- expectEquals(50, waterFall());
-
- expectEquals(false, periodicBoolIdiom1());
- expectEquals(false, periodicBoolIdiom2());
- expectEquals(false, periodicBoolIdiom3());
- for (int n = -4; n < 10; n++) {
- int tc = (n <= 0) ? 0 : n;
- boolean even = (tc & 1) == 0;
- expectEquals(even, periodicBoolIdiom1N(true, n));
- expectEquals(!even, periodicBoolIdiom1N(false, n));
- expectEquals(even, periodicBoolIdiom2N(true, n));
- expectEquals(!even, periodicBoolIdiom2N(false, n));
- expectEquals(even, periodicBoolIdiom3N(true, n));
- expectEquals(!even, periodicBoolIdiom3N(false, n));
+ /// CHECK-START: void Main.deadConditional(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadConditional(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static void deadConditional(int n) {
+ int k = 0;
+ int m = 0;
+ for (int i = 0; i < n; i++) {
+ if (i == 3)
+ k = i;
+ else
+ m = i;
+ }
}
- expectEquals( 2.0f, periodicFloat10());
- expectEquals(-1.0f, periodicFloat11());
- expectEquals( 4.5f, periodicFloat12());
-
- expectEquals(100, exceptionExitBeforeAdd());
- expectEquals(100, exceptionExitAfterAdd());
- a = null;
- expectEquals(-1, exceptionExitBeforeAdd());
- expectEquals(-11, exceptionExitAfterAdd());
- a = new int[4];
- expectEquals(-41, exceptionExitBeforeAdd());
- expectEquals(-51, exceptionExitAfterAdd());
-
- System.out.println("passed");
- }
-
- private static void expectEquals(float expected, float result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static void deadConditionalCycle(int n) {
+ int k = 0;
+ int m = 0;
+ for (int i = 0; i < n; i++) {
+ if (i == 3)
+ k--;
+ else
+ m++;
+ }
}
- }
- private static void expectEquals(int expected, int result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
- }
- }
- private static void expectEquals(boolean expected, boolean result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ /// CHECK-START: void Main.deadInduction() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadInduction() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ static void deadInduction() {
+ int dead = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = novec[2 * i] + 1;
+ dead += 5;
+ }
}
- }
+
+ /// CHECK-START: void Main.deadManyInduction() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadManyInduction() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ static void deadManyInduction() {
+ int dead1 = 0, dead2 = 1, dead3 = 3;
+ for (int i = 0; i < a.length; i++) {
+ dead1 += 5;
+ a[i] = novec[2 * i] + 2;
+ dead2 += 10;
+ dead3 += 100;
+ }
+ }
+
+ /// CHECK-START: void Main.deadSequence() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadSequence() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ static void deadSequence() {
+ int dead = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = novec[2 * i] + 3;
+ // Increment value defined inside loop,
+ // but sequence itself not used anywhere.
+ dead += i;
+ }
+ }
+
+ /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-NOT: BoundsCheck
+ //
+ /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none
+ static void deadCycleWithException(int k) {
+ int dead = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = novec[2 * i] + 4;
+ // Increment value of dead cycle may throw exception. Dynamic
+ // BCE takes care of the bounds check though, which enables
+ // removing the ArrayGet after removing the dead cycle.
+ dead += a[k];
+ }
+ }
+
+ /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormInductionUp() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 12395 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormInductionUp() {
+ int closed = 12345;
+ for (int i = 0; i < 10; i++) {
+ closed += 5;
+ }
+ return closed; // only needs last value
+ }
+
+ /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormInductionInAndDown(int) instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant -50 loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Par>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ static int closedFormInductionInAndDown(int closed) {
+ for (int i = 0; i < 10; i++) {
+ closed -= 5;
+ }
+ return closed; // only needs last value
+ }
+
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Select loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Select
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 81 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormInductionTrivialIf() {
+ int closed = 11;
+ for (int i = 0; i < 10; i++) {
+ // Trivial if becomes trivial select at HIR level.
+ // Make sure this is still recognized as induction.
+ if (i < 5) {
+ closed += 7;
+ } else {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last value
+ }
+
+ /// CHECK-START: int Main.closedFormNested() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormNested() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormNested() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 100 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormNested() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 10; j++) {
+ closed++;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormNestedAlt() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 15082 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormNestedAlt() {
+ int closed = 12345;
+ for (int i = 0; i < 17; i++) {
+ for (int j = 0; j < 23; j++) {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: taken test around closed form?
+ static int closedFormInductionUpN(int n) {
+ int closed = 12345;
+ for (int i = 0; i < n; i++) {
+ closed += 5;
+ }
+ return closed; // only needs last value
+ }
+
+ // TODO: taken test around closed form?
+ static int closedFormInductionInAndDownN(int closed, int n) {
+ for (int i = 0; i < n; i++) {
+ closed -= 5;
+ }
+ return closed; // only needs last value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedN(int n) {
+ int closed = 0;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < 10; j++) {
+ closed++;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedNAlt(int n) {
+ int closed = 12345;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < 23; j++) {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedMN(int m, int n) {
+ int closed = 0;
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ closed++;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedMNAlt(int m, int n) {
+ int closed = 12345;
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ /// CHECK-START: int Main.mainIndexReturned() loop_optimization (before)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:{{B\d+}} outer_loop:none
+ /// CHECK-DAG: Return [<<Phi>>] loop:none
+ //
+ /// CHECK-START: int Main.mainIndexReturned() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.mainIndexReturned() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int mainIndexReturned() {
+ int i;
+ for (i = 0; i < 10; i++);
+ return i;
+ }
+
+ /// CHECK-START: int Main.periodicReturned9() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.periodicReturned9() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.periodicReturned9() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int periodicReturned9() {
+ int k = 0;
+ for (int i = 0; i < 9; i++) {
+ k = 1 - k;
+ }
+ return k;
+ }
+
+ /// CHECK-START: int Main.periodicReturned10() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.periodicReturned10() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.periodicReturned10() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int periodicReturned10() {
+ int k = 0;
+ for (int i = 0; i < 10; i++) {
+ k = 1 - k;
+ }
+ return k;
+ }
+
+ /// CHECK-START: int Main.getSum21() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: int Main.getSum21() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.getSum21() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 21 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int getSum21() {
+ int k = 0;
+ int sum = 0;
+ for (int i = 0; i < 6; i++) {
+ k++;
+ sum += k;
+ }
+ return sum;
+ }
+
+ // Ensure double induction does not "overshoot" the subscript range.
+ private static int getIncr2(int[] arr) {
+ for (int i = 0; i < 12; ) {
+ arr[i++] = 30;
+ arr[i++] = 29;
+ }
+ int sum = 0;
+ for (int i = 0; i < 12; i++) {
+ sum += arr[i];
+ }
+ return sum;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexReturnedN(int n) {
+ int i;
+ for (i = 0; i < n; i++);
+ return i;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort1(short s) {
+ int i = 0;
+ for (i = 0; i < s; i++) { }
+ return i;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort2(short s) {
+ int i = 0;
+ for (i = 0; s > i; i++) { }
+ return i;
+ }
+
+ /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static int periodicReturnedN(int n) {
+ int k = 0;
+ for (int i = 0; i < n; i++) {
+ k = 1 - k;
+ }
+ return k;
+ }
+
+ // If ever replaced by closed form, last value should be correct!
+ private static int getSumN(int n) {
+ int k = 0;
+ int sum = 0;
+ for (int i = 0; i < n; i++) {
+ k++;
+ sum += k;
+ }
+ return sum;
+ }
+
+ // If ever replaced by closed form, last value should be correct!
+ private static int closedTwice() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed++;
+ }
+ // Closed form of first loop defines trip count of second loop.
+ int other_closed = 0;
+ for (int i = 0; i < closed; i++) {
+ other_closed++;
+ }
+ return other_closed;
+ }
+
+ /// CHECK-START: int Main.closedFeed() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi3>>] loop:none
+ /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
+ //
+ /// CHECK-START: int Main.closedFeed() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFeed() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 20 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedFeed() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed++;
+ }
+ // Closed form of first loop feeds into initial value of second loop,
+ // used when generating closed form for the latter.
+ for (int i = 0; i < 10; i++) {
+ closed++;
+ }
+ return closed;
+ }
+
+ /// CHECK-START: int Main.closedLargeUp() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedLargeUp() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedLargeUp() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant -10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedLargeUp() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed += 0x7fffffff;
+ }
+ return closed;
+ }
+
+ /// CHECK-START: int Main.closedLargeDown() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedLargeDown() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedLargeDown() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedLargeDown() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed -= 0x7fffffff;
+ }
+ return closed;
+ }
+
+ // Checks that we do not loop optimize if the calculation of the trip count would overflow.
+ /// CHECK-START: int Main.closedLinearStepOverflow() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedLinearStepOverflow() loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ private static int closedLinearStepOverflow() {
+ int closed = 0;
+ // Note that this isn't a "one-off" error.
+ // We are using MIN and MAX to make sure we overflow.
+ for (int i = Integer.MIN_VALUE; i < (Integer.MAX_VALUE - 80); i += 79) {
+ closed++;
+ }
+ return closed;
+ }
+
+ // Since we cannot guarantee that the start/end wouldn't overflow we do not perform loop
+ // optimization.
+ /// CHECK-START: int Main.$inline$closedByParameters(int, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.$inline$closedByParameters(int, int) loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ private static int $inline$closedByParameters(int start, int end) {
+ int closed = 0;
+ for (int i = start; i < end; i++) {
+ closed++;
+ }
+ return closed;
+ }
+
+ // Since we are inlining `closedByParameters` we know that the parameters are fixed and
+ // therefore we can perform loop optimization.
+ /// CHECK-START: int Main.closedByParametersWithInline() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedByParametersWithInline() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedByParametersWithInline() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedByParametersWithInline() {
+ return $inline$closedByParameters(0, 10);
+ }
+
+ /// CHECK-START: int Main.waterFall() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop3:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop4:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi5:i\d+>> Phi loop:<<Loop5:B\d+>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi5>>] loop:none
+ //
+ /// CHECK-START: int Main.waterFall() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.waterFall() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 50 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int waterFall() {
+ int i = 0;
+ for (; i < 10; i++);
+ for (; i < 20; i++);
+ for (; i < 30; i++);
+ for (; i < 40; i++);
+ for (; i < 50; i++);
+ return i; // this should become just 50
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom1() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static boolean periodicBoolIdiom1() {
+ boolean x = true;
+ for (int i = 0; i < 7; i++) {
+ x = !x;
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom2() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static boolean periodicBoolIdiom2() {
+ boolean x = true;
+ for (int i = 0; i < 7; i++) {
+ x = (x != true);
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom3() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static boolean periodicBoolIdiom3() {
+ boolean x = true;
+ for (int i = 0; i < 7; i++) {
+ x = (x == false);
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ private static boolean periodicBoolIdiom1N(boolean x, int n) {
+ for (int i = 0; i < n; i++) {
+ x = !x;
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ private static boolean periodicBoolIdiom2N(boolean x, int n) {
+ for (int i = 0; i < n; i++) {
+ x = (x != true);
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ private static boolean periodicBoolIdiom3N(boolean x, int n) {
+ for (int i = 0; i < n; i++) {
+ x = (x == false);
+ }
+ return x;
+ }
+
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 2 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat10() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 10; i++) {
+ float tmp = t;
+ t = r;
+ r = s;
+ s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant -1 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat11() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 11; i++) {
+ float tmp = t;
+ t = r;
+ r = s;
+ s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 4.5 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat12() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 12; i++) {
+ float tmp = t;
+ t = r;
+ r = s;
+ s = tmp;
+ }
+ return r;
+ }
+
+ private static int exceptionExitBeforeAdd() {
+ int k = 0;
+ try {
+ for (int i = 0; i < 10; i++) {
+ a[i] = 0;
+ k += 10; // increment last
+ }
+ } catch (Exception e) {
+ // Flag error by returning current
+ // value of k negated.
+ return -k - 1;
+ }
+ return k;
+ }
+
+ private static int exceptionExitAfterAdd() {
+ int k = 0;
+ try {
+ for (int i = 0; i < 10; i++) {
+ k += 10; // increment first
+ a[i] = 0;
+ }
+ } catch (Exception e) {
+ // Flag error by returning current
+ // value of k negated.
+ return -k - 1;
+ }
+ return k;
+ }
+
+ /// CHECK-START: long Main.closedLinearInductionUnmatchedTypesNotOptimized() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:j\d+>> Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: long Main.closedLinearInductionUnmatchedTypesNotOptimized() loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:j\d+>> Phi loop:<<Loop>> outer_loop:none
+ private static long closedLinearInductionUnmatchedTypesNotOptimized() {
+ long sum = 0;
+ for (int i = 0; i < 10; ++i) {
+ ++sum;
+ }
+ return sum;
+ }
+
+ /// CHECK-START: short Main.closedLinearInductionNarrowingNotOptimized() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ //
+ /// CHECK-START: short Main.closedLinearInductionNarrowingNotOptimized() loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ private static short closedLinearInductionNarrowingNotOptimized() {
+ short i = 0;
+ for (; i < 10; ++i);
+ return i;
+ }
+
+ public static void main(String[] args) {
+ deadSingleLoop();
+ deadSingleLoopN(4);
+ potentialInfiniteLoop(4);
+ deadNestedLoops();
+ deadNestedAndFollowingLoops();
+ deadConditional(4);
+ deadConditionalCycle(4);
+
+ deadInduction();
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(1, a[i]);
+ }
+ deadManyInduction();
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(2, a[i]);
+ }
+ deadSequence();
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(3, a[i]);
+ }
+ try {
+ deadCycleWithException(-1);
+ throw new Error("Expected: IOOB exception");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(i == 0 ? 4 : 3, a[i]);
+ }
+ deadCycleWithException(0);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(4, a[i]);
+ }
+
+ expectEquals(12395, closedFormInductionUp());
+ expectEquals(12295, closedFormInductionInAndDown(12345));
+ expectEquals(81, closedFormInductionTrivialIf());
+ expectEquals(10 * 10, closedFormNested());
+ expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
+ for (int n = -4; n < 10; n++) {
+ int tc = (n <= 0) ? 0 : n;
+ expectEquals(12345 + tc * 5, closedFormInductionUpN(n));
+ expectEquals(12345 - tc * 5, closedFormInductionInAndDownN(12345, n));
+ expectEquals(tc * 10, closedFormNestedN(n));
+ expectEquals(12345 + tc * 23 * 7, closedFormNestedNAlt(n));
+ expectEquals(tc * (tc + 1), closedFormNestedMN(n, n + 1));
+ expectEquals(12345 + tc * (tc + 1) * 7, closedFormNestedMNAlt(n, n + 1));
+ }
+
+ expectEquals(10, mainIndexReturned());
+ expectEquals(1, periodicReturned9());
+ expectEquals(0, periodicReturned10());
+ expectEquals(21, getSum21());
+ expectEquals(354, getIncr2(new int[12]));
+ for (int n = -4; n < 4; n++) {
+ int tc = (n <= 0) ? 0 : n;
+ expectEquals(tc, mainIndexReturnedN(n));
+ expectEquals(tc, mainIndexShort1((short) n));
+ expectEquals(tc, mainIndexShort2((short) n));
+ expectEquals(tc & 1, periodicReturnedN(n));
+ expectEquals((tc * (tc + 1)) / 2, getSumN(n));
+ }
+
+ expectEquals(10, closedTwice());
+ expectEquals(20, closedFeed());
+ expectEquals(-10, closedLargeUp());
+ expectEquals(10, closedLargeDown());
+ expectEquals(54366674, closedLinearStepOverflow());
+ expectEquals(10, $inline$closedByParameters(0, 10));
+ expectEquals(10, closedByParametersWithInline());
+ expectEquals(50, waterFall());
+
+ expectEquals(false, periodicBoolIdiom1());
+ expectEquals(false, periodicBoolIdiom2());
+ expectEquals(false, periodicBoolIdiom3());
+ for (int n = -4; n < 10; n++) {
+ int tc = (n <= 0) ? 0 : n;
+ boolean even = (tc & 1) == 0;
+ expectEquals(even, periodicBoolIdiom1N(true, n));
+ expectEquals(!even, periodicBoolIdiom1N(false, n));
+ expectEquals(even, periodicBoolIdiom2N(true, n));
+ expectEquals(!even, periodicBoolIdiom2N(false, n));
+ expectEquals(even, periodicBoolIdiom3N(true, n));
+ expectEquals(!even, periodicBoolIdiom3N(false, n));
+ }
+
+ expectEquals( 2.0f, periodicFloat10());
+ expectEquals(-1.0f, periodicFloat11());
+ expectEquals( 4.5f, periodicFloat12());
+
+ expectEquals(100, exceptionExitBeforeAdd());
+ expectEquals(100, exceptionExitAfterAdd());
+ a = null;
+ expectEquals(-1, exceptionExitBeforeAdd());
+ expectEquals(-11, exceptionExitAfterAdd());
+ a = new int[4];
+ expectEquals(-41, exceptionExitBeforeAdd());
+ expectEquals(-51, exceptionExitAfterAdd());
+
+ expectEquals(10, closedLinearInductionUnmatchedTypesNotOptimized());
+ expectEquals(10, closedLinearInductionNarrowingNotOptimized());
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
}
diff --git a/test/619-checker-current-method/src/Main.java b/test/619-checker-current-method/src/Main.java
index d829370..3ab73fa 100644
--- a/test/619-checker-current-method/src/Main.java
+++ b/test/619-checker-current-method/src/Main.java
@@ -15,19 +15,18 @@
*/
public class Main {
+ // Check that there is no instruction storing to stack.
+ /// CHECK-START-X86: int Main.foo(int, int, int, int, int, int) disassembly (after)
+ /// CHECK-NOT: mov [{{\w+}}], {{\w+}}
- // Check that there is no instruction storing to stack.
- /// CHECK-START-X86: int Main.foo(int, int, int, int, int, int) disassembly (after)
- /// CHECK-NOT: mov [{{\w+}}], {{\w+}}
-
- // Use enough parameters to ensure we'll need a frame.
- public static int foo(int a, int b, int c, int d, int e, int f) {
- return a + b + c + d + e + f;
- }
-
- public static void main(String[] args) {
- if (foo(1, 2, 3, 4, 5, 6) != 21) {
- throw new Error("Expected 21");
+ // Use enough parameters to ensure we'll need a frame.
+ public static int foo(int a, int b, int c, int d, int e, int f) {
+ return a + b + c + d + e + f;
}
- }
+
+ public static void main(String[] args) {
+ if (foo(1, 2, 3, 4, 5, 6) != 21) {
+ throw new Error("Expected 21");
+ }
+ }
}
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
index 1aa3cce..ef230ad 100644
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ b/test/626-const-class-linking/clear_dex_cache_types.cc
@@ -28,8 +28,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) {
- mirror::TypeDexCachePair cleared(nullptr, mirror::TypeDexCachePair::InvalidIndexForSlot(i));
- dex_cache->GetResolvedTypes()[i].store(cleared, std::memory_order_relaxed);
+ dex_cache->GetResolvedTypes()->Clear(i);
}
}
diff --git a/test/628-vdex/run b/test/628-vdex/run
deleted file mode 100644
index bf0ac91..0000000
--- a/test/628-vdex/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/628-vdex/run.py b/test/628-vdex/run.py
new file mode 100644
index 0000000..066152c
--- /dev/null
+++ b/test/628-vdex/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, Xcompiler_option=["--compiler-filter=verify"], vdex=True)
diff --git a/test/629-vdex-speed/run b/test/629-vdex-speed/run
deleted file mode 100644
index 1477e3d..0000000
--- a/test/629-vdex-speed/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-exec ${RUN} --vdex --vdex-filter speed "${@}"
diff --git a/test/629-vdex-speed/run.py b/test/629-vdex-speed/run.py
new file mode 100644
index 0000000..1d4f199
--- /dev/null
+++ b/test/629-vdex-speed/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, vdex=True, vdex_filter="speed")
diff --git a/test/634-vdex-duplicate/run b/test/634-vdex-duplicate/run
deleted file mode 100644
index 571ccd9..0000000
--- a/test/634-vdex-duplicate/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex-filter speed --vdex "${@}"
diff --git a/test/634-vdex-duplicate/run.py b/test/634-vdex-duplicate/run.py
new file mode 100644
index 0000000..c99522d
--- /dev/null
+++ b/test/634-vdex-duplicate/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--compiler-filter=verify"],
+ vdex_filter="speed",
+ vdex=True)
diff --git a/test/636-wrong-static-access/run b/test/636-wrong-static-access/run
deleted file mode 100755
index 5e99920..0000000
--- a/test/636-wrong-static-access/run
+++ /dev/null
@@ -1,20 +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.
-
-# Make verification soft fail, to ensure the verifier does not flag
-# the method we want to compile as "non-compilable" because it sees
-# the method will throw IncompatibleClassChangeError.
-exec ${RUN} $@ --verify-soft-fail
diff --git a/test/636-wrong-static-access/run.py b/test/636-wrong-static-access/run.py
new file mode 100644
index 0000000..70a7fca
--- /dev/null
+++ b/test/636-wrong-static-access/run.py
@@ -0,0 +1,22 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Make verification soft fail, to ensure the verifier does not flag
+ # the method we want to compile as "non-compilable" because it sees
+ # the method will throw IncompatibleClassChangeError.
+ ctx.default_run(args, verify_soft_fail=True)
diff --git a/test/638-checker-inline-cache-intrinsic/run b/test/638-checker-inline-cache-intrinsic/run
deleted file mode 100644
index 9016107..0000000
--- a/test/638-checker-inline-cache-intrinsic/run
+++ /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.
-
-# Set threshold to 1000 to match the iterations done in the test.
-# Pass --verbose-methods to only generate the CFG of these methods.
-# The test is for JIT, but we run in "optimizing" (AOT) mode, so that the Checker
-# stanzas in test/638-checker-inline-cache-intrinsic/src/Main.java will be checked.
-# Also pass a large JIT code cache size to avoid getting the inline caches GCed.
-exec ${RUN} --jit --runtime-option -Xjitinitialsize:32M --runtime-option -Xjitthreshold:1000 -Xcompiler-option --verbose-methods=inlineMonomorphic,inlinePolymorphic,knownReceiverType,stringEquals $@
diff --git a/test/638-checker-inline-cache-intrinsic/run.py b/test/638-checker-inline-cache-intrinsic/run.py
new file mode 100644
index 0000000..48f0f3f
--- /dev/null
+++ b/test/638-checker-inline-cache-intrinsic/run.py
@@ -0,0 +1,30 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Set threshold to 1000 to match the iterations done in the test.
+ # Pass --verbose-methods to only generate the CFG of these methods.
+ # The test is for JIT, but we run in "optimizing" (AOT) mode, so that the Checker
+ # stanzas in test/638-checker-inline-cache-intrinsic/src/Main.java will be checked.
+ # Also pass a large JIT code cache size to avoid getting the inline caches GCed.
+ ctx.default_run(
+ args,
+ jit=True,
+ runtime_option=["-Xjitinitialsize:32M", "-Xjitthreshold:1000"],
+ Xcompiler_option=[
+ "--verbose-methods=inlineMonomorphic,inlinePolymorphic,knownReceiverType,stringEquals"
+ ])
diff --git a/test/638-checker-inline-caches/expected-stdout.txt b/test/638-checker-inline-caches/expected-stdout.txt
index e69de29..8f1cb06 100644
--- a/test/638-checker-inline-caches/expected-stdout.txt
+++ b/test/638-checker-inline-caches/expected-stdout.txt
@@ -0,0 +1 @@
+I don't throw
diff --git a/test/638-checker-inline-caches/profile b/test/638-checker-inline-caches/profile
index 7756a16..4630a8d 100644
--- a/test/638-checker-inline-caches/profile
+++ b/test/638-checker-inline-caches/profile
@@ -4,3 +4,4 @@
HSLMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
HSLMain;->inlineMissingTypes(LSuper;)I+missing_types
HSLMain;->noInlineCache(LSuper;)I
+HSLMain;->noInlineSomeSubclassesThrow(LSuper;)V+LSubA;
diff --git a/test/638-checker-inline-caches/run b/test/638-checker-inline-caches/run
deleted file mode 100644
index 146e180..0000000
--- a/test/638-checker-inline-caches/run
+++ /dev/null
@@ -1,17 +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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/638-checker-inline-caches/run.py b/test/638-checker-inline-caches/run.py
new file mode 100644
index 0000000..7678899
--- /dev/null
+++ b/test/638-checker-inline-caches/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/638-checker-inline-caches/src-multidex/SubC.java b/test/638-checker-inline-caches/src-multidex/SubC.java
index f7e3c08..e0f571e 100644
--- a/test/638-checker-inline-caches/src-multidex/SubC.java
+++ b/test/638-checker-inline-caches/src-multidex/SubC.java
@@ -16,4 +16,5 @@
public class SubC extends Super {
public int getValue() { return 24; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
diff --git a/test/638-checker-inline-caches/src/Main.java b/test/638-checker-inline-caches/src/Main.java
index f104e6a..7a0fb26 100644
--- a/test/638-checker-inline-caches/src/Main.java
+++ b/test/638-checker-inline-caches/src/Main.java
@@ -16,18 +16,22 @@
class SubA extends Super {
int getValue() { return 42; }
+ void someSubclassesThrow() throws Error { throw new Error("I always throw"); }
}
class SubB extends Super {
int getValue() { return 38; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
class SubD extends Super {
int getValue() { return 10; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
class SubE extends Super {
int getValue() { return -4; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
public class Main {
@@ -134,6 +138,20 @@
return a.getValue();
}
+ // We shouldn't inline `someSubclassesThrow` since we are trying a monomorphic inline and it
+ // always throws for SubA. However, we shouldn't mark it as `always_throws` since we speculatively
+ // tried to inline and other subclasses (e.g. SubB) can call noInlineSomeSubclassesThrow and they
+ // don't throw.
+
+ /// CHECK-START: void Main.noInlineSomeSubclassesThrow(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.someSubclassesThrow always_throws:false
+
+ /// CHECK-START: void Main.noInlineSomeSubclassesThrow(Super) inliner (after)
+ /// CHECK: InvokeVirtual method_name:Super.someSubclassesThrow always_throws:false
+ public static void noInlineSomeSubclassesThrow(Super a) throws Error {
+ a.someSubclassesThrow();
+ }
+
public static void testInlineMonomorphic() {
if (inlineMonomorphicSubA(new SubA()) != 42) {
throw new Error("Expected 42");
@@ -186,11 +204,20 @@
}
}
- public static void main(String[] args) {
+ private static void $noinline$testsomeSubclassesThrow() throws Exception {
+ try {
+ noInlineSomeSubclassesThrow(new SubA());
+ throw new Exception("Unreachable");
+ } catch (Error expected) {
+ }
+ noInlineSomeSubclassesThrow(new SubB());
+ }
+
+ public static void main(String[] args) throws Exception {
testInlineMonomorphic();
testInlinePolymorhic();
testInlineMegamorphic();
testNoInlineCache();
+ $noinline$testsomeSubclassesThrow();
}
-
}
diff --git a/test/638-checker-inline-caches/src/Super.java b/test/638-checker-inline-caches/src/Super.java
index 30cdf30..1964866 100644
--- a/test/638-checker-inline-caches/src/Super.java
+++ b/test/638-checker-inline-caches/src/Super.java
@@ -16,4 +16,5 @@
public abstract class Super {
abstract int getValue();
+ abstract void someSubclassesThrow();
}
diff --git a/test/638-no-line-number/build b/test/638-no-line-number/build
deleted file mode 100644
index 9cd1955..0000000
--- a/test/638-no-line-number/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
-
-# Only keep the source name, to make sure we do remove it in the stack trace
-# when there is no line number mapping.
-JAVAC_ARGS="$JAVAC_ARGS -g:source" ./default-build "$@"
diff --git a/test/638-no-line-number/build.py b/test/638-no-line-number/build.py
new file mode 100644
index 0000000..3643347
--- /dev/null
+++ b/test/638-no-line-number/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Only keep the source name, to make sure we do remove it in the stack trace
+# when there is no line number mapping.
+def build(ctx):
+ ctx.default_build(javac_args=["-g:source"])
diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java
index 5e465f2..a42e40d 100644
--- a/test/639-checker-code-sinking/src/Main.java
+++ b/test/639-checker-code-sinking/src/Main.java
@@ -27,6 +27,7 @@
testPhiInput();
testVolatileStore();
testCatchBlock();
+ $noinline$testTwoThrowingPathsAndStringBuilderAppend();
doThrow = true;
try {
testInstanceSideEffects();
@@ -392,45 +393,13 @@
}
private static void testCatchBlock() {
- assertEquals(456, testSinkToCatchBlock());
assertEquals(456, testDoNotSinkToTry());
- assertEquals(456, testDoNotSinkToCatchInsideTry());
assertEquals(456, testSinkWithinTryBlock());
assertEquals(456, testSinkRightBeforeTryBlock());
- assertEquals(456, testSinkToSecondCatch());
assertEquals(456, testDoNotSinkToCatchInsideTryWithMoreThings(false, false));
- assertEquals(456, testSinkToCatchBlockCustomClass());
assertEquals(456, DoNotSinkWithOOMThrow());
}
- /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (before)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
-
- // Consistency check to make sure there's only one entry TryBoundary.
- /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
-
- // Tests that we can sink the Object creation to the catch block.
- private static int testSinkToCatchBlock() {
- Object o = new Object();
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error(o.toString());
- }
- return 456;
- }
-
/// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (before)
/// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
/// CHECK: NewInstance [<<ObjLoadClass>>]
@@ -459,41 +428,6 @@
return 456;
}
- /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (before)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (after)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
-
- // Consistency check to make sure there's exactly two entry TryBoundary.
- /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
-
- // Tests that we don't sink the Object creation into a catch handler surrounded by try/catch.
- private static int testDoNotSinkToCatchInsideTry() {
- Object o = new Object();
- try {
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error(o.toString());
- }
- } catch (Error e) {
- throw new Error();
- }
- return 456;
- }
-
/// CHECK-START: int Main.testSinkWithinTryBlock() code_sinking (before)
/// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
/// CHECK: NewInstance [<<ObjLoadClass>>]
@@ -538,46 +472,6 @@
return 456;
}
- /// CHECK-START: int Main.testSinkToSecondCatch() code_sinking (before)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testSinkToSecondCatch() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
-
- // Consistency check to make sure there's exactly two entry TryBoundary.
- /// CHECK-START: int Main.testSinkToSecondCatch() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
- private static int testSinkToSecondCatch() {
- Object o = new Object();
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error();
- }
-
- try {
- // We need a different boolean to the one above, so that the compiler cannot optimize this
- // return away.
- if (doOtherEarlyReturn) {
- return 789;
- }
- } catch (Error e) {
- throw new Error(o.toString());
- }
-
- return 456;
- }
-
/// CHECK-START: int Main.testDoNotSinkToCatchInsideTryWithMoreThings(boolean, boolean) code_sinking (before)
/// CHECK-NOT: TryBoundary kind:entry
/// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
@@ -616,38 +510,6 @@
int x;
}
- /// CHECK-START: int Main.testSinkToCatchBlockCustomClass() code_sinking (before)
- /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main$ObjectWithInt
- /// CHECK: <<Clinit:l\d+>> ClinitCheck [<<LoadClass>>]
- /// CHECK: NewInstance [<<Clinit>>]
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testSinkToCatchBlockCustomClass() code_sinking (after)
- /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main$ObjectWithInt
- /// CHECK: <<Clinit:l\d+>> ClinitCheck [<<LoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: NewInstance [<<Clinit>>]
-
- // Consistency check to make sure there's only one entry TryBoundary.
- /// CHECK-START: int Main.testSinkToCatchBlockCustomClass() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
-
- // Similar to testSinkToCatchBlock, but using a custom class. CLinit check is not an instruction
- // that we sink since it can throw and it is not in the allow list. We can sink the NewInstance
- // nevertheless.
- private static int testSinkToCatchBlockCustomClass() {
- ObjectWithInt obj = new ObjectWithInt();
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error(Integer.toString(obj.x));
- }
- return 456;
- }
-
/// CHECK-START: int Main.DoNotSinkWithOOMThrow() code_sinking (before)
/// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main$ObjectWithInt
/// CHECK: <<Clinit:l\d+>> ClinitCheck [<<LoadClass>>]
@@ -687,12 +549,77 @@
return x;
}
+ private static void $noinline$testTwoThrowingPathsAndStringBuilderAppend() {
+ try {
+ $noinline$twoThrowingPathsAndStringBuilderAppend(null);
+ throw new Error("Unreachable");
+ } catch (Error expected) {
+ assertEquals("Object is null", expected.getMessage());
+ }
+ try {
+ $noinline$twoThrowingPathsAndStringBuilderAppend(new Object());
+ throw new Error("Unreachable");
+ } catch (Error expected) {
+ assertEquals("s1s2", expected.getMessage());
+ }
+ }
+
+ // We currently do not inline the `StringBuilder` constructor.
+ // When we did, the `StringBuilderAppend` pattern recognition was looking for
+ // the inlined `NewArray` (and its associated `LoadClass`) and checked in
+ // debug build that the `StringBuilder` has an environment use from this
+ // `NewArray` (and maybe from `LoadClass`). However, code sinking was pruning
+ // the environment of the `NewArray`, leading to a crash when compiling the
+ // code below on the device (we do not inline `core-oj` on host). b/252799691
+
+ // We currently have a heuristic that disallows inlining methods if their basic blocks end with a
+ // throw. We could add code so that `requireNonNull`'s block doesn't end with a throw but that
+ // would mean that the string builder optimization wouldn't fire as it requires all uses to be in
+ // the same block. If `requireNonNull` is inlined at some point, we need to re-mark it as $inline$
+ // so that the test is operational again.
+
+ /// CHECK-START: void Main.$noinline$twoThrowingPathsAndStringBuilderAppend(java.lang.Object) inliner (before)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.requireNonNull
+
+ /// CHECK-START: void Main.$noinline$twoThrowingPathsAndStringBuilderAppend(java.lang.Object) inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.requireNonNull
+ private static void $noinline$twoThrowingPathsAndStringBuilderAppend(Object o) {
+ String s1 = "s1";
+ String s2 = "s2";
+ StringBuilder sb = new StringBuilder();
+
+ // Before inlining, the environment use from this invoke prevents the
+ // `StringBuilderAppend` pattern recognition. After inlining, we end up
+ // with two paths ending with a `Throw` and we could sink the `sb`
+ // instructions from above down to those below, enabling the
+ // `StringBuilderAppend` pattern recognition.
+ // (But that does not happen when the `StringBuilder` constructor is
+ // not inlined, see above.)
+ requireNonNull(o);
+
+ String s1s2 = sb.append(s1).append(s2).toString();
+ sb = null;
+ throw new Error(s1s2);
+ }
+
+ private static void requireNonNull(Object o) {
+ if (o == null) {
+ throw new Error("Object is null");
+ }
+ }
+
private static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
}
}
+ private static void assertEquals(String expected, String actual) {
+ if (!expected.equals(actual)) {
+ throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
+ }
+ }
+
volatile int volatileField;
int intField;
int intField2;
diff --git a/test/643-checker-bogus-ic/run b/test/643-checker-bogus-ic/run
deleted file mode 100644
index 146e180..0000000
--- a/test/643-checker-bogus-ic/run
+++ /dev/null
@@ -1,17 +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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/643-checker-bogus-ic/run.py b/test/643-checker-bogus-ic/run.py
new file mode 100644
index 0000000..7678899
--- /dev/null
+++ b/test/643-checker-bogus-ic/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/648-inline-caches-unresolved/run b/test/648-inline-caches-unresolved/run
deleted file mode 100644
index d24ef42..0000000
--- a/test/648-inline-caches-unresolved/run
+++ /dev/null
@@ -1,17 +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.
-
-exec ${RUN} $@ --profile
diff --git a/test/648-inline-caches-unresolved/run.py b/test/648-inline-caches-unresolved/run.py
new file mode 100644
index 0000000..5ef44b3
--- /dev/null
+++ b/test/648-inline-caches-unresolved/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, profile=True)
diff --git a/test/648-many-direct-methods/build b/test/648-many-direct-methods/build
deleted file mode 100755
index 7e888e5..0000000
--- a/test/648-many-direct-methods/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#! /bin/bash
-#
-# Copyright 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.
-
-# Exit on a failure.
-set -e
-
-mkdir -p ./src
-
-# Generate the Java file or fail.
-./util-src/generate_java.py ./src
-
-./default-build "$@"
diff --git a/test/648-many-direct-methods/build.py b/test/648-many-direct-methods/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/648-many-direct-methods/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/648-many-direct-methods/generate-sources b/test/648-many-direct-methods/generate-sources
new file mode 100755
index 0000000..8907645
--- /dev/null
+++ b/test/648-many-direct-methods/generate-sources
@@ -0,0 +1,23 @@
+#! /bin/bash
+#
+# Copyright 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.
+
+# Exit on a failure.
+set -e
+
+mkdir -p ./src
+
+# Generate the Java file or fail.
+./util-src/generate_java.py ./src
diff --git a/test/652-deopt-intrinsic/run b/test/652-deopt-intrinsic/run
deleted file mode 100755
index 1acedf9..0000000
--- a/test/652-deopt-intrinsic/run
+++ /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.
-
-# Ensure this test is not subject to code collection.
-# We also need at least a few invocations of the method Main.$noinline$doCall
-# to ensure the inline cache sees the two types being passed to the method. Pass
-# a large number in case there's some weights on some invocation kinds (eg
-# compiler to interpreter transitions).
-exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M --runtime-option -Xjitthreshold:1000
diff --git a/test/652-deopt-intrinsic/run.py b/test/652-deopt-intrinsic/run.py
new file mode 100644
index 0000000..82f8ed0
--- /dev/null
+++ b/test/652-deopt-intrinsic/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ensure this test is not subject to code collection.
+ # We also need at least a few invocations of the method Main.$noinline$doCall
+ # to ensure the inline cache sees the two types being passed to the method. Pass
+ # a large number in case there's some weights on some invocation kinds (eg
+ # compiler to interpreter transitions).
+ ctx.default_run(
+ args, runtime_option=["-Xjitinitialsize:32M", "-Xjitthreshold:1000"])
diff --git a/test/656-annotation-lookup-generic-jni/check b/test/656-annotation-lookup-generic-jni/check
deleted file mode 100755
index e02c84d..0000000
--- a/test/656-annotation-lookup-generic-jni/check
+++ /dev/null
@@ -1,27 +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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# On gcstress configurations, an extra "JNI_OnUnload called" line may
-# be emitted. If so, remove it.
-sed -e '${/^JNI_OnUnload called$/d;}' "$2" > "$2.tmp"
-
-./default-check "$1" "$2.tmp" "$3" "$4"
diff --git a/test/656-annotation-lookup-generic-jni/run.py b/test/656-annotation-lookup-generic-jni/run.py
new file mode 100644
index 0000000..0ebb768
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # On gcstress configurations, an extra "JNI_OnUnload called" line may
+ # be emitted. If so, remove it.
+ ctx.run(fr"sed -i '/^JNI_OnUnload called$/d' '{args.stdout_file}'")
diff --git a/test/660-clinit/run b/test/660-clinit/run
deleted file mode 100644
index a0e79ee..0000000
--- a/test/660-clinit/run
+++ /dev/null
@@ -1,17 +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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --initialize-app-image-classes=true
diff --git a/test/660-clinit/run.py b/test/660-clinit/run.py
new file mode 100644
index 0000000..75c2bcb
--- /dev/null
+++ b/test/660-clinit/run.py
@@ -0,0 +1,22 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=["--initialize-app-image-classes=true"])
diff --git a/test/661-oat-writer-layout/run b/test/661-oat-writer-layout/run
deleted file mode 100644
index 087cd20..0000000
--- a/test/661-oat-writer-layout/run
+++ /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.
-
-# Always use the 'profile'.
-# Note that this test only works with --compiler-filter=speed
-# -- we accomplish this by blocklisting other compiler variants
-# and we also have to pass the option explicitly as dex2oat
-# defaults to speed-profile if a profile is specified.
-"${RUN}" "$@" --profile -Xcompiler-option --compiler-filter=speed
diff --git a/test/661-oat-writer-layout/run.py b/test/661-oat-writer-layout/run.py
new file mode 100644
index 0000000..838f3b1
--- /dev/null
+++ b/test/661-oat-writer-layout/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Always use the 'profile'.
+ # Note that this test only works with --compiler-filter=speed
+ # -- we accomplish this by blocklisting other compiler variants
+ # and we also have to pass the option explicitly as dex2oat
+ # defaults to speed-profile if a profile is specified.
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed"])
diff --git a/test/663-checker-select-generator/Android.bp b/test/663-checker-select-generator/Android.bp
new file mode 100644
index 0000000..c6b9087
--- /dev/null
+++ b/test/663-checker-select-generator/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `663-checker-select-generator`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-663-checker-select-generator",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-663-checker-select-generator-expected-stdout",
+ ":art-run-test-663-checker-select-generator-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-663-checker-select-generator-expected-stdout",
+ out: ["art-run-test-663-checker-select-generator-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-663-checker-select-generator-expected-stderr",
+ out: ["art-run-test-663-checker-select-generator-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/663-checker-select-generator/info.txt b/test/663-checker-select-generator/info.txt
index 792779f..6a5ac27 100644
--- a/test/663-checker-select-generator/info.txt
+++ b/test/663-checker-select-generator/info.txt
@@ -1,14 +1,26 @@
Test for select generation for conditional returns.
-Tests the rewriting from:
-
+For example rewrites a simpled diamond pattern e.g.:
If [ Condition ]
/ \
false branch true branch
- return FalseValue return TrueValue
+ \ /
+ Return Phi[FalseValue, TrueValue]
to:
-
true branch
false branch
return Select [FalseValue, TrueValue, Condition]
+
+It tests:
+* Simple diamond pattern with:
+ * Same value on each branch
+ * Different value
+* Double diamond pattern (i.e. nested simple diamonds) with:
+ * Same value
+ * All different values
+ * Same value in some cases but not all
+
+For all cases it tests:
+* Branches merging with a Phi.
+* Branches returning instead of having a Phi.
\ No newline at end of file
diff --git a/test/663-checker-select-generator/smali/TestCase.smali b/test/663-checker-select-generator/smali/TestCase.smali
deleted file mode 100644
index 844a9cf..0000000
--- a/test/663-checker-select-generator/smali/TestCase.smali
+++ /dev/null
@@ -1,72 +0,0 @@
-# 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.
-
-.class public LTestCase;
-
-.super Ljava/lang/Object;
-
-## CHECK-START: boolean TestCase.testCase(boolean) select_generator (before)
-## CHECK-DAG: <<Param:z\d+>> ParameterValue
-## CHECK-DAG: <<Int0:i\d+>> IntConstant 0
-## CHECK-DAG: <<Int1:i\d+>> IntConstant 1
-## CHECK-DAG: If [<<Param>>]
-## CHECK-DAG: Return [<<Int0>>]
-## CHECK-DAG: Return [<<Int1>>]
-
-## CHECK-START: boolean TestCase.testCase(boolean) select_generator (after)
-## CHECK-DAG: <<Param:z\d+>> ParameterValue
-## CHECK-DAG: <<Int0:i\d+>> IntConstant 0
-## CHECK-DAG: <<Int1:i\d+>> IntConstant 1
-## CHECK-DAG: <<Select:i\d+>> Select [<<Int0>>,<<Int1>>,<<Param>>]
-## CHECK-DAG: Return [<<Select>>]
-
-.method public static testCase(Z)Z
- .registers 1
-
- # The select generation will replace this with a select
- # instruction and a return.
- if-eqz v0, :else
- const v0, 0x1
- return v0
-
- :else
- const v0, 0x0
- return v0
-.end method
-
-
-## CHECK-START: java.lang.Object TestCase.referenceTypeTestCase(Main$Sub1, Main$Sub2, boolean) select_generator (before)
-## CHECK-DAG: <<Param0:l\d+>> ParameterValue
-## CHECK-DAG: <<Param1:l\d+>> ParameterValue
-## CHECK-DAG: <<Param2:z\d+>> ParameterValue
-## CHECK-DAG: If [<<Param2>>]
-## CHECK-DAG: Return [<<Param1>>]
-## CHECK-DAG: Return [<<Param0>>]
-
-## CHECK-START: java.lang.Object TestCase.referenceTypeTestCase(Main$Sub1, Main$Sub2, boolean) select_generator (after)
-## CHECK-DAG: <<Param0:l\d+>> ParameterValue
-## CHECK-DAG: <<Param1:l\d+>> ParameterValue
-## CHECK-DAG: <<Param2:z\d+>> ParameterValue
-## CHECK-DAG: <<Select:l\d+>> Select [<<Param1>>,<<Param0>>,<<Param2>>]
-## CHECK-DAG: Return [<<Select>>]
-
-.method public static referenceTypeTestCase(LMain$Sub1;LMain$Sub2;Z)Ljava/lang/Object;
- .registers 3
-
- if-eqz v2, :else
- return-object v0
-
- :else
- return-object v1
-.end method
diff --git a/test/663-checker-select-generator/src/Main.java b/test/663-checker-select-generator/src/Main.java
index c5c7a43..0ab3aef 100644
--- a/test/663-checker-select-generator/src/Main.java
+++ b/test/663-checker-select-generator/src/Main.java
@@ -14,49 +14,353 @@
* limitations under the License.
*/
-import java.lang.reflect.Method;
-
public class Main {
- public static class Super {}
- public static class Sub1 {}
- public static class Sub2 {}
+ // Check that we don't generate a select since we don't have no Phi (not even at the builder
+ // stage) since both values are the same.
- public static void assertTrue(boolean result) {
- if (!result) {
- throw new Error("Expected true");
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) builder (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) select_generator (before)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) select_generator (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+ private static int $noinline$testSimpleDiamondSameValue(boolean bool_param) {
+ int return_value;
+ if (bool_param) {
+ return_value = 10;
+ } else {
+ return_value = 10;
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a simple diamond pattern, with different values.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValue(boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Const10>>","<<Const20>>"])
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValue(boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool>>]
+ /// CHECK-DAG: Return [<<Select>>]
+ private static int $noinline$testSimpleDiamondDifferentValue(boolean bool_param) {
+ int return_value;
+ if (bool_param) {
+ return_value = 10;
+ } else {
+ return_value = 20;
+ }
+ return return_value;
+ }
+
+ // Check that we don't generate a select since we don't have no Phi (not even at the builder
+ // stage) since all values are the same.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) builder (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) select_generator (before)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) select_generator (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) select_generator (after)
+ /// CHECK-NOT: Select
+ private static int $noinline$testDoubleDiamondSameValue(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 10;
+ } else {
+ if (bool_param_2) {
+ return_value = 10;
+ } else {
+ return_value = 10;
+ }
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a double diamond pattern, with a different value in the outer branch.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuter(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>"]) == set(["<<Const10>>","<<Const20>>","<<Const20>>"])
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuter(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const20>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllOuter(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 10;
+ } else {
+ if (bool_param_2) {
+ return_value = 20;
+ } else {
+ return_value = 20;
+ }
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a double diamond pattern, with a different value in the inner branch.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInner(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>"]) == set(["<<Const10>>","<<Const20>>","<<Const20>>"])
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInner(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const20>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllInner(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 20;
+ } else {
+ if (bool_param_2) {
+ return_value = 10;
+ } else {
+ return_value = 20;
+ }
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a double diamond pattern, with a all different values.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValue(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>"]) == set(["<<Const10>>","<<Const20>>","<<Const30>>"])
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValue(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const30>>,<<Const20>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondDifferentValue(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 10;
+ } else {
+ if (bool_param_2) {
+ return_value = 20;
+ } else {
+ return_value = 30;
+ }
+ }
+ return return_value;
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
}
}
- public static void assertFalse(boolean result) {
- if (result) {
- throw new Error("Expected false");
+ // Check that we don't generate a select since we only have a single return.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: <<Const10:i\d+>> IntConstant 10
+ /// CHECK: Return [<<Const10>>]
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: Return
+ /// CHECK-NOT: Return
+
+ private static int $noinline$testSimpleDiamondSameValueWithReturn(boolean bool_param) {
+ if (bool_param) {
+ return 10;
+ } else {
+ return 10;
}
}
- public static void assertInstanceOfSub1(Object result) {
- if (!(result instanceof Sub1)) {
- throw new Error("Expected instance of Sub1");
+ // Same as testSimpleDiamondDifferentValue, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValueWithReturn(boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValueWithReturn(boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool>>]
+ /// CHECK-DAG: Return [<<Select>>]
+ private static int $noinline$testSimpleDiamondDifferentValueWithReturn(boolean bool_param) {
+ if (bool_param) {
+ return 10;
+ } else {
+ return 20;
}
}
- public static void assertInstanceOfSub2(Object result) {
- if (!(result instanceof Sub2)) {
- throw new Error("Expected instance of Sub2");
+ // Check that we don't generate a select since we only have a single return.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: <<Const10:i\d+>> IntConstant 10
+ /// CHECK: Return [<<Const10>>]
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: Return
+ /// CHECK-NOT: Return
+ private static int $noinline$testDoubleDiamondSameValueWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 10;
+ } else {
+ if (bool_param_2) {
+ return 10;
+ } else {
+ return 10;
+ }
+ }
+ }
+
+ // Same as testDoubleDiamondSameValueButNotAllOuter, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+
+ // Note that we have 2 returns instead of 3 as the two `return 20;` get merged into one before `select_generator`.
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK: Return
+ /// CHECK: Return
+ /// CHECK-NOT: Return
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 10;
+ } else {
+ if (bool_param_2) {
+ return 20;
+ } else {
+ return 20;
+ }
+ }
+ }
+
+ // Same as testDoubleDiamondSameValueButNotAllInner, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const20>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 20;
+ } else {
+ if (bool_param_2) {
+ return 10;
+ } else {
+ return 20;
+ }
+ }
+ }
+
+ // Same as testDoubleDiamondDifferentValue, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValueWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+ /// CHECK-DAG: Return [<<Const30>>]
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValueWithReturn(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const30>>,<<Const20>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondDifferentValueWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 10;
+ } else {
+ if (bool_param_2) {
+ return 20;
+ } else {
+ return 30;
+ }
}
}
public static void main(String[] args) throws Throwable {
- Class<?> c = Class.forName("TestCase");
- Method m = c.getMethod("testCase", boolean.class);
- Method m2 = c.getMethod("referenceTypeTestCase", Sub1.class, Sub2.class, boolean.class);
+ // With phi
+ assertEquals(10, $noinline$testSimpleDiamondSameValue(false));
+ assertEquals(20, $noinline$testSimpleDiamondDifferentValue(false));
+ assertEquals(10, $noinline$testDoubleDiamondSameValue(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllOuter(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllInner(false, false));
+ assertEquals(30, $noinline$testDoubleDiamondDifferentValue(false, false));
- try {
- assertTrue((Boolean) m.invoke(null, true));
- assertFalse((Boolean) m.invoke(null, false));
- assertInstanceOfSub1(m2.invoke(null, new Sub1(), new Sub2(), true));
- assertInstanceOfSub2(m2.invoke(null, new Sub1(), new Sub2(), false));
- } catch (Exception e) {
- throw new Error(e);
- }
+ // With return
+ assertEquals(10, $noinline$testSimpleDiamondSameValueWithReturn(false));
+ assertEquals(20, $noinline$testSimpleDiamondDifferentValueWithReturn(false));
+ assertEquals(10, $noinline$testDoubleDiamondSameValueWithReturn(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(false, false));
+ assertEquals(30, $noinline$testDoubleDiamondDifferentValueWithReturn(false, false));
}
}
diff --git a/test/663-odd-dex-size/run b/test/663-odd-dex-size/run
deleted file mode 100644
index 51777ca..0000000
--- a/test/663-odd-dex-size/run
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Run normally.
-${RUN} $@
-return_status1=$?
-
-# Run without cdex to trigger the unalignment in the vdex file.
-${RUN} ${@} -Xcompiler-option --compact-dex-level=none
-return_status2=$?
-
-(exit ${return_status1}) && (exit ${return_status2})
diff --git a/test/663-odd-dex-size/run.py b/test/663-odd-dex-size/run.py
new file mode 100644
index 0000000..635d23b
--- /dev/null
+++ b/test/663-odd-dex-size/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+
+def run(ctx, args):
+ # Run normally.
+ ctx.default_run(args)
+
+ # Run without cdex to trigger the unalignment in the vdex file.
+ ctx.default_run(args, Xcompiler_option=["--compact-dex-level=none"])
diff --git a/test/663-odd-dex-size2/build b/test/663-odd-dex-size2/build
deleted file mode 100644
index 5636558..0000000
--- a/test/663-odd-dex-size2/build
+++ /dev/null
@@ -1,17 +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.
-
-# Nothing to do
diff --git a/test/663-odd-dex-size2/build.py b/test/663-odd-dex-size2/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/663-odd-dex-size2/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/663-odd-dex-size3/build b/test/663-odd-dex-size3/build
deleted file mode 100644
index 5636558..0000000
--- a/test/663-odd-dex-size3/build
+++ /dev/null
@@ -1,17 +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.
-
-# Nothing to do
diff --git a/test/663-odd-dex-size3/build.py b/test/663-odd-dex-size3/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/663-odd-dex-size3/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/663-odd-dex-size4/build b/test/663-odd-dex-size4/build
deleted file mode 100644
index 5636558..0000000
--- a/test/663-odd-dex-size4/build
+++ /dev/null
@@ -1,17 +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.
-
-# Nothing to do
diff --git a/test/663-odd-dex-size4/build.py b/test/663-odd-dex-size4/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/663-odd-dex-size4/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run
deleted file mode 100755
index b7ce913..0000000
--- a/test/667-jit-jni-stub/run
+++ /dev/null
@@ -1,19 +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.
-
-# Disable AOT compilation of JNI stubs.
-# Ensure this test is not subject to unexpected code collection.
-${RUN} "${@}" --no-prebuild --runtime-option -Xjitinitialsize:32M
diff --git a/test/667-jit-jni-stub/run.py b/test/667-jit-jni-stub/run.py
new file mode 100644
index 0000000..e211a9b
--- /dev/null
+++ b/test/667-jit-jni-stub/run.py
@@ -0,0 +1,21 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Disable AOT compilation of JNI stubs.
+ # Ensure this test is not subject to unexpected code collection.
+ ctx.default_run(args, prebuild=False, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/670-bitstring-type-check/build b/test/670-bitstring-type-check/build
deleted file mode 100644
index 38307f2..0000000
--- a/test/670-bitstring-type-check/build
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Stop if something fails.
-set -e
-
-# Write out the source file.
-
-mkdir src
-cat >src/Main.java <<EOF
-/*
- * 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.
- */
-
-EOF
-
-for i in {0..8192}; do echo "class Level1Class$i { }" >>src/Main.java; done
-for i in {0..1024}; do echo "class Level2Class$i extends Level1Class0 { }" >>src/Main.java; done
-
-cat >>src/Main.java <<EOF
-class Level3Class0 extends Level2Class0 { }
-class Level4Class0 extends Level3Class0 { }
-class Level5Class0 extends Level4Class0 { }
-class Level6Class0 extends Level5Class0 { }
-class Level7Class0 extends Level6Class0 { }
-class Level8Class0 extends Level7Class0 { }
-class Level9Class0 extends Level8Class0 { }
-
-public class Main {
- public static void main(String[] args) throws Exception {
- // 8193 classes at level 1 make sure we shall have an overflow if there are 13 or
- // less bits for the level 1 character. 1025 classes at level 2 similarly guarantees
- // an overflow if the number of bits for level 2 character is 10 or less. To test
- // type checks also for the depth overflow, we provide a hierarchy 9 levels deep.
-
- // Make sure the bitstrings are initialized.
- for (int i = 0; i <= 8192; ++i) {
- Class.forName("Level1Class" + i).newInstance();
- }
- for (int i = 0; i <= 1024; ++i) {
- Class.forName("Level2Class" + i).newInstance();
- }
-
- // Note: Using a different class for tests so that verification of Main.main() does
- // not try to resolve classes used by the tests. This guarantees uninitialized type
- // check bitstrings when we enter Main.main() and start initializing them above.
- Helper.testInstanceOf();
- Helper.testCheckCast();
- }
-}
-
-class Helper {
- public static void testInstanceOf() throws Exception {
- for (int i = 1; i <= 9; ++i) {
- Object o = createInstance("Level" + i + "Class0");
- assertTrue(o instanceof Level1Class0);
- if (o instanceof Level2Class0) {
- assertFalse(i < 2);
- } else {
- assertTrue(i < 2);
- }
- if (o instanceof Level3Class0) {
- assertFalse(i < 3);
- } else {
- assertTrue(i < 3);
- }
- if (o instanceof Level4Class0) {
- assertFalse(i < 4);
- } else {
- assertTrue(i < 4);
- }
- if (o instanceof Level5Class0) {
- assertFalse(i < 5);
- } else {
- assertTrue(i < 5);
- }
- if (o instanceof Level6Class0) {
- assertFalse(i < 6);
- } else {
- assertTrue(i < 6);
- }
- if (o instanceof Level7Class0) {
- assertFalse(i < 7);
- } else {
- assertTrue(i < 7);
- }
- if (o instanceof Level8Class0) {
- assertFalse(i < 8);
- } else {
- assertTrue(i < 8);
- }
- if (o instanceof Level9Class0) {
- assertFalse(i < 9);
- } else {
- assertTrue(i < 9);
- }
- }
-
- assertTrue(createInstance("Level1Class8192") instanceof Level1Class8192);
- assertFalse(createInstance("Level1Class8192") instanceof Level1Class0);
- assertTrue(createInstance("Level2Class1024") instanceof Level2Class1024);
- assertTrue(createInstance("Level2Class1024") instanceof Level1Class0);
- assertFalse(createInstance("Level2Class1024") instanceof Level2Class0);
- }
-
- public static void testCheckCast() throws Exception {
- for (int i = 1; i <= 9; ++i) {
- Object o = createInstance("Level" + i + "Class0");
- Level1Class0 l1c0 = (Level1Class0) o;
- try {
- Level2Class0 l2c0 = (Level2Class0) o;
- assertFalse(i < 2);
- } catch (ClassCastException cce) {
- assertTrue(i < 2);
- }
- try {
- Level3Class0 l3c0 = (Level3Class0) o;
- assertFalse(i < 3);
- } catch (ClassCastException cce) {
- assertTrue(i < 3);
- }
- try {
- Level4Class0 l4c0 = (Level4Class0) o;
- assertFalse(i < 4);
- } catch (ClassCastException cce) {
- assertTrue(i < 4);
- }
- try {
- Level5Class0 l5c0 = (Level5Class0) o;
- assertFalse(i < 5);
- } catch (ClassCastException cce) {
- assertTrue(i < 5);
- }
- try {
- Level6Class0 l6c0 = (Level6Class0) o;
- assertFalse(i < 6);
- } catch (ClassCastException cce) {
- assertTrue(i < 6);
- }
- try {
- Level7Class0 l7c0 = (Level7Class0) o;
- assertFalse(i < 7);
- } catch (ClassCastException cce) {
- assertTrue(i < 7);
- }
- try {
- Level8Class0 l8c0 = (Level8Class0) o;
- assertFalse(i < 8);
- } catch (ClassCastException cce) {
- assertTrue(i < 8);
- }
- try {
- Level9Class0 l9c0 = (Level9Class0) o;
- assertFalse(i < 9);
- } catch (ClassCastException cce) {
- assertTrue(i < 9);
- }
- }
-
- Level1Class8192 l1c8192 = (Level1Class8192) createInstance("Level1Class8192");
- try {
- Level1Class0 l1c0 = (Level1Class0) createInstance("Level1Class8192");
- throw new AssertionError("Unexpected");
- } catch (ClassCastException expected) {}
- Level2Class1024 l2c1024 = (Level2Class1024) createInstance("Level2Class1024");
- Level1Class0 l1c0 = (Level1Class0) createInstance("Level2Class1024");
- try {
- Level2Class0 l2c0 = (Level2Class0) createInstance("Level2Class1024");
- throw new AssertionError("Unexpected");
- } catch (ClassCastException expected) {}
- }
-
- public static Object createInstance(String className) throws Exception {
- return Class.forName(className).newInstance();
- }
-
- public static void assertTrue(boolean value) throws Exception {
- if (!value) {
- throw new AssertionError();
- }
- }
-
- public static void assertFalse(boolean value) throws Exception {
- if (value) {
- throw new AssertionError();
- }
- }
-}
-EOF
-
-./default-build "$@"
diff --git a/test/670-bitstring-type-check/build.py b/test/670-bitstring-type-check/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/670-bitstring-type-check/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/670-bitstring-type-check/generate-sources b/test/670-bitstring-type-check/generate-sources
new file mode 100755
index 0000000..4d88839
--- /dev/null
+++ b/test/670-bitstring-type-check/generate-sources
@@ -0,0 +1,214 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# Write out the source file.
+
+mkdir src
+cat >src/Main.java <<EOF
+/*
+ * 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.
+ */
+
+EOF
+
+for i in {0..8192}; do echo "class Level1Class$i { }" >>src/Main.java; done
+for i in {0..1024}; do echo "class Level2Class$i extends Level1Class0 { }" >>src/Main.java; done
+
+cat >>src/Main.java <<EOF
+class Level3Class0 extends Level2Class0 { }
+class Level4Class0 extends Level3Class0 { }
+class Level5Class0 extends Level4Class0 { }
+class Level6Class0 extends Level5Class0 { }
+class Level7Class0 extends Level6Class0 { }
+class Level8Class0 extends Level7Class0 { }
+class Level9Class0 extends Level8Class0 { }
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ // 8193 classes at level 1 make sure we shall have an overflow if there are 13 or
+ // less bits for the level 1 character. 1025 classes at level 2 similarly guarantees
+ // an overflow if the number of bits for level 2 character is 10 or less. To test
+ // type checks also for the depth overflow, we provide a hierarchy 9 levels deep.
+
+ // Make sure the bitstrings are initialized.
+ for (int i = 0; i <= 8192; ++i) {
+ Class.forName("Level1Class" + i).newInstance();
+ }
+ for (int i = 0; i <= 1024; ++i) {
+ Class.forName("Level2Class" + i).newInstance();
+ }
+
+ // Note: Using a different class for tests so that verification of Main.main() does
+ // not try to resolve classes used by the tests. This guarantees uninitialized type
+ // check bitstrings when we enter Main.main() and start initializing them above.
+ Helper.testInstanceOf();
+ Helper.testCheckCast();
+ }
+}
+
+class Helper {
+ public static void testInstanceOf() throws Exception {
+ for (int i = 1; i <= 9; ++i) {
+ Object o = createInstance("Level" + i + "Class0");
+ assertTrue(o instanceof Level1Class0);
+ if (o instanceof Level2Class0) {
+ assertFalse(i < 2);
+ } else {
+ assertTrue(i < 2);
+ }
+ if (o instanceof Level3Class0) {
+ assertFalse(i < 3);
+ } else {
+ assertTrue(i < 3);
+ }
+ if (o instanceof Level4Class0) {
+ assertFalse(i < 4);
+ } else {
+ assertTrue(i < 4);
+ }
+ if (o instanceof Level5Class0) {
+ assertFalse(i < 5);
+ } else {
+ assertTrue(i < 5);
+ }
+ if (o instanceof Level6Class0) {
+ assertFalse(i < 6);
+ } else {
+ assertTrue(i < 6);
+ }
+ if (o instanceof Level7Class0) {
+ assertFalse(i < 7);
+ } else {
+ assertTrue(i < 7);
+ }
+ if (o instanceof Level8Class0) {
+ assertFalse(i < 8);
+ } else {
+ assertTrue(i < 8);
+ }
+ if (o instanceof Level9Class0) {
+ assertFalse(i < 9);
+ } else {
+ assertTrue(i < 9);
+ }
+ }
+
+ assertTrue(createInstance("Level1Class8192") instanceof Level1Class8192);
+ assertFalse(createInstance("Level1Class8192") instanceof Level1Class0);
+ assertTrue(createInstance("Level2Class1024") instanceof Level2Class1024);
+ assertTrue(createInstance("Level2Class1024") instanceof Level1Class0);
+ assertFalse(createInstance("Level2Class1024") instanceof Level2Class0);
+ }
+
+ public static void testCheckCast() throws Exception {
+ for (int i = 1; i <= 9; ++i) {
+ Object o = createInstance("Level" + i + "Class0");
+ Level1Class0 l1c0 = (Level1Class0) o;
+ try {
+ Level2Class0 l2c0 = (Level2Class0) o;
+ assertFalse(i < 2);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 2);
+ }
+ try {
+ Level3Class0 l3c0 = (Level3Class0) o;
+ assertFalse(i < 3);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 3);
+ }
+ try {
+ Level4Class0 l4c0 = (Level4Class0) o;
+ assertFalse(i < 4);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 4);
+ }
+ try {
+ Level5Class0 l5c0 = (Level5Class0) o;
+ assertFalse(i < 5);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 5);
+ }
+ try {
+ Level6Class0 l6c0 = (Level6Class0) o;
+ assertFalse(i < 6);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 6);
+ }
+ try {
+ Level7Class0 l7c0 = (Level7Class0) o;
+ assertFalse(i < 7);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 7);
+ }
+ try {
+ Level8Class0 l8c0 = (Level8Class0) o;
+ assertFalse(i < 8);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 8);
+ }
+ try {
+ Level9Class0 l9c0 = (Level9Class0) o;
+ assertFalse(i < 9);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 9);
+ }
+ }
+
+ Level1Class8192 l1c8192 = (Level1Class8192) createInstance("Level1Class8192");
+ try {
+ Level1Class0 l1c0 = (Level1Class0) createInstance("Level1Class8192");
+ throw new AssertionError("Unexpected");
+ } catch (ClassCastException expected) {}
+ Level2Class1024 l2c1024 = (Level2Class1024) createInstance("Level2Class1024");
+ Level1Class0 l1c0 = (Level1Class0) createInstance("Level2Class1024");
+ try {
+ Level2Class0 l2c0 = (Level2Class0) createInstance("Level2Class1024");
+ throw new AssertionError("Unexpected");
+ } catch (ClassCastException expected) {}
+ }
+
+ public static Object createInstance(String className) throws Exception {
+ return Class.forName(className).newInstance();
+ }
+
+ public static void assertTrue(boolean value) throws Exception {
+ if (!value) {
+ throw new AssertionError();
+ }
+ }
+
+ public static void assertFalse(boolean value) throws Exception {
+ if (value) {
+ throw new AssertionError();
+ }
+ }
+}
+EOF
diff --git a/test/670-bitstring-type-check/run b/test/670-bitstring-type-check/run
deleted file mode 100644
index a189dc5..0000000
--- a/test/670-bitstring-type-check/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# This test can take 7-11 mins, so raise the default 10 min timeout.
-export ART_TIME_OUT_MULTIPLIER=2
-
-exec ${RUN} "$@"
diff --git a/test/670-bitstring-type-check/run.py b/test/670-bitstring-type-check/run.py
new file mode 100644
index 0000000..5bfe969
--- /dev/null
+++ b/test/670-bitstring-type-check/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 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.
+
+
+def run(ctx, args):
+ # This test can take 7-11 mins, so raise the default 10 min timeout.
+ ctx.env.ART_TIME_OUT_MULTIPLIER = 2
+
+ ctx.default_run(args)
diff --git a/test/674-HelloWorld-Dm/run b/test/674-HelloWorld-Dm/run
deleted file mode 100644
index b8a61c5..0000000
--- a/test/674-HelloWorld-Dm/run
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-${RUN} --dex2oat-dm "${@}"
-return_status1=$?
-
-${RUN} --runtime-dm "${@}"
-return_status2=$?
-
-(exit ${return_status1}) && (exit ${return_status2})
diff --git a/test/674-HelloWorld-Dm/run.py b/test/674-HelloWorld-Dm/run.py
new file mode 100644
index 0000000..9945fec
--- /dev/null
+++ b/test/674-HelloWorld-Dm/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, dex2oat_dm=True)
+
+ ctx.default_run(args, runtime_dm=True)
diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build
deleted file mode 100644
index 330a6de..0000000
--- a/test/674-hiddenapi/build
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-set -e
-
-# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
-# a second time without to create a normal jar. We need to do this because we
-# want to load the jar once as an app module and once as a member of the boot
-# class path. The DexFileVerifier would fail on the former as it does not allow
-# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
-# class path dex files, so the boot jar loads fine in the latter case.
-
-export USE_HIDDENAPI=true
-./default-build "$@"
-
-# Move the jar file into the resource folder to be bundled with the test.
-mkdir res
-mv ${TEST_NAME}.jar res/boot.jar
-
-# Clear all intermediate files otherwise default-build would either skip
-# compilation or fail rebuilding.
-rm -rf classes*
-
-export USE_HIDDENAPI=false
-./default-build "$@"
diff --git a/test/674-hiddenapi/build.py b/test/674-hiddenapi/build.py
new file mode 100644
index 0000000..c3ebb50
--- /dev/null
+++ b/test/674-hiddenapi/build.py
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2022 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 os
+
+# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
+# a second time without to create a normal jar. We need to do this because we
+# want to load the jar once as an app module and once as a member of the boot
+# class path. The DexFileVerifier would fail on the former as it does not allow
+# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
+# class path dex files, so the boot jar loads fine in the latter case.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build(use_hiddenapi=True)
+
+ # Move the jar file into the resource folder to be bundled with the test.
+ os.mkdir(ctx.test_dir / "res")
+ os.rename(ctx.test_dir / "674-hiddenapi.jar", ctx.test_dir / "res/boot.jar")
+
+ # Clear all intermediate files otherwise default-build would either skip
+ # compilation or fail rebuilding.
+ ctx.bash("rm -rf classes*")
+
+ ctx.default_build(use_hiddenapi=False)
diff --git a/test/674-hiddenapi/check b/test/674-hiddenapi/check
deleted file mode 100644
index c8afc22..0000000
--- a/test/674-hiddenapi/check
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Remove pid and date from the log messages.
-grep -v JNI_OnLoad "$2" \
- | grep -v JNI_OnUnload \
- > "$2.tmp"
-grep -vE '^dalvikvm(32|64) E [^]]+]' "$4" \
- > "$4.tmp"
-
-./default-check "$1" "$2.tmp" "$3" "$4.tmp"
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index ebe9d10..5fa2532 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -15,13 +15,10 @@
*/
#include "base/sdk_version.h"
-#include "class_linker.h"
#include "dex/art_dex_file_loader.h"
#include "hidden_api.h"
#include "jni.h"
#include "runtime.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread.h"
#include "ti-agent/scoped_utf_chars.h"
namespace art {
@@ -77,10 +74,7 @@
Java_Main_setDexDomain(env, klass, int_index, is_core_platform);
- ScopedObjectAccess soa(Thread::Current());
- for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
- Runtime::Current()->GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get());
- }
+ Runtime::Current()->AppendToBootClassPath(path, path, opened_dex_files[index]);
return int_index;
}
diff --git a/test/674-hiddenapi/run b/test/674-hiddenapi/run
deleted file mode 100755
index 0ab4763..0000000
--- a/test/674-hiddenapi/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Make verification soft fail so that we can re-verify boot classpath
-# methods at runtime.
-#
-# N.B. Compilation of secondary dexes can prevent hidden API checks, e.g. if
-# a blocklisted field get is inlined.
-exec ${RUN} $@ --verify-soft-fail --no-secondary-compilation
diff --git a/test/674-hiddenapi/run.py b/test/674-hiddenapi/run.py
new file mode 100644
index 0000000..1e364fa
--- /dev/null
+++ b/test/674-hiddenapi/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ # Make verification soft fail so that we can re-verify boot classpath
+ # methods at runtime.
+ #
+ # N.B. Compilation of secondary dexes can prevent hidden API checks, e.g. if
+ # a blocklisted field get is inlined.
+ ctx.default_run(args, verify_soft_fail=True, secondary_compilation=False)
+
+ ctx.run(fr"sed -i -E '/(JNI_OnLoad|JNI_OnUnload)/d' '{args.stdout_file}'")
+ ctx.run(fr"sed -i -E '/^dalvikvm(32|64) E [^]]+]/d' '{args.stderr_file}'")
diff --git a/test/674-hotness-compiled/run b/test/674-hotness-compiled/run
deleted file mode 100755
index 85e8e3b..0000000
--- a/test/674-hotness-compiled/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-${RUN} "$@" -Xcompiler-option --count-hotness-in-compiled-code
diff --git a/test/674-hotness-compiled/run.py b/test/674-hotness-compiled/run.py
new file mode 100644
index 0000000..aca5b13
--- /dev/null
+++ b/test/674-hotness-compiled/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, Xcompiler_option=["--count-hotness-in-compiled-code"])
diff --git a/test/674-vdex-uncompress/build b/test/674-vdex-uncompress/build
deleted file mode 100755
index 7b1804d..0000000
--- a/test/674-vdex-uncompress/build
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Uncompress and align the dex files so that dex2oat will not copy the dex
-# code to the .vdex file.
-./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/674-vdex-uncompress/build.py b/test/674-vdex-uncompress/build.py
new file mode 100644
index 0000000..cbe1f62
--- /dev/null
+++ b/test/674-vdex-uncompress/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Uncompress and align the dex files so that dex2oat will not copy the dex
+# code to the .vdex file.
+def build(ctx):
+ ctx.default_build(zip_compression_method="store", zip_align_bytes="4")
diff --git a/test/674-vdex-uncompress/run b/test/674-vdex-uncompress/run
deleted file mode 100644
index edf699f..0000000
--- a/test/674-vdex-uncompress/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/674-vdex-uncompress/run.py b/test/674-vdex-uncompress/run.py
new file mode 100644
index 0000000..7684cbd
--- /dev/null
+++ b/test/674-vdex-uncompress/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, Xcompiler_option=["--compiler-filter=verify"], vdex=True)
diff --git a/test/676-proxy-jit-at-first-use/run b/test/676-proxy-jit-at-first-use/run
deleted file mode 100644
index 16c9f76..0000000
--- a/test/676-proxy-jit-at-first-use/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Enable "jit at first use" (-Xjitthreshold:0).
-# Ensure this test is not subject to unexpected code collection.
-${RUN} "${@}" --runtime-option -Xjitthreshold:0 --runtime-option -Xjitinitialsize:32M
diff --git a/test/676-proxy-jit-at-first-use/run.py b/test/676-proxy-jit-at-first-use/run.py
new file mode 100644
index 0000000..4687094
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Enable "jit at first use" (-Xjitthreshold:0).
+ # Ensure this test is not subject to unexpected code collection.
+ ctx.default_run(
+ args, runtime_option=["-Xjitthreshold:0", "-Xjitinitialsize:32M"])
diff --git a/test/676-resolve-field-type/build.py b/test/676-resolve-field-type/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/676-resolve-field-type/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/676-resolve-field-type/test-metadata.json b/test/676-resolve-field-type/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/676-resolve-field-type/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/677-fsi/build b/test/677-fsi/build
deleted file mode 100755
index b90b408..0000000
--- a/test/677-fsi/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/677-fsi/build.py b/test/677-fsi/build.py
new file mode 100644
index 0000000..69afbf3
--- /dev/null
+++ b/test/677-fsi/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(zip_compression_method="store", zip_align_bytes=4)
diff --git a/test/677-fsi/check b/test/677-fsi/check
deleted file mode 100644
index 8c7f18b..0000000
--- a/test/677-fsi/check
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Only keep the lines we're interested in.
-sed -s '/Hello World/!d' "$2" > "$2.tmp"
-sed -s '/^.*: oat file has dex code, but APK has uncompressed dex code/!d' "$4" > "$4.tmp"
-
-# Remove part of message containing filename.
-sed -s 's/^.*: //' "$4.tmp" > "$4.tmp2"
-
-diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp2" >/dev/null
diff --git a/test/677-fsi/run b/test/677-fsi/run
deleted file mode 100644
index 30d925e..0000000
--- a/test/677-fsi/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Redirect logger to stderr, as the test relies on error
-# messages being printed there.
-exec ${RUN} $@ -Xcompiler-option --copy-dex-files=always --runtime-option -Xonly-use-system-oat-files --runtime-option -Xuse-stderr-logger
diff --git a/test/677-fsi/run.py b/test/677-fsi/run.py
new file mode 100644
index 0000000..357692f
--- /dev/null
+++ b/test/677-fsi/run.py
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Redirect logger to stderr, as the test relies on error
+ # messages being printed there.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--copy-dex-files=always"],
+ runtime_option=["-Xonly-use-system-oat-files", "-Xuse-stderr-logger"])
+
+ # Only keep the lines we're interested in.
+ ctx.run(fr"sed -i '/Hello World/!d' '{args.stdout_file}'")
+ ctx.run(
+ fr"sed -i '/^.*: oat file has dex code, but APK has uncompressed dex code/!d' '{args.stderr_file}'"
+ )
+
+ # Remove part of message containing filename.
+ ctx.run(fr"sed -i 's/^.*: //' '{args.stderr_file}'")
diff --git a/test/677-fsi2/run b/test/677-fsi2/run
deleted file mode 100644
index 651f082..0000000
--- a/test/677-fsi2/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-${RUN} $@ --runtime-option -Xonly-use-system-oat-files
diff --git a/test/677-fsi2/run.py b/test/677-fsi2/run.py
new file mode 100644
index 0000000..0bbabb9
--- /dev/null
+++ b/test/677-fsi2/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, runtime_option=["-Xonly-use-system-oat-files"])
diff --git a/test/678-quickening/run b/test/678-quickening/run
deleted file mode 100644
index 0cc87f3..0000000
--- a/test/678-quickening/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.i
-
-# Run without an app image to prevent the class NotLoaded to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/678-quickening/run.py b/test/678-quickening/run.py
new file mode 100644
index 0000000..ea7a2bb
--- /dev/null
+++ b/test/678-quickening/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.i
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the class NotLoaded to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/679-locks/run b/test/679-locks/run
deleted file mode 100644
index 0cc87f3..0000000
--- a/test/679-locks/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.i
-
-# Run without an app image to prevent the class NotLoaded to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/679-locks/run.py b/test/679-locks/run.py
new file mode 100644
index 0000000..ea7a2bb
--- /dev/null
+++ b/test/679-locks/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.i
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the class NotLoaded to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/683-clinit-inline-static-invoke/src-multidex/MyCalendarUtils.java b/test/683-clinit-inline-static-invoke/src-multidex/MyCalendarUtils.java
new file mode 100644
index 0000000..fc79e32
--- /dev/null
+++ b/test/683-clinit-inline-static-invoke/src-multidex/MyCalendarUtils.java
@@ -0,0 +1,22 @@
+/*
+ * 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 sun.util.calendar.CalendarUtils;
+
+public abstract class MyCalendarUtils extends CalendarUtils {
+ // Reference to MyCalendarUtils.isJulianLeapYear() shall resolve to
+ // CalendarUtils.isJulianLeapYear() which should be easily inlined.
+}
diff --git a/test/683-clinit-inline-static-invoke/src-multidex/MyModifier.java b/test/683-clinit-inline-static-invoke/src-multidex/MyModifier.java
deleted file mode 100644
index bc01940..0000000
--- a/test/683-clinit-inline-static-invoke/src-multidex/MyModifier.java
+++ /dev/null
@@ -1,22 +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.
- */
-
-import java.lang.reflect.Modifier;
-
-public abstract class MyModifier extends Modifier {
- // Reference to MyModifier.classModifiers() shall resolve to
- // Modifier.classModifiers() which should be easily inlined.
-}
diff --git a/test/683-clinit-inline-static-invoke/src/Main.java b/test/683-clinit-inline-static-invoke/src/Main.java
index 6f15e27..d222539 100644
--- a/test/683-clinit-inline-static-invoke/src/Main.java
+++ b/test/683-clinit-inline-static-invoke/src/Main.java
@@ -26,6 +26,6 @@
// TypeId in the current DexFile, we erroneously provided the type index from the
// declaring DexFile and that caused a crash. This was fixed by changing the
// ClinitCheck entrypoint to take the Class reference from LoadClass.
- int placeholder = MyModifier.classModifiers();
+ boolean placeholder = MyCalendarUtils.isJulianLeapYear(-43);
}
}
diff --git a/test/688-shared-library/check b/test/688-shared-library/check
deleted file mode 100644
index 8501835..0000000
--- a/test/688-shared-library/check
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Finalizers of DexFile will complain not being able to close
-# the main dex file, as it's still open. That's OK to ignore.
-# Oat file manager will also complain about duplicate dex files. Ignore.
-sed -e '/^E\/System/d' "$4" | sed -e '/.*oat_file_manager.*/d' > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/688-shared-library/run b/test/688-shared-library/run
deleted file mode 100644
index fa6ab58..0000000
--- a/test/688-shared-library/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# App images are incompatible with what the test is doing: loading one
-# dex file multiple times.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/688-shared-library/run.py b/test/688-shared-library/run.py
new file mode 100644
index 0000000..f71e350
--- /dev/null
+++ b/test/688-shared-library/run.py
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # App images are incompatible with what the test is doing: loading one
+ # dex file multiple times.
+ ctx.default_run(args, app_image=False)
+
+ # Finalizers of DexFile will complain not being able to close
+ # the main dex file, as it's still open. That's OK to ignore.
+ # Oat file manager will also complain about duplicate dex files. Ignore.
+ ctx.run(
+ fr"sed -i -e '/^E\/System/d' -e '/.*oat_file_manager.*/d' '{args.stderr_file}'"
+ )
diff --git a/test/689-zygote-jit-deopt/build.py b/test/689-zygote-jit-deopt/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/689-zygote-jit-deopt/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/689-zygote-jit-deopt/run b/test/689-zygote-jit-deopt/run
deleted file mode 100644
index 7b4b7eb..0000000
--- a/test/689-zygote-jit-deopt/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-./default-run "$@" --zygote
diff --git a/test/689-zygote-jit-deopt/run.py b/test/689-zygote-jit-deopt/run.py
new file mode 100644
index 0000000..3c4f0fe
--- /dev/null
+++ b/test/689-zygote-jit-deopt/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, zygote=True)
diff --git a/test/689-zygote-jit-deopt/test-metadata.json b/test/689-zygote-jit-deopt/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/689-zygote-jit-deopt/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/690-hiddenapi-same-name-methods/build b/test/690-hiddenapi-same-name-methods/build
deleted file mode 100644
index c364b3b..0000000
--- a/test/690-hiddenapi-same-name-methods/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/690-hiddenapi-same-name-methods/build.py b/test/690-hiddenapi-same-name-methods/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/691-hiddenapi-proxy/build b/test/691-hiddenapi-proxy/build
deleted file mode 100644
index c364b3b..0000000
--- a/test/691-hiddenapi-proxy/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/691-hiddenapi-proxy/build.py b/test/691-hiddenapi-proxy/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/691-hiddenapi-proxy/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/692-vdex-inmem-loader/build.py b/test/692-vdex-inmem-loader/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/692-vdex-inmem-loader/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/692-vdex-inmem-loader/test-metadata.json b/test/692-vdex-inmem-loader/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/692-vdex-inmem-loader/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
index 1c7b6e8..a478f26 100644
--- a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
+++ b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
@@ -45,7 +45,7 @@
std::vector<const DexFile*> dex_files;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
dex_files.push_back(dex_file);
@@ -84,7 +84,7 @@
std::vector<const DexFile*> dex_files;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
dex_files.push_back(dex_file);
@@ -116,7 +116,7 @@
bool all_backed_by_oat = false;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
bool is_backed_by_oat = (dex_file->GetOatDexFile() != nullptr);
@@ -141,7 +141,7 @@
std::vector<const DexFile*> dex_files;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
dex_files.push_back(dex_file);
diff --git a/test/692-vdex-secondary-loader/build.py b/test/692-vdex-secondary-loader/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/692-vdex-secondary-loader/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/692-vdex-secondary-loader/run b/test/692-vdex-secondary-loader/run
deleted file mode 100644
index 35b55d6..0000000
--- a/test/692-vdex-secondary-loader/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Disable dex2oat of secondary dex files.
-${RUN} "$@" --no-secondary-compilation
-return_status1=$?
-
-# Set low RAM to hit the Madvise code which used to crash
-${RUN} "$@" --runtime-option -XX:LowMemoryMode --no-secondary-compilation
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/692-vdex-secondary-loader/run.py b/test/692-vdex-secondary-loader/run.py
new file mode 100644
index 0000000..7c9de5c
--- /dev/null
+++ b/test/692-vdex-secondary-loader/run.py
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+
+def run(ctx, args):
+ # Disable dex2oat of secondary dex files.
+ ctx.default_run(args, secondary_compilation=False)
+
+ # Set low RAM to hit the Madvise code which used to crash
+ ctx.default_run(
+ args, runtime_option=["-XX:LowMemoryMode"], secondary_compilation=False)
diff --git a/test/692-vdex-secondary-loader/test-metadata.json b/test/692-vdex-secondary-loader/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/692-vdex-secondary-loader/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/693-vdex-inmem-loader-evict/build.py b/test/693-vdex-inmem-loader-evict/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/693-vdex-inmem-loader-evict/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/693-vdex-inmem-loader-evict/test-metadata.json b/test/693-vdex-inmem-loader-evict/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/693-vdex-inmem-loader-evict/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/695-simplify-throws/src/Main.java b/test/695-simplify-throws/src/Main.java
index 2799624..9a95d80 100644
--- a/test/695-simplify-throws/src/Main.java
+++ b/test/695-simplify-throws/src/Main.java
@@ -15,31 +15,31 @@
*/
public class Main {
- public static boolean alwaysThrows() {
- throw new Error("");
- }
-
- public static void test() {
- alwaysThrows();
- while (condition) {
- int a = 2;
- while (otherCondition) {
- a = 3;
- }
- staticField = a;
+ public static boolean alwaysThrows() {
+ throw new Error("");
}
- }
- public static void main(String[] args) throws Exception {
- try {
- test();
- throw new Exception("Unexpected exception");
- } catch (Error e) {
- // Expected.
+ public static void test() {
+ alwaysThrows();
+ while (condition) {
+ int a = 2;
+ while (otherCondition) {
+ a = 3;
+ }
+ staticField = a;
+ }
}
- }
- static boolean condition = false;
- static boolean otherCondition = false;
- static int staticField = 1;
+ public static void main(String[] args) throws Exception {
+ try {
+ test();
+ throw new Exception("Unexpected exception");
+ } catch (Error e) {
+ // Expected.
+ }
+ }
+
+ static boolean condition = false;
+ static boolean otherCondition = false;
+ static int staticField = 1;
}
diff --git a/test/696-loop/src/Main.java b/test/696-loop/src/Main.java
index b92166a..9763a8e 100644
--- a/test/696-loop/src/Main.java
+++ b/test/696-loop/src/Main.java
@@ -15,24 +15,24 @@
*/
public class Main {
- static int[] sA = new int[12];
- static int a = 1;
+ static int[] sA = new int[12];
+ static int a = 1;
- static void doIt(int n) {
- for (int i = 0; i < 2; i++) {
- n+=a;
+ static void doIt(int n) {
+ for (int i = 0; i < 2; i++) {
+ n += a;
+ }
+ for (int i = 0; i < n; i++) {
+ sA[i] += 1;
+ }
}
- for (int i = 0; i < n; i++) {
- sA[i] += 1;
- }
- }
- public static void main(String[] args) {
- doIt(10);
- for (int i = 0; i < sA.length; i++) {
- if (sA[i] != 1) {
- throw new Error("Expected 1, got " + sA[i]);
- }
+ public static void main(String[] args) {
+ doIt(10);
+ for (int i = 0; i < sA.length; i++) {
+ if (sA[i] != 1) {
+ throw new Error("Expected 1, got " + sA[i]);
+ }
+ }
}
- }
}
diff --git a/test/697-checker-string-append/src/Main.java b/test/697-checker-string-append/src/Main.java
index c63c328..e35986a 100644
--- a/test/697-checker-string-append/src/Main.java
+++ b/test/697-checker-string-append/src/Main.java
@@ -18,6 +18,9 @@
public static void main(String[] args) {
testAppendStringAndLong();
testAppendStringAndInt();
+ testAppendStringAndFloat();
+ testAppendStringAndDouble();
+ testAppendDoubleAndFloat();
testAppendStringAndString();
testMiscelaneous();
testNoArgs();
@@ -186,6 +189,159 @@
}
}
+ private static final String APPEND_FLOAT_PREFIX = "Float/";
+ private static final String[] APPEND_FLOAT_TEST_CASES = {
+ // We're testing only exact values here, i.e. values that do not require rounding.
+ "Float/1.0",
+ "Float/9.0",
+ "Float/10.0",
+ "Float/99.0",
+ "Float/100.0",
+ "Float/999.0",
+ "Float/1000.0",
+ "Float/9999.0",
+ "Float/10000.0",
+ "Float/99999.0",
+ "Float/100000.0",
+ "Float/999999.0",
+ "Float/1000000.0",
+ "Float/9999999.0",
+ "Float/1.0E7",
+ "Float/1.0E10",
+ "Float/-1.0",
+ "Float/-9.0",
+ "Float/-10.0",
+ "Float/-99.0",
+ "Float/-100.0",
+ "Float/-999.0",
+ "Float/-1000.0",
+ "Float/-9999.0",
+ "Float/-10000.0",
+ "Float/-99999.0",
+ "Float/-100000.0",
+ "Float/-999999.0",
+ "Float/-1000000.0",
+ "Float/-9999999.0",
+ "Float/-1.0E7",
+ "Float/-1.0E10",
+ "Float/0.25",
+ "Float/1.625",
+ "Float/9.3125",
+ "Float/-0.25",
+ "Float/-1.625",
+ "Float/-9.3125",
+ };
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndFloat(java.lang.String, float) instruction_simplifier (before)
+ /// CHECK-NOT: StringBuilderAppend
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndFloat(java.lang.String, float) instruction_simplifier (after)
+ /// CHECK: StringBuilderAppend
+ public static String $noinline$appendStringAndFloat(String s, float f) {
+ return new StringBuilder().append(s).append(f).toString();
+ }
+
+ public static void testAppendStringAndFloat() {
+ for (String expected : APPEND_FLOAT_TEST_CASES) {
+ float f = Float.valueOf(expected.substring(APPEND_FLOAT_PREFIX.length()));
+ String result = $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, f);
+ assertEquals(expected, result);
+ }
+ // Special values.
+ assertEquals("Float/NaN", $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, Float.NaN));
+ assertEquals("Float/Infinity",
+ $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, Float.POSITIVE_INFINITY));
+ assertEquals("Float/-Infinity",
+ $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, Float.NEGATIVE_INFINITY));
+ }
+
+ private static final String APPEND_DOUBLE_PREFIX = "Double/";
+ private static final String[] APPEND_DOUBLE_TEST_CASES = {
+ // We're testing only exact values here, i.e. values that do not require rounding.
+ "Double/1.0",
+ "Double/9.0",
+ "Double/10.0",
+ "Double/99.0",
+ "Double/100.0",
+ "Double/999.0",
+ "Double/1000.0",
+ "Double/9999.0",
+ "Double/10000.0",
+ "Double/99999.0",
+ "Double/100000.0",
+ "Double/999999.0",
+ "Double/1000000.0",
+ "Double/9999999.0",
+ "Double/1.0E7",
+ "Double/1.0E24",
+ "Double/-1.0",
+ "Double/-9.0",
+ "Double/-10.0",
+ "Double/-99.0",
+ "Double/-100.0",
+ "Double/-999.0",
+ "Double/-1000.0",
+ "Double/-9999.0",
+ "Double/-10000.0",
+ "Double/-99999.0",
+ "Double/-100000.0",
+ "Double/-999999.0",
+ "Double/-1000000.0",
+ "Double/-9999999.0",
+ "Double/-1.0E7",
+ "Double/-1.0E24",
+ "Double/0.25",
+ "Double/1.625",
+ "Double/9.3125",
+ "Double/-0.25",
+ "Double/-1.625",
+ "Double/-9.3125",
+ };
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndDouble(java.lang.String, double) instruction_simplifier (before)
+ /// CHECK-NOT: StringBuilderAppend
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndDouble(java.lang.String, double) instruction_simplifier (after)
+ /// CHECK: StringBuilderAppend
+ public static String $noinline$appendStringAndDouble(String s, double d) {
+ return new StringBuilder().append(s).append(d).toString();
+ }
+
+ public static void testAppendStringAndDouble() {
+ for (String expected : APPEND_DOUBLE_TEST_CASES) {
+ double f = Double.valueOf(expected.substring(APPEND_DOUBLE_PREFIX.length()));
+ String result = $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, f);
+ assertEquals(expected, result);
+ }
+ // Special values.
+ assertEquals(
+ "Double/NaN",
+ $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, Double.NaN));
+ assertEquals(
+ "Double/Infinity",
+ $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, Double.POSITIVE_INFINITY));
+ assertEquals(
+ "Double/-Infinity",
+ $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, Double.NEGATIVE_INFINITY));
+ }
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendDoubleAndFloat(double, float) instruction_simplifier (before)
+ /// CHECK-NOT: StringBuilderAppend
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendDoubleAndFloat(double, float) instruction_simplifier (after)
+ /// CHECK: StringBuilderAppend
+ public static String $noinline$appendDoubleAndFloat(double d, float f) {
+ return new StringBuilder().append(d).append(f).toString();
+ }
+
+ public static void testAppendDoubleAndFloat() {
+ assertEquals("1.50.325", $noinline$appendDoubleAndFloat(1.5, 0.325f));
+ assertEquals("1.5E170.3125", $noinline$appendDoubleAndFloat(1.5E17, 0.3125f));
+ assertEquals("1.0E8NaN", $noinline$appendDoubleAndFloat(1.0E8, Float.NaN));
+ assertEquals("Infinity0.5", $noinline$appendDoubleAndFloat(Double.POSITIVE_INFINITY, 0.5f));
+ assertEquals("2.5-Infinity", $noinline$appendDoubleAndFloat(2.5, Float.NEGATIVE_INFINITY));
+ }
+
public static String $noinline$appendStringAndString(String s1, String s2) {
return new StringBuilder().append(s1).append(s2).toString();
}
diff --git a/test/698-selects/src/Main.java b/test/698-selects/src/Main.java
index 1fadb86..514770a 100644
--- a/test/698-selects/src/Main.java
+++ b/test/698-selects/src/Main.java
@@ -15,28 +15,28 @@
*/
public class Main {
- public static int mZenMode = 0;
+ public static int mZenMode = 0;
- public static int $noinline$foo(int internal, boolean check1, boolean check2) {
- int result = internal;
- if (check1) {
- // This block is to ensure `result` is a phi in the return block. Without this block
- // the compiler could just generate one block with selects.
- if (check2) {
- mZenMode = 42;
- }
- result = (internal == 1) ? 1 : 0;
+ public static int $noinline$foo(int internal, boolean check1, boolean check2) {
+ int result = internal;
+ if (check1) {
+ // This block is to ensure `result` is a phi in the return block. Without this block
+ // the compiler could just generate one block with selects.
+ if (check2) {
+ mZenMode = 42;
+ }
+ result = (internal == 1) ? 1 : 0;
+ }
+ // The optimization bug was to make the incorrect assumption that:
+ // phi = (internal, (internal == 1))
+ // meant `internal` was a boolean.
+ return result;
}
- // The optimization bug was to make the incorrect assumption that:
- // phi = (internal, (internal == 1))
- // meant `internal` was a boolean.
- return result;
- }
- public static void main(String[] args) {
- int result = $noinline$foo(2, true, true);
- if (result != 0) {
- throw new Error("Expected 0, got " + result);
+ public static void main(String[] args) {
+ int result = $noinline$foo(2, true, true);
+ if (result != 0) {
+ throw new Error("Expected 0, got " + result);
+ }
}
- }
}
diff --git a/test/701-easy-div-rem/build b/test/701-easy-div-rem/build
deleted file mode 100644
index 6d114b6..0000000
--- a/test/701-easy-div-rem/build
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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
-
-# Write out the source file.
-mkdir src
-python3 ./genMain.py
-
-./default-build "$@"
diff --git a/test/701-easy-div-rem/build.py b/test/701-easy-div-rem/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/701-easy-div-rem/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/701-easy-div-rem/generate-sources b/test/701-easy-div-rem/generate-sources
new file mode 100755
index 0000000..efe4b05
--- /dev/null
+++ b/test/701-easy-div-rem/generate-sources
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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
+
+# Write out the source file.
+mkdir src
+python3 ./genMain.py
diff --git a/test/702-LargeBranchOffset/build b/test/702-LargeBranchOffset/build
deleted file mode 100644
index 2505b0a..0000000
--- a/test/702-LargeBranchOffset/build
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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
-
-# Write out the source file.
-mkdir -p src
-./generate
-
-./default-build "$@"
diff --git a/test/702-LargeBranchOffset/build.py b/test/702-LargeBranchOffset/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/702-LargeBranchOffset/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/702-LargeBranchOffset/generate-sources b/test/702-LargeBranchOffset/generate-sources
new file mode 100755
index 0000000..b01afc4
--- /dev/null
+++ b/test/702-LargeBranchOffset/generate-sources
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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
+
+# Write out the source file.
+mkdir -p src
+./generate
diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java
index 1b8377d..255599d 100644
--- a/test/706-checker-scheduler/src/Main.java
+++ b/test/706-checker-scheduler/src/Main.java
@@ -66,23 +66,23 @@
}
/// CHECK-START-ARM: void Main.arrayAccessVariable(int) scheduler (before)
- /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Param:i\d+>> ParameterValue
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
- /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
- /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
- /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
- /// CHECK: <<Array:i\d+>> IntermediateAddress
- /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
- /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const1>>]
- /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
- /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
- /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const1>>]
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
- /// CHECK: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
- /// CHECK: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const1>>]
- /// CHECK: <<ArraySet3:v\d+>> ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK-DAG: <<Array:i\d+>> IntermediateAddress
+ /// CHECK-DAG: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK-DAG: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK-DAG: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK-DAG: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK-DAG: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
+ /// CHECK-DAG: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
/// CHECK-START-ARM: void Main.arrayAccessVariable(int) scheduler (after)
/// CHECK: <<Param:i\d+>> ParameterValue
@@ -104,23 +104,23 @@
/// CHECK: ArraySet
/// CHECK-START-ARM64: void Main.arrayAccessVariable(int) scheduler (before)
- /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Param:i\d+>> ParameterValue
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
- /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
- /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
- /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
- /// CHECK: <<Array:i\d+>> IntermediateAddress
- /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
- /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const1>>]
- /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
- /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
- /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const1>>]
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
- /// CHECK: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
- /// CHECK: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const1>>]
- /// CHECK: <<ArraySet3:v\d+>> ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK-DAG: <<Array:i\d+>> IntermediateAddress
+ /// CHECK-DAG: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK-DAG: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK-DAG: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK-DAG: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK-DAG: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
+ /// CHECK-DAG: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const2>>]
+ /// CHECK-DAG: ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
/// CHECK-START-ARM64: void Main.arrayAccessVariable(int) scheduler (after)
/// CHECK: <<Param:i\d+>> ParameterValue
@@ -131,9 +131,9 @@
/// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
/// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
/// CHECK: <<Array:i\d+>> IntermediateAddress
- /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
- /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
- /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK: ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK: ArrayGet [<<Array>>,<<Add1>>]
/// CHECK: Add
/// CHECK: Add
/// CHECK: Add
diff --git a/test/707-checker-invalid-profile/check b/test/707-checker-invalid-profile/check
deleted file mode 100755
index 58d3a52..0000000
--- a/test/707-checker-invalid-profile/check
+++ /dev/null
@@ -1,28 +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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# When profile verification fails, dex2oat logs an error. The following
-# command strips out the error message.
-grep -v -f $1 $2 > $1
-grep -v -f $3 $4 > $3
-
-./default-check "$@"
diff --git a/test/707-checker-invalid-profile/run b/test/707-checker-invalid-profile/run
deleted file mode 100644
index 146e180..0000000
--- a/test/707-checker-invalid-profile/run
+++ /dev/null
@@ -1,17 +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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/707-checker-invalid-profile/run.py b/test/707-checker-invalid-profile/run.py
new file mode 100644
index 0000000..1616f1b
--- /dev/null
+++ b/test/707-checker-invalid-profile/run.py
@@ -0,0 +1,29 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
+
+ # When profile verification fails, dex2oat logs an error. The following
+ # command strips out the error message.
+ ctx.run(
+ fr"grep -v -f expected-stdout.txt '{args.stdout_file}' > expected-stdout.txt",
+ check=False)
+ ctx.run(
+ fr"grep -v -f expected-stderr.txt '{args.stderr_file}' > expected-stderr.txt",
+ check=False)
diff --git a/test/710-varhandle-creation/build b/test/710-varhandle-creation/build
deleted file mode 100644
index ca1e557..0000000
--- a/test/710-varhandle-creation/build
+++ /dev/null
@@ -1,20 +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
-
-./default-build "$@" --experimental var-handles
diff --git a/test/710-varhandle-creation/build.py b/test/710-varhandle-creation/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/710-varhandle-creation/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/710-varhandle-creation/run b/test/710-varhandle-creation/run
deleted file mode 100644
index 46b1a83..0000000
--- a/test/710-varhandle-creation/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Currently app images aren't unloaded when dex files are unloaded.
-exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/710-varhandle-creation/run.py b/test/710-varhandle-creation/run.py
new file mode 100644
index 0000000..930869a
--- /dev/null
+++ b/test/710-varhandle-creation/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+
+def run(ctx, args):
+ # Currently app images aren't unloaded when dex files are unloaded.
+ ctx.default_run(args, secondary_app_image=False)
diff --git a/test/710-varhandle-creation/src/Main.java b/test/710-varhandle-creation/src/Main.java
index c4fe2ff..6ddc58a 100644
--- a/test/710-varhandle-creation/src/Main.java
+++ b/test/710-varhandle-creation/src/Main.java
@@ -271,6 +271,7 @@
private static void checkInstantiatedVarHandles() {
System.out.print("checkInstantiatedVarHandles...");
+ // clang-format off
System.out.print("vz...");
checkNotNull(vz);
checkVarType(vz, boolean.class);
@@ -2052,6 +2053,7 @@
System.out.print("vbbo...");
checkNull(vbbo);
+ // clang-format on
System.out.println("PASS");
}
@@ -2090,6 +2092,7 @@
if (VarHandle.AccessMode.values().length != expectedLength) {
fail("VarHandle.AccessMode.value().length != " + expectedLength);
}
+ // clang-format off
checkAccessMode(VarHandle.AccessMode.GET, "GET", "get", 0);
checkAccessMode(VarHandle.AccessMode.SET, "SET", "set", 1);
checkAccessMode(VarHandle.AccessMode.GET_VOLATILE, "GET_VOLATILE", "getVolatile", 2);
@@ -2121,6 +2124,7 @@
checkAccessMode(VarHandle.AccessMode.GET_AND_BITWISE_XOR, "GET_AND_BITWISE_XOR", "getAndBitwiseXor", 28);
checkAccessMode(VarHandle.AccessMode.GET_AND_BITWISE_XOR_RELEASE, "GET_AND_BITWISE_XOR_RELEASE", "getAndBitwiseXorRelease", 29);
checkAccessMode(VarHandle.AccessMode.GET_AND_BITWISE_XOR_ACQUIRE, "GET_AND_BITWISE_XOR_ACQUIRE", "getAndBitwiseXorAcquire", 30);
+ // clang-format on
System.out.println("PASS");
}
@@ -2456,4 +2460,3 @@
checkStaticFieldVarHandleGc();
}
}
-
diff --git a/test/710-varhandle-creation/test-metadata.json b/test/710-varhandle-creation/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/710-varhandle-creation/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build
deleted file mode 100755
index 9a6e96e..0000000
--- a/test/712-varhandle-invocations/build
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-# Set variables for source directories. Using src-art so we use
-# VarHandles in the bootclasspath and can compile with the Java 8
-# compiler.
-MANUAL_SRC=src
-GENERATED_SRC=src2
-
-# Build the Java files
-mkdir -p src2
-
-# Collate list of manual test classes
-MANUAL_TESTS=$(cd "${MANUAL_SRC}" && find . -name 'Var*Tests.java' | sed -e 's@.*\(Var.*Tests\).*@\1@g' | sort)
-
-# Generate tests and Main that covers both the generated tests and manual tests
-python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS}
-
-./default-build "$@" --experimental var-handles
diff --git a/test/712-varhandle-invocations/build.py b/test/712-varhandle-invocations/build.py
new file mode 100644
index 0000000..511d1c1
--- /dev/null
+++ b/test/712-varhandle-invocations/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="var-handles")
diff --git a/test/712-varhandle-invocations/generate-sources b/test/712-varhandle-invocations/generate-sources
new file mode 100755
index 0000000..d025b26
--- /dev/null
+++ b/test/712-varhandle-invocations/generate-sources
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Make us exit on a failure
+set -e
+
+# Set variables for source directories. Using src-art so we use
+# VarHandles in the bootclasspath and can compile with the Java 8
+# compiler.
+MANUAL_SRC=src
+GENERATED_SRC=src2
+
+# Build the Java files
+mkdir -p src2
+
+# Collate list of manual test classes
+MANUAL_TESTS=$(cd "${MANUAL_SRC}" && find . -name 'Var*Tests.java' | sed -e 's@.*\(Var.*Tests\).*@\1@g' | sort)
+
+# Generate tests and Main that covers both the generated tests and manual tests
+python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS}
diff --git a/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java b/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java
index 73e3044..51c3b95 100644
--- a/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java
+++ b/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java
@@ -54,6 +54,52 @@
}
}
+ public static class ReferenceReturnTypeTest extends VarHandleUnitTest {
+ private Object o;
+ private static final VarHandle vh;
+
+ static {
+ try {
+ Class<?> cls = VoidReturnTypeTest.class;
+ vh = MethodHandles.lookup().findVarHandle(cls, "i", Object.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void doTest() {
+ vh.set(this, null);
+ try {
+ int i = (int) vh.get(this);
+ failUnreachable();
+ } catch (NullPointerException cce) {
+ }
+
+ vh.set(this, new Object());
+ try {
+ int i = (int) vh.get(this);
+ failUnreachable();
+ } catch (ClassCastException cce) {
+ }
+
+ vh.set(this, Integer.valueOf(42));
+ {
+ int i = (int) vh.get(this);
+ }
+
+ vh.set(this, new ReferenceReturnTypeTest());
+ try {
+ int i = (int) vh.get(this);
+ } catch (ClassCastException cce) {
+ }
+ }
+
+ public static void main(String[] args) {
+ new ReferenceReturnTypeTest().run();
+ }
+ }
+
//
// Tests that a null reference as a boxed primitive type argument
// throws a NullPointerException. These vary the VarHandle type
diff --git a/test/713-varhandle-invokers/build b/test/713-varhandle-invokers/build
deleted file mode 100755
index 09d376b..0000000
--- a/test/713-varhandle-invokers/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/713-varhandle-invokers/build.py b/test/713-varhandle-invokers/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/713-varhandle-invokers/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/713-varhandle-invokers/test-metadata.json b/test/713-varhandle-invokers/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/713-varhandle-invokers/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/714-invoke-custom-lambda-metafactory/build b/test/714-invoke-custom-lambda-metafactory/build
deleted file mode 100644
index b5002ba..0000000
--- a/test/714-invoke-custom-lambda-metafactory/build
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-# Opt-out from desugaring to ensure offending lambda is in the DEX.
-export USE_DESUGAR=false
-./default-build "$@" --experimental method-handles
diff --git a/test/714-invoke-custom-lambda-metafactory/build.py b/test/714-invoke-custom-lambda-metafactory/build.py
new file mode 100644
index 0000000..c1b1b60
--- /dev/null
+++ b/test/714-invoke-custom-lambda-metafactory/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(use_desugar=False, api_level="method-handles")
diff --git a/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt b/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt
index a9bf5a0..e69de29 100644
--- a/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt
+++ b/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt
@@ -1 +0,0 @@
-exit status: 1
diff --git a/test/714-invoke-custom-lambda-metafactory/run b/test/714-invoke-custom-lambda-metafactory/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/714-invoke-custom-lambda-metafactory/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/714-invoke-custom-lambda-metafactory/run.py b/test/714-invoke-custom-lambda-metafactory/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/714-invoke-custom-lambda-metafactory/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/715-clinit-implicit-parameter-annotations/build b/test/715-clinit-implicit-parameter-annotations/build
deleted file mode 100644
index 2b5f92c..0000000
--- a/test/715-clinit-implicit-parameter-annotations/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental parameter-annotations
diff --git a/test/715-clinit-implicit-parameter-annotations/build.py b/test/715-clinit-implicit-parameter-annotations/build.py
new file mode 100644
index 0000000..98d6c5d
--- /dev/null
+++ b/test/715-clinit-implicit-parameter-annotations/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="parameter-annotations")
diff --git a/test/716-jli-jit-samples/build b/test/716-jli-jit-samples/build
deleted file mode 100755
index 730a8a1..0000000
--- a/test/716-jli-jit-samples/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/716-jli-jit-samples/build.py b/test/716-jli-jit-samples/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/716-jli-jit-samples/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/716-jli-jit-samples/test-metadata.json b/test/716-jli-jit-samples/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/716-jli-jit-samples/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/719-varhandle-concurrency/build b/test/719-varhandle-concurrency/build
deleted file mode 100755
index 98a9967..0000000
--- a/test/719-varhandle-concurrency/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/719-varhandle-concurrency/build.py b/test/719-varhandle-concurrency/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/719-varhandle-concurrency/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/719-varhandle-concurrency/src/Main.java b/test/719-varhandle-concurrency/src/Main.java
index 784e7cc..47f523f 100644
--- a/test/719-varhandle-concurrency/src/Main.java
+++ b/test/719-varhandle-concurrency/src/Main.java
@@ -33,197 +33,196 @@
* were not run at all (skipped by all threads).
*/
public class Main {
- private static final VarHandle QA;
- static {
- QA = MethodHandles.arrayElementVarHandle(TestTask[].class);
- }
-
- private static final int TASK_COUNT = 10000;
- private static final int THREAD_COUNT = 100;
- /* Each test may need several retries before a concurrent failure is seen. In the past, for a
- * known bug, between 5 and 10 retries were sufficient. Use RETRIES to configure how many
- * iterations to retry for each test scenario. However, to avoid the test running for too long,
- * for example with gcstress, set a cap duration in MAX_RETRIES_DURATION. With this at least one
- * iteration would run, but there could be fewer retries if each of them takes too long. */
- private static final int RETRIES = 50;
- // b/235431387: timeout reduced from 1 minute
- private static final Duration MAX_RETRIES_DURATION = Duration.ofSeconds(15);
-
- public static void main(String[] args) throws Throwable {
- testConcurrentProcessing(new CompareAndExchangeRunnerFactory(), "compareAndExchange");
- testConcurrentProcessing(new CompareAndSetRunnerFactory(), "compareAndSet");
- testConcurrentProcessing(new WeakCompareAndSetRunnerFactory(), "weakCompareAndSet");
- }
-
- private static void testConcurrentProcessing(RunnerFactory factory,
- String testName) throws Throwable {
- final Duration startTs = Duration.ofNanos(System.nanoTime());
- final Duration endTs = startTs.plus(MAX_RETRIES_DURATION);
- for (int i = 0; i < RETRIES; ++i) {
- concurrentProcessingTestIteration(factory, i, testName);
- Duration now = Duration.ofNanos(System.nanoTime());
- if (0 < now.compareTo(endTs)) {
- break;
- }
+ private static final VarHandle QA;
+ static {
+ QA = MethodHandles.arrayElementVarHandle(TestTask[].class);
}
- }
- private static void concurrentProcessingTestIteration(RunnerFactory factory,
- int iteration, String testName) throws Throwable {
- final TestTask[] tasks = new TestTask[TASK_COUNT];
- final AtomicInteger result = new AtomicInteger();
+ private static final int TASK_COUNT = 10000;
+ private static final int THREAD_COUNT = 20;
+ /* Each test may need several retries before a concurrent failure is seen. In the past, for a
+ * known bug, between 5 and 10 retries were sufficient. Use RETRIES to configure how many
+ * iterations to retry for each test scenario. However, to avoid the test running for too long,
+ * for example with gcstress, set a cap duration in MAX_RETRIES_DURATION. With this at least one
+ * iteration would run, but there could be fewer retries if each of them takes too long. */
+ private static final int RETRIES = 50;
+ // b/235431387: timeout reduced from 1 minute
+ private static final Duration MAX_RETRIES_DURATION = Duration.ofSeconds(15);
- for (int i = 0; i < TASK_COUNT; ++i) {
- tasks[i] = new TestTask(Integer.valueOf(i+1), result::addAndGet);
- }
-
- Thread[] threads = new Thread[THREAD_COUNT];
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads[i] = factory.createRunner(tasks);
- }
-
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads[i].start();
- }
-
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads[i].join();
- }
-
- check(result.get(), TASK_COUNT * (TASK_COUNT + 1) / 2,
- testName + " test result not as expected", iteration);
- }
-
- /**
- * Processes the task queue until there are no tasks left.
- *
- * The actual task-grabbing mechanism is implemented in subclasses through grabTask(). This allows
- * testing various mechanisms, like compareAndSet() and compareAndExchange().
- */
- private static abstract class TaskRunner extends Thread {
-
- protected final TestTask[] tasks;
-
- TaskRunner(TestTask[] tasks) {
- this.tasks = tasks;
- }
-
- @Override
- public void run() {
- int i = 0;
- while (i < TASK_COUNT) {
- TestTask t = (TestTask) QA.get(tasks, i);
- if (t == null) {
- ++i;
- continue;
- }
- if (!grabTask(t, i)) {
- continue;
- }
- ++i;
- VarHandle.releaseFence();
- t.exec();
- }
- }
-
- /**
- * Grabs the next task from the queue in an atomic way.
- *
- * Once a task is retrieved successfully, the queue should no longer hold a reference to it.
- * This would be done, for example, by swapping the task with a null value.
- *
- * @param t The task to get from the queue
- * @param i The index where the task is found
- *
- * @return {@code true} if the task has been retrieved and is not available to any other
- * threads. Otherwise {@code false}. If {@code false} is returned, then either the task was no
- * longer present on the queue due to another thread grabbing it, or, in case of spurious
- * failure, the task is still available and no other thread managed to grab it.
- */
- protected abstract boolean grabTask(TestTask t, int i);
- }
-
- private static class TaskRunnerWithCompareAndExchange extends TaskRunner {
-
- TaskRunnerWithCompareAndExchange(TestTask[] tasks) {
- super(tasks);
- }
-
- @Override
- protected boolean grabTask(TestTask t, int i) {
- return (t == QA.compareAndExchange(tasks, i, t, null));
- }
- }
-
- private static class TaskRunnerWithCompareAndSet extends TaskRunner {
-
- TaskRunnerWithCompareAndSet(TestTask[] tasks) {
- super(tasks);
- }
-
- @Override
- protected boolean grabTask(TestTask t, int i) {
- return QA.compareAndSet(tasks, i, t, null);
- }
- }
-
- private static class TaskRunnerWithWeakCompareAndSet extends TaskRunner {
-
- TaskRunnerWithWeakCompareAndSet(TestTask[] tasks) {
- super(tasks);
- }
-
- @Override
- protected boolean grabTask(TestTask t, int i) {
- return QA.weakCompareAndSet(tasks, i, t, null);
- }
- }
-
-
- private interface RunnerFactory {
- Thread createRunner(TestTask[] tasks);
- }
-
- private static class CompareAndExchangeRunnerFactory implements RunnerFactory {
- @Override
- public Thread createRunner(TestTask[] tasks) {
- return new TaskRunnerWithCompareAndExchange(tasks);
- }
- }
-
- private static class CompareAndSetRunnerFactory implements RunnerFactory {
- @Override
- public Thread createRunner(TestTask[] tasks) {
- return new TaskRunnerWithCompareAndSet(tasks);
- }
- }
-
- private static class WeakCompareAndSetRunnerFactory implements RunnerFactory {
- @Override
- public Thread createRunner(TestTask[] tasks) {
- return new TaskRunnerWithWeakCompareAndSet(tasks);
- }
- }
-
- private static class TestTask {
- private final Integer ord;
- private final Consumer<Integer> action;
-
- TestTask(Integer ord, Consumer<Integer> action) {
- this.ord = ord;
- this.action = action;
- }
-
- public void exec() {
- action.accept(ord);
- }
- }
-
- private static void check(int actual, int expected, String msg, int iteration) {
- if (actual != expected) {
- System.err.println(String.format("[iteration %d] %s : %d != %d",
- iteration, msg, actual, expected));
- System.exit(1);
+ public static void main(String[] args) throws Throwable {
+ testConcurrentProcessing(new CompareAndExchangeRunnerFactory(), "compareAndExchange");
+ testConcurrentProcessing(new CompareAndSetRunnerFactory(), "compareAndSet");
+ testConcurrentProcessing(new WeakCompareAndSetRunnerFactory(), "weakCompareAndSet");
}
- }
+
+ private static void testConcurrentProcessing(RunnerFactory factory, String testName)
+ throws Throwable {
+ final Duration startTs = Duration.ofNanos(System.nanoTime());
+ final Duration endTs = startTs.plus(MAX_RETRIES_DURATION);
+ for (int i = 0; i < RETRIES; ++i) {
+ concurrentProcessingTestIteration(factory, i, testName);
+ Duration now = Duration.ofNanos(System.nanoTime());
+ if (0 < now.compareTo(endTs)) {
+ break;
+ }
+ }
+ }
+
+ private static void concurrentProcessingTestIteration(
+ RunnerFactory factory, int iteration, String testName) throws Throwable {
+ final TestTask[] tasks = new TestTask[TASK_COUNT];
+ final AtomicInteger result = new AtomicInteger();
+
+ for (int i = 0; i < TASK_COUNT; ++i) {
+ tasks[i] = new TestTask(Integer.valueOf(i + 1), result::addAndGet);
+ }
+
+ Thread[] threads = new Thread[THREAD_COUNT];
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads[i] = factory.createRunner(tasks);
+ }
+
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads[i].start();
+ }
+
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads[i].join();
+ }
+
+ check(result.get(),
+ TASK_COUNT * (TASK_COUNT + 1) / 2,
+ testName + " test result not as expected",
+ iteration);
+ }
+
+ /**
+ * Processes the task queue until there are no tasks left.
+ *
+ * The actual task-grabbing mechanism is implemented in subclasses through grabTask().
+ * This allows testing various mechanisms, like compareAndSet() and compareAndExchange().
+ */
+ private static abstract class TaskRunner extends Thread {
+
+ protected final TestTask[] tasks;
+
+ TaskRunner(TestTask[] tasks) {
+ this.tasks = tasks;
+ }
+
+ @Override
+ public void run() {
+ int i = 0;
+ while (i < TASK_COUNT) {
+ TestTask t = (TestTask) QA.get(tasks, i);
+ if (t == null) {
+ ++i;
+ continue;
+ }
+ if (!grabTask(t, i)) {
+ continue;
+ }
+ ++i;
+ VarHandle.releaseFence();
+ t.exec();
+ }
+ }
+
+ /**
+ * Grabs the next task from the queue in an atomic way.
+ *
+ * Once a task is retrieved successfully, the queue should no longer hold a reference to it.
+ * This would be done, for example, by swapping the task with a null value.
+ *
+ * @param t The task to get from the queue
+ * @param i The index where the task is found
+ *
+ * @return {@code true} if the task has been retrieved and is not available to any other
+ * threads. Otherwise {@code false}. If {@code false} is returned, then either the task was
+ * no longer present on the queue due to another thread grabbing it, or, in case of spurious
+ * failure, the task is still available and no other thread managed to grab it.
+ */
+ protected abstract boolean grabTask(TestTask t, int i);
+ }
+
+ private static class TaskRunnerWithCompareAndExchange extends TaskRunner {
+ TaskRunnerWithCompareAndExchange(TestTask[] tasks) {
+ super(tasks);
+ }
+
+ @Override
+ protected boolean grabTask(TestTask t, int i) {
+ return (t == QA.compareAndExchange(tasks, i, t, null));
+ }
+ }
+
+ private static class TaskRunnerWithCompareAndSet extends TaskRunner {
+ TaskRunnerWithCompareAndSet(TestTask[] tasks) {
+ super(tasks);
+ }
+
+ @Override
+ protected boolean grabTask(TestTask t, int i) {
+ return QA.compareAndSet(tasks, i, t, null);
+ }
+ }
+
+ private static class TaskRunnerWithWeakCompareAndSet extends TaskRunner {
+ TaskRunnerWithWeakCompareAndSet(TestTask[] tasks) {
+ super(tasks);
+ }
+
+ @Override
+ protected boolean grabTask(TestTask t, int i) {
+ return QA.weakCompareAndSet(tasks, i, t, null);
+ }
+ }
+
+
+ private interface RunnerFactory {
+ Thread createRunner(TestTask[] tasks);
+ }
+
+ private static class CompareAndExchangeRunnerFactory implements RunnerFactory {
+ @Override
+ public Thread createRunner(TestTask[] tasks) {
+ return new TaskRunnerWithCompareAndExchange(tasks);
+ }
+ }
+
+ private static class CompareAndSetRunnerFactory implements RunnerFactory {
+ @Override
+ public Thread createRunner(TestTask[] tasks) {
+ return new TaskRunnerWithCompareAndSet(tasks);
+ }
+ }
+
+ private static class WeakCompareAndSetRunnerFactory implements RunnerFactory {
+ @Override
+ public Thread createRunner(TestTask[] tasks) {
+ return new TaskRunnerWithWeakCompareAndSet(tasks);
+ }
+ }
+
+ private static class TestTask {
+ private final Integer ord;
+ private final Consumer<Integer> action;
+
+ TestTask(Integer ord, Consumer<Integer> action) {
+ this.ord = ord;
+ this.action = action;
+ }
+
+ public void exec() {
+ action.accept(ord);
+ }
+ }
+
+ private static void check(int actual, int expected, String msg, int iteration) {
+ if (actual != expected) {
+ System.err.println(String.format(
+ "[iteration %d] %s : %d != %d", iteration, msg, actual, expected));
+ System.exit(1);
+ }
+ }
}
diff --git a/test/719-varhandle-concurrency/test-metadata.json b/test/719-varhandle-concurrency/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/719-varhandle-concurrency/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/727-checker-unresolved-class/run b/test/727-checker-unresolved-class/run
deleted file mode 100644
index 1c9dd11..0000000
--- a/test/727-checker-unresolved-class/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- exec ${RUN} $@
-else
- # Append graphs for checker tests (we run dex2oat twice) with
- # --dump-cfg-append.
- # Make some classes unresolved for AOT compilation with
- # --updatable-bcp-packages-file.
- exec ${RUN} $@ \
- -Xcompiler-option --dump-cfg-append \
- -Xcompiler-option --updatable-bcp-packages-file="$DEX_LOCATION/res/updateable.txt"
-fi
diff --git a/test/727-checker-unresolved-class/run.py b/test/727-checker-unresolved-class/run.py
new file mode 100644
index 0000000..986897b
--- /dev/null
+++ b/test/727-checker-unresolved-class/run.py
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+
+def run(ctx, args):
+ if args.jvm:
+ ctx.default_run(args)
+ else:
+ # Append graphs for checker tests (we run dex2oat twice) with
+ # --dump-cfg-append.
+ # Make some classes unresolved for AOT compilation with
+ # --updatable-bcp-packages-file.
+ ctx.default_run(
+ args,
+ Xcompiler_option=[
+ "--dump-cfg-append",
+ f"--updatable-bcp-packages-file={ctx.env.DEX_LOCATION}/res/updateable.txt"
+ ])
diff --git a/test/728-imt-conflict-zygote/run b/test/728-imt-conflict-zygote/run
deleted file mode 100644
index 8fdff6d..0000000
--- a/test/728-imt-conflict-zygote/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-./default-run "$@" --zygote
diff --git a/test/728-imt-conflict-zygote/run.py b/test/728-imt-conflict-zygote/run.py
new file mode 100644
index 0000000..d0b6e49
--- /dev/null
+++ b/test/728-imt-conflict-zygote/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2020 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, zygote=True)
diff --git a/test/729-checker-polymorphic-intrinsic/run b/test/729-checker-polymorphic-intrinsic/run
deleted file mode 100644
index 5fa72ed..0000000
--- a/test/729-checker-polymorphic-intrinsic/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/729-checker-polymorphic-intrinsic/run.py b/test/729-checker-polymorphic-intrinsic/run.py
new file mode 100644
index 0000000..43aa25b
--- /dev/null
+++ b/test/729-checker-polymorphic-intrinsic/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/800-smali/run b/test/800-smali/run
deleted file mode 100644
index c8ce0bc..0000000
--- a/test/800-smali/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-# Target 31 to have the verifier behavior the test expects.
-./default-run "$@" --runtime-option -Xtarget-sdk-version:31
diff --git a/test/800-smali/run.py b/test/800-smali/run.py
new file mode 100644
index 0000000..f20dbc0
--- /dev/null
+++ b/test/800-smali/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2021 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.
+
+
+def run(ctx, args):
+ # Target 31 to have the verifier behavior the test expects.
+ ctx.default_run(args, runtime_option=["-Xtarget-sdk-version:31"])
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 90476b3..06d24d8 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -21,7 +21,7 @@
import java.util.List;
/**
- * Smali excercise.
+ * Smali exercise.
*/
public class Main {
diff --git a/test/804-class-extends-itself/build.py b/test/804-class-extends-itself/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/804-class-extends-itself/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/804-class-extends-itself/build b/test/804-class-extends-itself/generate-sources
old mode 100644
new mode 100755
similarity index 100%
rename from test/804-class-extends-itself/build
rename to test/804-class-extends-itself/generate-sources
diff --git a/test/807-method-handle-and-mr/build b/test/807-method-handle-and-mr/build
deleted file mode 100755
index 12a8e18..0000000
--- a/test/807-method-handle-and-mr/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Exit on failure.
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/807-method-handle-and-mr/build.py b/test/807-method-handle-and-mr/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/807-method-handle-and-mr/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/811-checker-invoke-super-secondary/build.py b/test/811-checker-invoke-super-secondary/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/811-checker-invoke-super-secondary/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/811-checker-invoke-super-secondary/test-metadata.json b/test/811-checker-invoke-super-secondary/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/811-checker-invoke-super-secondary/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/815-invokeinterface-default/src/Main.java b/test/815-invokeinterface-default/src/Main.java
index db3612b..97321c8 100644
--- a/test/815-invokeinterface-default/src/Main.java
+++ b/test/815-invokeinterface-default/src/Main.java
@@ -18,174 +18,174 @@
// An interface with enough methods to trigger a conflict.
interface Itf {
- public void method0a();
- public void method0b();
- public void method0c();
- public void method0d();
- public void method0e();
- public void method0f();
- public void method0g();
- public void method0h();
- public void method0i();
- public void method0j();
- public void method0k();
- public void method0l();
- public void method0m();
- public void method0n();
- public void method0o();
- public void method0p();
- public void method0q();
- public void method0r();
- public void method0s();
- public void method0t();
- public void method0u();
- public void method0v();
- public void method0w();
- public void method0x();
- public void method0y();
- public void method0z();
- public void method1a();
- public void method1b();
- public void method1c();
- public void method1d();
- public void method1e();
- public void method1f();
- public void method1g();
- public void method1h();
- public void method1i();
- public void method1j();
- public void method1k();
- public void method1l();
- public void method1m();
- public void method1n();
- public void method1o();
- public void method1p();
- public void method1q();
- public void method1r();
- public void method1s();
- public void method1t();
- public void method1u();
- public void method1v();
- public void method1w();
- public void method1x();
- public void method1y();
- public void method1z();
- public void method2a();
- public void method2b();
- public void method2c();
- public void method2d();
- public void method2e();
- public void method2f();
- public void method2g();
- public void method2h();
- public void method2i();
- public void method2j();
- public void method2k();
- public void method2l();
- public void method2m();
- public void method2n();
- public void method2o();
- public void method2p();
- public void method2q();
- public void method2r();
- public void method2s();
- public void method2t();
- public void method2u();
- public void method2v();
- public void method2w();
- public void method2x();
- public void method2y();
- public void method2z();
+ public void method0a();
+ public void method0b();
+ public void method0c();
+ public void method0d();
+ public void method0e();
+ public void method0f();
+ public void method0g();
+ public void method0h();
+ public void method0i();
+ public void method0j();
+ public void method0k();
+ public void method0l();
+ public void method0m();
+ public void method0n();
+ public void method0o();
+ public void method0p();
+ public void method0q();
+ public void method0r();
+ public void method0s();
+ public void method0t();
+ public void method0u();
+ public void method0v();
+ public void method0w();
+ public void method0x();
+ public void method0y();
+ public void method0z();
+ public void method1a();
+ public void method1b();
+ public void method1c();
+ public void method1d();
+ public void method1e();
+ public void method1f();
+ public void method1g();
+ public void method1h();
+ public void method1i();
+ public void method1j();
+ public void method1k();
+ public void method1l();
+ public void method1m();
+ public void method1n();
+ public void method1o();
+ public void method1p();
+ public void method1q();
+ public void method1r();
+ public void method1s();
+ public void method1t();
+ public void method1u();
+ public void method1v();
+ public void method1w();
+ public void method1x();
+ public void method1y();
+ public void method1z();
+ public void method2a();
+ public void method2b();
+ public void method2c();
+ public void method2d();
+ public void method2e();
+ public void method2f();
+ public void method2g();
+ public void method2h();
+ public void method2i();
+ public void method2j();
+ public void method2k();
+ public void method2l();
+ public void method2m();
+ public void method2n();
+ public void method2o();
+ public void method2p();
+ public void method2q();
+ public void method2r();
+ public void method2s();
+ public void method2t();
+ public void method2u();
+ public void method2v();
+ public void method2w();
+ public void method2x();
+ public void method2y();
+ public void method2z();
- public default void $noinline$defaultRecursiveMethod(boolean callRecursive) {
- if (callRecursive) {
- $noinline$defaultRecursiveMethod(false);
+ public default void $noinline$defaultRecursiveMethod(boolean callRecursive) {
+ if (callRecursive) {
+ $noinline$defaultRecursiveMethod(false);
+ }
}
- }
}
public class Main implements Itf {
- public static void main(String[] args) throws Exception {
- Main main = new Main();
- main.$noinline$defaultRecursiveMethod(true);
- }
+ public static void main(String[] args) throws Exception {
+ Main main = new Main();
+ main.$noinline$defaultRecursiveMethod(true);
+ }
- public void method0a() {}
- public void method0b() {}
- public void method0c() {}
- public void method0d() {}
- public void method0e() {}
- public void method0f() {}
- public void method0g() {}
- public void method0h() {}
- public void method0i() {}
- public void method0j() {}
- public void method0k() {}
- public void method0l() {}
- public void method0m() {}
- public void method0n() {}
- public void method0o() {}
- public void method0p() {}
- public void method0q() {}
- public void method0r() {}
- public void method0s() {}
- public void method0t() {}
- public void method0u() {}
- public void method0v() {}
- public void method0w() {}
- public void method0x() {}
- public void method0y() {}
- public void method0z() {}
- public void method1a() {}
- public void method1b() {}
- public void method1c() {}
- public void method1d() {}
- public void method1e() {}
- public void method1f() {}
- public void method1g() {}
- public void method1h() {}
- public void method1i() {}
- public void method1j() {}
- public void method1k() {}
- public void method1l() {}
- public void method1m() {}
- public void method1n() {}
- public void method1o() {}
- public void method1p() {}
- public void method1q() {}
- public void method1r() {}
- public void method1s() {}
- public void method1t() {}
- public void method1u() {}
- public void method1v() {}
- public void method1w() {}
- public void method1x() {}
- public void method1y() {}
- public void method1z() {}
- public void method2a() {}
- public void method2b() {}
- public void method2c() {}
- public void method2d() {}
- public void method2e() {}
- public void method2f() {}
- public void method2g() {}
- public void method2h() {}
- public void method2i() {}
- public void method2j() {}
- public void method2k() {}
- public void method2l() {}
- public void method2m() {}
- public void method2n() {}
- public void method2o() {}
- public void method2p() {}
- public void method2q() {}
- public void method2r() {}
- public void method2s() {}
- public void method2t() {}
- public void method2u() {}
- public void method2v() {}
- public void method2w() {}
- public void method2x() {}
- public void method2y() {}
- public void method2z() {}
+ public void method0a() {}
+ public void method0b() {}
+ public void method0c() {}
+ public void method0d() {}
+ public void method0e() {}
+ public void method0f() {}
+ public void method0g() {}
+ public void method0h() {}
+ public void method0i() {}
+ public void method0j() {}
+ public void method0k() {}
+ public void method0l() {}
+ public void method0m() {}
+ public void method0n() {}
+ public void method0o() {}
+ public void method0p() {}
+ public void method0q() {}
+ public void method0r() {}
+ public void method0s() {}
+ public void method0t() {}
+ public void method0u() {}
+ public void method0v() {}
+ public void method0w() {}
+ public void method0x() {}
+ public void method0y() {}
+ public void method0z() {}
+ public void method1a() {}
+ public void method1b() {}
+ public void method1c() {}
+ public void method1d() {}
+ public void method1e() {}
+ public void method1f() {}
+ public void method1g() {}
+ public void method1h() {}
+ public void method1i() {}
+ public void method1j() {}
+ public void method1k() {}
+ public void method1l() {}
+ public void method1m() {}
+ public void method1n() {}
+ public void method1o() {}
+ public void method1p() {}
+ public void method1q() {}
+ public void method1r() {}
+ public void method1s() {}
+ public void method1t() {}
+ public void method1u() {}
+ public void method1v() {}
+ public void method1w() {}
+ public void method1x() {}
+ public void method1y() {}
+ public void method1z() {}
+ public void method2a() {}
+ public void method2b() {}
+ public void method2c() {}
+ public void method2d() {}
+ public void method2e() {}
+ public void method2f() {}
+ public void method2g() {}
+ public void method2h() {}
+ public void method2i() {}
+ public void method2j() {}
+ public void method2k() {}
+ public void method2l() {}
+ public void method2m() {}
+ public void method2n() {}
+ public void method2o() {}
+ public void method2p() {}
+ public void method2q() {}
+ public void method2r() {}
+ public void method2s() {}
+ public void method2t() {}
+ public void method2u() {}
+ public void method2v() {}
+ public void method2w() {}
+ public void method2x() {}
+ public void method2y() {}
+ public void method2z() {}
}
diff --git a/test/816-illegal-new-array/src/Main.java b/test/816-illegal-new-array/src/Main.java
index 2d48c12..7a960bb 100644
--- a/test/816-illegal-new-array/src/Main.java
+++ b/test/816-illegal-new-array/src/Main.java
@@ -19,37 +19,37 @@
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("TestCase");
- Method m = c.getMethod("filledNewArray");
- try {
- m.invoke(null);
- throw new Error("Expected IllegalAccessError");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof IllegalAccessError)) {
- throw new Error("Expected IllegalAccessError, got " + e.getCause());
- }
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("TestCase");
+ Method m = c.getMethod("filledNewArray");
+ try {
+ m.invoke(null);
+ throw new Error("Expected IllegalAccessError");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof IllegalAccessError)) {
+ throw new Error("Expected IllegalAccessError, got " + e.getCause());
+ }
+ }
- m = c.getMethod("filledNewArrayRange");
- try {
- m.invoke(null);
- throw new Error("Expected IllegalAccessError");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof IllegalAccessError)) {
- throw new Error("Expected IllegalAccessError, got " + e.getCause());
- }
- }
+ m = c.getMethod("filledNewArrayRange");
+ try {
+ m.invoke(null);
+ throw new Error("Expected IllegalAccessError");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof IllegalAccessError)) {
+ throw new Error("Expected IllegalAccessError, got " + e.getCause());
+ }
+ }
- m = c.getMethod("newArray");
- try {
- m.invoke(null);
- throw new Error("Expected IllegalAccessError");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof IllegalAccessError)) {
- throw new Error("Expected IllegalAccessError, got " + e.getCause());
- }
+ m = c.getMethod("newArray");
+ try {
+ m.invoke(null);
+ throw new Error("Expected IllegalAccessError");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof IllegalAccessError)) {
+ throw new Error("Expected IllegalAccessError, got " + e.getCause());
+ }
+ }
}
- }
}
diff --git a/test/817-hiddenapi/build b/test/817-hiddenapi/build
deleted file mode 100644
index 330a6de..0000000
--- a/test/817-hiddenapi/build
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-set -e
-
-# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
-# a second time without to create a normal jar. We need to do this because we
-# want to load the jar once as an app module and once as a member of the boot
-# class path. The DexFileVerifier would fail on the former as it does not allow
-# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
-# class path dex files, so the boot jar loads fine in the latter case.
-
-export USE_HIDDENAPI=true
-./default-build "$@"
-
-# Move the jar file into the resource folder to be bundled with the test.
-mkdir res
-mv ${TEST_NAME}.jar res/boot.jar
-
-# Clear all intermediate files otherwise default-build would either skip
-# compilation or fail rebuilding.
-rm -rf classes*
-
-export USE_HIDDENAPI=false
-./default-build "$@"
diff --git a/test/817-hiddenapi/build.py b/test/817-hiddenapi/build.py
new file mode 100644
index 0000000..37b8cd5
--- /dev/null
+++ b/test/817-hiddenapi/build.py
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+
+ # Build the jars twice. First with applying hiddenapi, creating a boot jar, then
+ # a second time without to create a normal jar. We need to do this because we
+ # want to load the jar once as an app module and once as a member of the boot
+ # class path. The DexFileVerifier would fail on the former as it does not allow
+ # hidden API access flags in dex files. DexFileVerifier is not invoked on boot
+ # class path dex files, so the boot jar loads fine in the latter case.
+
+ ctx.default_build(use_hiddenapi=True)
+
+ # Move the jar file into the resource folder to be bundled with the test.
+ os.mkdir(ctx.test_dir / "res")
+ os.rename(ctx.test_dir / "817-hiddenapi.jar", ctx.test_dir / "res/boot.jar")
+
+ # Clear all intermediate files otherwise default-build would either skip
+ # compilation or fail rebuilding.
+ ctx.bash("rm -rf classes*")
+
+ ctx.default_build(use_hiddenapi=False)
diff --git a/test/817-hiddenapi/check b/test/817-hiddenapi/check
deleted file mode 100755
index 8c21ab4..0000000
--- a/test/817-hiddenapi/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# On gcstress configurations, an extra "JNI_OnUnload called" line may
-# be emitted. If so, remove it.
-sed -e '${/^JNI_OnUnload called$/d;}' "$2" > "$2.tmp"
-
-./default-check "$1" "$2.tmp" "$3" "$4"
diff --git a/test/817-hiddenapi/run.py b/test/817-hiddenapi/run.py
new file mode 100644
index 0000000..0ebb768
--- /dev/null
+++ b/test/817-hiddenapi/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # On gcstress configurations, an extra "JNI_OnUnload called" line may
+ # be emitted. If so, remove it.
+ ctx.run(fr"sed -i '/^JNI_OnUnload called$/d' '{args.stdout_file}'")
diff --git a/test/818-clinit-nterp/run b/test/818-clinit-nterp/run
deleted file mode 100644
index 52d2b5f..0000000
--- a/test/818-clinit-nterp/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2019 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.
-
-exec ${RUN} $@ --profile
diff --git a/test/818-clinit-nterp/run.py b/test/818-clinit-nterp/run.py
new file mode 100644
index 0000000..0f3a0ea
--- /dev/null
+++ b/test/818-clinit-nterp/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, profile=True)
diff --git a/test/818-clinit-nterp/src/Main.java b/test/818-clinit-nterp/src/Main.java
index 5342cec..5ebd384 100644
--- a/test/818-clinit-nterp/src/Main.java
+++ b/test/818-clinit-nterp/src/Main.java
@@ -15,20 +15,19 @@
*/
public class Main {
- public static void main(String[] args) {
- Clinit.run();
- if (!clinitDidRun) {
- throw new Error("Expected Clinit.<clinit> to have run");
+ public static void main(String[] args) {
+ Clinit.run();
+ if (!clinitDidRun) {
+ throw new Error("Expected Clinit.<clinit> to have run");
+ }
}
- }
- static boolean clinitDidRun = false;
+ static boolean clinitDidRun = false;
}
class Clinit {
- public static void run() {
- }
+ public static void run() {}
- static {
- Main.clinitDidRun = true;
- }
+ static {
+ Main.clinitDidRun = true;
+ }
}
diff --git a/test/819-verification-runtime/run b/test/819-verification-runtime/run
deleted file mode 100755
index c8ce0bc..0000000
--- a/test/819-verification-runtime/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-# Target 31 to have the verifier behavior the test expects.
-./default-run "$@" --runtime-option -Xtarget-sdk-version:31
diff --git a/test/819-verification-runtime/run.py b/test/819-verification-runtime/run.py
new file mode 100644
index 0000000..f20dbc0
--- /dev/null
+++ b/test/819-verification-runtime/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2021 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.
+
+
+def run(ctx, args):
+ # Target 31 to have the verifier behavior the test expects.
+ ctx.default_run(args, runtime_option=["-Xtarget-sdk-version:31"])
diff --git a/test/820-vdex-multidex/run b/test/820-vdex-multidex/run
deleted file mode 100644
index 3f6dc3c..0000000
--- a/test/820-vdex-multidex/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/820-vdex-multidex/run.py b/test/820-vdex-multidex/run.py
new file mode 100644
index 0000000..f597e41
--- /dev/null
+++ b/test/820-vdex-multidex/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, Xcompiler_option=["--compiler-filter=verify"], vdex=True)
diff --git a/test/821-madvise-willneed/run b/test/821-madvise-willneed/run
deleted file mode 100644
index 2c3917f..0000000
--- a/test/821-madvise-willneed/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.i
-
-# Load and run HelloWorld after madvising odex, vdex, art files to 100MB size
-# limit
-exec ${RUN} "${@}" --runtime-option -XMadviseWillNeedVdexFileSize:104857600 \
- --runtime-option -XMadviseWillNeedOdexFileSize:104857600 \
- --runtime-option -XMadviseWillNeedArtFileSize:104857600
diff --git a/test/821-madvise-willneed/run.py b/test/821-madvise-willneed/run.py
new file mode 100644
index 0000000..06d3bac
--- /dev/null
+++ b/test/821-madvise-willneed/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.i
+
+
+def run(ctx, args):
+ # Load and run HelloWorld after madvising odex, vdex, art files to 100MB size
+ # limit
+ ctx.default_run(
+ args,
+ runtime_option=[
+ "-XMadviseWillNeedVdexFileSize:104857600",
+ "-XMadviseWillNeedOdexFileSize:104857600",
+ "-XMadviseWillNeedArtFileSize:104857600"
+ ])
diff --git a/test/822-hiddenapi-future/build b/test/822-hiddenapi-future/build
deleted file mode 100644
index 02ce549..0000000
--- a/test/822-hiddenapi-future/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/822-hiddenapi-future/build.py b/test/822-hiddenapi-future/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/822-hiddenapi-future/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/823-cha-inlining/Android.bp b/test/823-cha-inlining/Android.bp
new file mode 100644
index 0000000..f174b02
--- /dev/null
+++ b/test/823-cha-inlining/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `823-cha-inlining`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-823-cha-inlining-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-823-cha-inlining",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-823-cha-inlining-src"
+ ],
+ data: [
+ ":art-run-test-823-cha-inlining-expected-stdout",
+ ":art-run-test-823-cha-inlining-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-823-cha-inlining-expected-stdout",
+ out: ["art-run-test-823-cha-inlining-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-823-cha-inlining-expected-stderr",
+ out: ["art-run-test-823-cha-inlining-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/829-unresolved-enclosing/build b/test/829-unresolved-enclosing/build
deleted file mode 100644
index f378df1..0000000
--- a/test/829-unresolved-enclosing/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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
-
-./default-build "$@"
diff --git a/test/829-unresolved-enclosing/javac_post.sh b/test/829-unresolved-enclosing/javac_post.sh
new file mode 100755
index 0000000..93875d4
--- /dev/null
+++ b/test/829-unresolved-enclosing/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Remove class available at compile time but not at run time.
+rm classes/MissingSuperClass.class
diff --git a/test/829-unresolved-enclosing/javac_wrapper.sh b/test/829-unresolved-enclosing/javac_wrapper.sh
deleted file mode 100755
index e3dc1e3..0000000
--- a/test/829-unresolved-enclosing/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Remove class available at compile time but not at run time.
-rm classes/MissingSuperClass.class
diff --git a/test/831-unverified-bcp/build.py b/test/831-unverified-bcp/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/831-unverified-bcp/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/831-unverified-bcp/test-metadata.json b/test/831-unverified-bcp/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/831-unverified-bcp/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/833-background-verification/run b/test/833-background-verification/run
deleted file mode 100755
index c455473..0000000
--- a/test/833-background-verification/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-# Target 31 to run the background verifier.
-# Disable dex2oat of secondary dex files to ensure we always run the background
-# verifier.
-./default-run "$@" --runtime-option -Xtarget-sdk-version:31 --no-secondary-compilation
diff --git a/test/833-background-verification/run.py b/test/833-background-verification/run.py
new file mode 100644
index 0000000..44d80ac
--- /dev/null
+++ b/test/833-background-verification/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2021 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.
+
+
+def run(ctx, args):
+ # Target 31 to run the background verifier.
+ # Disable dex2oat of secondary dex files to ensure we always run the background
+ # verifier.
+ ctx.default_run(
+ args,
+ runtime_option=["-Xtarget-sdk-version:31"],
+ secondary_compilation=False)
diff --git a/test/837-deopt/Android.bp b/test/837-deopt/Android.bp
new file mode 100644
index 0000000..2e0e3aa
--- /dev/null
+++ b/test/837-deopt/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `837-deopt`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-837-deopt",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-837-deopt-expected-stdout",
+ ":art-run-test-837-deopt-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-837-deopt-expected-stdout",
+ out: ["art-run-test-837-deopt-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-837-deopt-expected-stderr",
+ out: ["art-run-test-837-deopt-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/837-deopt/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/837-deopt/expected-stderr.txt
diff --git a/test/837-deopt/expected-stdout.txt b/test/837-deopt/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/837-deopt/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/837-deopt/info.txt b/test/837-deopt/info.txt
new file mode 100644
index 0000000..5c95277
--- /dev/null
+++ b/test/837-deopt/info.txt
@@ -0,0 +1 @@
+Tests around deoptimization.
diff --git a/test/837-deopt/src/Main.java b/test/837-deopt/src/Main.java
new file mode 100644
index 0000000..8e3ad7c
--- /dev/null
+++ b/test/837-deopt/src/Main.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main {
+ int field = 42;
+
+ // Test that deoptimization preserves objects that are singletons.
+ public static int $noinline$foo(Main arg) {
+ Main m = new Main();
+ arg.returnValue();
+ return m.field;
+ }
+
+ // Test that doing OSR after deoptimization works.
+ public static int $noinline$foo2(Main arg, boolean osr) {
+ Main m = new Main();
+ arg.returnValue();
+ if (osr) {
+ while (!isInOsrCode("$noinline$foo2")) {}
+ }
+ return m.field;
+ }
+
+ public static void main(String[] args) throws Throwable {
+ System.loadLibrary(args[0]);
+ if (isDebuggable()) {
+ // We do not deoptimize with inline caches when the app is debuggable, so just don't run the
+ // test.
+ return;
+ }
+ test1();
+ test2();
+ }
+
+ public static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void test1() {
+ ensureJitBaselineCompiled(Main.class, "$noinline$foo");
+ // Surround the call with GCs to increase chances we execute $noinline$foo
+ // while the GC isn't marking. This makes sure the inline cache is populated.
+ Runtime.getRuntime().gc();
+ assertEquals(42, $noinline$foo(new Main()));
+ Runtime.getRuntime().gc();
+
+ ensureJitCompiled(Main.class, "$noinline$foo");
+ assertEquals(42, $noinline$foo(new SubMain()));
+ }
+
+ public static void test2() {
+ ensureJitBaselineCompiled(Main.class, "$noinline$foo2");
+ // Surround the call with GCs to increase chances we execute $noinline$foo
+ // while the GC isn't marking. This makes sure the inline cache is populated.
+ Runtime.getRuntime().gc();
+ assertEquals(42, $noinline$foo2(new Main(), false));
+ Runtime.getRuntime().gc();
+
+ ensureJitCompiled(Main.class, "$noinline$foo2");
+ assertEquals(42, $noinline$foo2(new SubMain(), true));
+ }
+
+ public String returnValue() {
+ return "Main";
+ }
+
+ public static native void ensureJitCompiled(Class<?> cls, String methodName);
+ public static native void ensureJitBaselineCompiled(Class<?> cls, String methodName);
+ public static native boolean isInOsrCode(String methodName);
+ public static native boolean isDebuggable();
+}
+
+// Define a subclass with another implementation of returnValue to deoptimize $noinline$foo and
+// $noinline$foo2.
+class SubMain extends Main {
+ public String returnValue() {
+ return "SubMain";
+ }
+}
diff --git a/test/838-override/Android.bp b/test/838-override/Android.bp
new file mode 100644
index 0000000..2ac4e21
--- /dev/null
+++ b/test/838-override/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `838-override`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-838-override",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-838-override-expected-stdout",
+ ":art-run-test-838-override-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-838-override-expected-stdout",
+ out: ["art-run-test-838-override-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-838-override-expected-stderr",
+ out: ["art-run-test-838-override-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/838-override/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/838-override/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/838-override/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/838-override/expected-stdout.txt
diff --git a/test/838-override/info.txt b/test/838-override/info.txt
new file mode 100644
index 0000000..a33ddb2
--- /dev/null
+++ b/test/838-override/info.txt
@@ -0,0 +1 @@
+Tests for method overriding in the presence of package private methods.
diff --git a/test/838-override/src/Main.java b/test/838-override/src/Main.java
new file mode 100644
index 0000000..1e6aa49
--- /dev/null
+++ b/test/838-override/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+
+ public static void assertEquals(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Tescase 1: a class with:
+ // - a package-private pkg1 'foo'
+ // - a public pkg2 'foo'.
+ {
+ pkg2.PublicFoo obj = new pkg2.PublicFoo();
+ assertEquals(pkg1.Pkg1Foo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFoo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFoo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFoo.class, obj.foo());
+ }
+ // Tescase 2: a class with:
+ // - a package-private pkg1 'foo'
+ // - a public pkg2 'foo'
+ // - a public pkg1 'foo.
+ {
+ pkg1.PublicFoo obj = new pkg1.PublicFoo();
+ assertEquals(pkg1.PublicFoo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFoo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFoo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFoo.class, obj.foo());
+ }
+
+ // Tescase 3: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg3 'foo.
+ {
+ pkg3.PublicFoo obj = new pkg3.PublicFoo();
+ assertEquals(pkg1.Pkg1Foo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.Pkg2Foo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg3.PublicFoo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg3.PublicFoo.class, obj.foo());
+ }
+
+ // Tescase 4: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg3 'foo.
+ // - a public pkg2 'foo'
+ {
+ pkg2.PublicFooInheritsPkg3 obj = new pkg2.PublicFooInheritsPkg3();
+ assertEquals(pkg1.Pkg1Foo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFooInheritsPkg3.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFooInheritsPkg3.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFooInheritsPkg3.class, obj.foo());
+ }
+
+ // Tescase 5: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg3 'foo.
+ // - a public pkg2 'foo'
+ // - a public pkg1 'foo'
+ {
+ pkg1.PublicFooInheritsPkg2 obj = new pkg1.PublicFooInheritsPkg2();
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, obj.foo());
+ }
+
+ // Tescase 6: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg1 'foo.
+ {
+ pkg1.LowerIndexImplementsFoo obj = new pkg1.LowerIndexImplementsFoo();
+ assertEquals(pkg1.LowerIndexPublicFoo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.Pkg2Foo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg2.Pkg2Foo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg1.LowerIndexPublicFoo.class, obj.foo());
+ }
+ }
+}
diff --git a/test/838-override/src/pkg1/InterfaceFoo.java b/test/838-override/src/pkg1/InterfaceFoo.java
new file mode 100644
index 0000000..cd9e44b
--- /dev/null
+++ b/test/838-override/src/pkg1/InterfaceFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public interface InterfaceFoo {
+ public Class<?> foo();
+}
diff --git a/test/838-override/src/pkg1/LowerIndexImplementsFoo.java b/test/838-override/src/pkg1/LowerIndexImplementsFoo.java
new file mode 100644
index 0000000..290f07b
--- /dev/null
+++ b/test/838-override/src/pkg1/LowerIndexImplementsFoo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class LowerIndexImplementsFoo extends LowerIndexPublicFoo implements InterfaceFoo {
+}
diff --git a/test/838-override/src/pkg1/LowerIndexPublicFoo.java b/test/838-override/src/pkg1/LowerIndexPublicFoo.java
new file mode 100644
index 0000000..87c0c72
--- /dev/null
+++ b/test/838-override/src/pkg1/LowerIndexPublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class LowerIndexPublicFoo extends pkg2.Pkg2Foo {
+ public Class<?> foo() {
+ return LowerIndexPublicFoo.class;
+ }
+}
diff --git a/test/838-override/src/pkg1/Pkg1Foo.java b/test/838-override/src/pkg1/Pkg1Foo.java
new file mode 100644
index 0000000..0ffbc27
--- /dev/null
+++ b/test/838-override/src/pkg1/Pkg1Foo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class Pkg1Foo {
+
+ Class<?> foo() {
+ return Pkg1Foo.class;
+ }
+
+ public static Class<?> callFoo(Pkg1Foo obj) {
+ return obj.foo();
+ }
+}
diff --git a/test/838-override/src/pkg1/PublicFoo.java b/test/838-override/src/pkg1/PublicFoo.java
new file mode 100644
index 0000000..c43b282
--- /dev/null
+++ b/test/838-override/src/pkg1/PublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class PublicFoo extends pkg2.PublicFoo {
+ public Class<?> foo() {
+ return PublicFoo.class;
+ }
+}
diff --git a/test/838-override/src/pkg1/PublicFooInheritsPkg2.java b/test/838-override/src/pkg1/PublicFooInheritsPkg2.java
new file mode 100644
index 0000000..a255f36
--- /dev/null
+++ b/test/838-override/src/pkg1/PublicFooInheritsPkg2.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class PublicFooInheritsPkg2 extends pkg2.PublicFooInheritsPkg3 {
+ public Class<?> foo() {
+ return PublicFooInheritsPkg2.class;
+ }
+}
diff --git a/test/838-override/src/pkg2/Pkg2Foo.java b/test/838-override/src/pkg2/Pkg2Foo.java
new file mode 100644
index 0000000..c521914
--- /dev/null
+++ b/test/838-override/src/pkg2/Pkg2Foo.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class Pkg2Foo extends pkg1.Pkg1Foo{
+ Class<?> foo() {
+ return Pkg2Foo.class;
+ }
+
+ public static Class<?> callFoo(Pkg2Foo obj) {
+ return obj.foo();
+ }
+}
diff --git a/test/838-override/src/pkg2/PublicFoo.java b/test/838-override/src/pkg2/PublicFoo.java
new file mode 100644
index 0000000..5434234
--- /dev/null
+++ b/test/838-override/src/pkg2/PublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class PublicFoo extends Pkg2Foo {
+ public Class<?> foo() {
+ return PublicFoo.class;
+ }
+}
diff --git a/test/838-override/src/pkg2/PublicFooInheritsPkg3.java b/test/838-override/src/pkg2/PublicFooInheritsPkg3.java
new file mode 100644
index 0000000..1a983a1
--- /dev/null
+++ b/test/838-override/src/pkg2/PublicFooInheritsPkg3.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class PublicFooInheritsPkg3 extends pkg3.PublicFoo {
+ public Class<?> foo() {
+ return PublicFooInheritsPkg3.class;
+ }
+}
diff --git a/test/838-override/src/pkg3/Pkg3Foo.java b/test/838-override/src/pkg3/Pkg3Foo.java
new file mode 100644
index 0000000..72a16aa
--- /dev/null
+++ b/test/838-override/src/pkg3/Pkg3Foo.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 pkg3;
+
+public class Pkg3Foo extends pkg2.Pkg2Foo {
+ Class<?> foo() {
+ return Pkg3Foo.class;
+ }
+
+ public static Class<?> callFoo(Pkg3Foo obj) {
+ return obj.foo();
+ }
+}
diff --git a/test/838-override/src/pkg3/PublicFoo.java b/test/838-override/src/pkg3/PublicFoo.java
new file mode 100644
index 0000000..9d37137
--- /dev/null
+++ b/test/838-override/src/pkg3/PublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg3;
+
+public class PublicFoo extends Pkg3Foo {
+ public Class<?> foo() {
+ return PublicFoo.class;
+ }
+}
diff --git a/test/839-clinit-throw/Android.bp b/test/839-clinit-throw/Android.bp
new file mode 100644
index 0000000..dad79f0
--- /dev/null
+++ b/test/839-clinit-throw/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `839-clinit-throw`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-839-clinit-throw",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-839-clinit-throw-expected-stdout",
+ ":art-run-test-839-clinit-throw-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-839-clinit-throw-expected-stdout",
+ out: ["art-run-test-839-clinit-throw-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-839-clinit-throw-expected-stderr",
+ out: ["art-run-test-839-clinit-throw-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/839-clinit-throw/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/839-clinit-throw/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/839-clinit-throw/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/839-clinit-throw/expected-stdout.txt
diff --git a/test/839-clinit-throw/info.txt b/test/839-clinit-throw/info.txt
new file mode 100644
index 0000000..36cbb6f
--- /dev/null
+++ b/test/839-clinit-throw/info.txt
@@ -0,0 +1,2 @@
+Test that a static initializer throwing doesn't contain a static method of the
+same class in the stack trace.
diff --git a/test/839-clinit-throw/src/Main.java b/test/839-clinit-throw/src/Main.java
new file mode 100644
index 0000000..bb21de1
--- /dev/null
+++ b/test/839-clinit-throw/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+
+ static class NoPreloadHolder {
+ static Object o = null;
+
+ static {
+ o.toString();
+ }
+
+ static void $noinline$doCall() {
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ NoPreloadHolder.$noinline$doCall();
+ throw new Error("Expected ExceptionInInitializerError");
+ } catch (ExceptionInInitializerError e) {
+ // expected
+ check(e, mainLine);
+ }
+ }
+
+ public static int mainLine = 32;
+
+ static void check(ExceptionInInitializerError ie, int mainLine) {
+ StackTraceElement[] trace = ie.getStackTrace();
+ assertEquals(trace.length, 1);
+ checkElement(trace[0], "Main", "main", "Main.java", mainLine);
+ }
+
+ static void checkElement(StackTraceElement element,
+ String declaringClass, String methodName,
+ String fileName, int lineNumber) {
+ assertEquals(declaringClass, element.getClassName());
+ assertEquals(methodName, element.getMethodName());
+ assertEquals(fileName, element.getFileName());
+ assertEquals(lineNumber, element.getLineNumber());
+ }
+
+ static void assertEquals(Object expected, Object actual) {
+ if (!expected.equals(actual)) {
+ String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+ throw new AssertionError(msg);
+ }
+ }
+
+ static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/840-resolution/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/840-resolution/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/840-resolution/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/840-resolution/expected-stdout.txt
diff --git a/test/840-resolution/info.txt b/test/840-resolution/info.txt
new file mode 100644
index 0000000..bd88f7d
--- /dev/null
+++ b/test/840-resolution/info.txt
@@ -0,0 +1,2 @@
+Various tests on interface method linking when not finding a public
+implementation.
diff --git a/test/840-resolution/jasmin/SubClass2.j b/test/840-resolution/jasmin/SubClass2.j
new file mode 100644
index 0000000..8a65e1f
--- /dev/null
+++ b/test/840-resolution/jasmin/SubClass2.j
@@ -0,0 +1,35 @@
+; Copyright (C) 2022 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class public SubClass2
+.super SuperClass
+.implements Interface
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial SuperClass/<init>()V
+ return
+.end method
+
+.method foo()Ljava/lang/Class;
+ .limit stack 1
+ .limit locals 1
+ ; jasmin does not support ldc with a class, so just return null for the
+ ; purpose of this test.
+ aconst_null
+ areturn
+.end method
+
diff --git a/test/840-resolution/src/Main.java b/test/840-resolution/src/Main.java
new file mode 100644
index 0000000..23cd31d
--- /dev/null
+++ b/test/840-resolution/src/Main.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+
+ // Testcase 1: the superclass has a package private version in the same package.
+ static Interface s = new SubClass();
+
+ // Testcase 2: the class has a package private version.
+ static Interface s2;
+
+ // Testcase 3: the superclass has a package private version in a different package.
+ static Interface s3 = new SubClassFromPkg();
+
+ // Testcase 4: there is no implementation in the hierarchy.
+ static Interface s4 = new SubClassNoFoo();
+
+ // Testcase 5: there is a private method in the hierarchy.
+ static Interface s5 = new SubClassPrivateFoo();
+
+ // Testcase 6: there is a static method in the hierarchy.
+ static Interface s6 = new SubClassStaticFoo();
+
+ static {
+ try {
+ s2 = (Interface) Class.forName("SubClass2").newInstance();
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ public static void assertEquals(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void assertTrue(boolean value) {
+ if (!value) {
+ throw new Error("");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ assertEquals(SuperClass.class, ((SubClass) s).foo());
+ assertEquals(SuperClass.class, ((SuperClass) s).foo());
+
+ try {
+ s.foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ assertEquals(null, ((SuperClass) s2).foo());
+ try {
+ s2.foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ ((pkg.PkgSuperClass) s3).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ ((SubClassFromPkg) s3).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ s3.foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ ((SuperClassNoFoo) s4).foo();
+ throw new Error("Expected NoSuchMethodError");
+ } catch (NoSuchMethodError e) {
+ // expected
+ }
+
+ try {
+ ((SubClassNoFoo) s4).foo();
+ throw new Error("Expected AbstractMethodError");
+ } catch (AbstractMethodError e) {
+ // expected
+ }
+
+ try {
+ s4.foo();
+ throw new Error("Expected AbstractMethodError");
+ } catch (AbstractMethodError e) {
+ // expected
+ }
+
+ try {
+ ((SuperClassPrivateFoo) s5).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ ((SubClassPrivateFoo) s5).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ s5.foo();
+ throw new Error("Expected AbstractMethodError on RI, IllegalAccessError on ART");
+ } catch (AbstractMethodError | IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ ((SuperClassStaticFoo) s6).foo();
+ throw new Error("Expected IncompatibleClassChangeError");
+ } catch (IncompatibleClassChangeError e) {
+ // expected
+ }
+
+ try {
+ ((SubClassStaticFoo) s6).foo();
+ throw new Error("Expected IncompatibleClassChangeError");
+ } catch (IncompatibleClassChangeError e) {
+ // expected
+ }
+
+ try {
+ s6.foo();
+ throw new Error("Expected AbstractMethodError");
+ } catch (AbstractMethodError e) {
+ // expected
+ }
+ }
+}
+
+interface Interface {
+ public Class<?> foo();
+}
+
+class SubClass extends SuperClass implements Interface {
+}
+
+class SubClassFromPkg extends pkg.PkgSuperClass implements Interface {
+}
+
+class SubClassNoFoo extends SuperClassNoFoo implements Interface {
+}
+
+class SubClassPrivateFoo extends SuperClassPrivateFoo implements Interface {
+}
+
+class SubClassStaticFoo extends SuperClassStaticFoo implements Interface {
+}
diff --git a/test/840-resolution/src/SuperClass.java b/test/840-resolution/src/SuperClass.java
new file mode 100644
index 0000000..ece0188
--- /dev/null
+++ b/test/840-resolution/src/SuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClass {
+ public Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src/SuperClassNoFoo.java b/test/840-resolution/src/SuperClassNoFoo.java
new file mode 100644
index 0000000..747aaef
--- /dev/null
+++ b/test/840-resolution/src/SuperClassNoFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class SuperClassNoFoo {
+ public Class<?> foo() {
+ throw new Error("Unreachable");
+ }
+}
diff --git a/test/840-resolution/src/SuperClassPrivateFoo.java b/test/840-resolution/src/SuperClassPrivateFoo.java
new file mode 100644
index 0000000..95af4f7
--- /dev/null
+++ b/test/840-resolution/src/SuperClassPrivateFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClassPrivateFoo {
+ public Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src/SuperClassStaticFoo.java b/test/840-resolution/src/SuperClassStaticFoo.java
new file mode 100644
index 0000000..490637f
--- /dev/null
+++ b/test/840-resolution/src/SuperClassStaticFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class SuperClassStaticFoo {
+ public Class<?> foo() {
+ throw new Error("Unreachable");
+ }
+}
diff --git a/test/840-resolution/src/pkg/PkgSuperClass.java b/test/840-resolution/src/pkg/PkgSuperClass.java
new file mode 100644
index 0000000..397ae28
--- /dev/null
+++ b/test/840-resolution/src/pkg/PkgSuperClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg;
+
+public class PkgSuperClass {
+ public Class<?> foo() {
+ return PkgSuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src2/SuperClass.java b/test/840-resolution/src2/SuperClass.java
new file mode 100644
index 0000000..fe40c0a
--- /dev/null
+++ b/test/840-resolution/src2/SuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClass {
+ Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src2/SuperClassNoFoo.java b/test/840-resolution/src2/SuperClassNoFoo.java
new file mode 100644
index 0000000..c0e8c44
--- /dev/null
+++ b/test/840-resolution/src2/SuperClassNoFoo.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class SuperClassNoFoo {
+}
diff --git a/test/840-resolution/src2/SuperClassPrivateFoo.java b/test/840-resolution/src2/SuperClassPrivateFoo.java
new file mode 100644
index 0000000..f0c1c68
--- /dev/null
+++ b/test/840-resolution/src2/SuperClassPrivateFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClassPrivateFoo {
+ private Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src2/SuperClassStaticFoo.java b/test/840-resolution/src2/SuperClassStaticFoo.java
new file mode 100644
index 0000000..ecf8bc1
--- /dev/null
+++ b/test/840-resolution/src2/SuperClassStaticFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class SuperClassStaticFoo {
+ public static Class<?> foo() {
+ return SuperClassStaticFoo.class;
+ }
+}
diff --git a/test/840-resolution/src2/pkg/PkgSuperClass.java b/test/840-resolution/src2/pkg/PkgSuperClass.java
new file mode 100644
index 0000000..2f333ee
--- /dev/null
+++ b/test/840-resolution/src2/pkg/PkgSuperClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg;
+
+public class PkgSuperClass {
+ Class<?> foo() {
+ return PkgSuperClass.class;
+ }
+}
diff --git a/test/841-defaults/Android.bp b/test/841-defaults/Android.bp
new file mode 100644
index 0000000..2d2fce6
--- /dev/null
+++ b/test/841-defaults/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `841-defaults`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-841-defaults",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-841-defaults-expected-stdout",
+ ":art-run-test-841-defaults-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-841-defaults-expected-stdout",
+ out: ["art-run-test-841-defaults-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-841-defaults-expected-stderr",
+ out: ["art-run-test-841-defaults-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/841-defaults/expected-stderr.txt
similarity index 100%
rename from test/089-many-methods/expected-stderr.txt
rename to test/841-defaults/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/841-defaults/expected-stdout.txt
similarity index 100%
rename from test/089-many-methods/expected-stdout.txt
rename to test/841-defaults/expected-stdout.txt
diff --git a/test/841-defaults/info.txt b/test/841-defaults/info.txt
new file mode 100644
index 0000000..85e265f
--- /dev/null
+++ b/test/841-defaults/info.txt
@@ -0,0 +1,2 @@
+Regression test for doing an invokeinterface on a default method whose
+dex method index is greater than the imt size.
diff --git a/test/841-defaults/src/Main.java b/test/841-defaults/src/Main.java
new file mode 100644
index 0000000..c07b516
--- /dev/null
+++ b/test/841-defaults/src/Main.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+interface Itf {
+ default int defaultMethod1() { return 1; }
+ default int defaultMethod2() { return 2; }
+ default int defaultMethod3() { return 3; }
+ default int defaultMethod4() { return 4; }
+ default int defaultMethod5() { return 5; }
+ default int defaultMethod6() { return 6; }
+ default int defaultMethod7() { return 7; }
+ default int defaultMethod8() { return 8; }
+ default int defaultMethod9() { return 9; }
+ default int defaultMethod10() { return 10; }
+ default int defaultMethod11() { return 11; }
+ default int defaultMethod12() { return 12; }
+ default int defaultMethod13() { return 13; }
+ default int defaultMethod14() { return 14; }
+ default int defaultMethod15() { return 15; }
+ default int defaultMethod16() { return 16; }
+ default int defaultMethod17() { return 17; }
+ default int defaultMethod18() { return 18; }
+ default int defaultMethod19() { return 19; }
+ default int defaultMethod20() { return 20; }
+ default int defaultMethod21() { return 21; }
+ default int defaultMethod22() { return 22; }
+ default int defaultMethod23() { return 23; }
+ default int defaultMethod24() { return 24; }
+ default int defaultMethod25() { return 25; }
+ default int defaultMethod26() { return 26; }
+ default int defaultMethod27() { return 27; }
+ default int defaultMethod28() { return 28; }
+ default int defaultMethod29() { return 29; }
+ default int defaultMethod30() { return 30; }
+ default int defaultMethod31() { return 31; }
+ default int defaultMethod32() { return 32; }
+ default int defaultMethod33() { return 33; }
+ default int defaultMethod34() { return 34; }
+ default int defaultMethod35() { return 35; }
+ default int defaultMethod36() { return 36; }
+ default int defaultMethod37() { return 37; }
+ default int defaultMethod38() { return 38; }
+ default int defaultMethod39() { return 39; }
+ default int defaultMethod40() { return 40; }
+ default int defaultMethod41() { return 41; }
+ default int defaultMethod42() { return 42; }
+ default int defaultMethod43() { return 43; }
+ default int defaultMethod44() { return 44; }
+ default int defaultMethod45() { return 45; }
+ default int defaultMethod46() { return 46; }
+ default int defaultMethod47() { return 47; }
+ default int defaultMethod48() { return 48; }
+ default int defaultMethod49() { return 49; }
+ default int defaultMethod50() { return 50; }
+ default int defaultMethod51() { return 51; }
+}
+
+public class Main implements Itf {
+ static Itf itf = new Main();
+ public static void assertEquals(int value, int expected) {
+ if (value != expected) {
+ throw new Error("Expected " + expected + ", got " + value);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ assertEquals(itf.defaultMethod1(), 1);
+ assertEquals(itf.defaultMethod2(), 2);
+ assertEquals(itf.defaultMethod3(), 3);
+ assertEquals(itf.defaultMethod4(), 4);
+ assertEquals(itf.defaultMethod5(), 5);
+ assertEquals(itf.defaultMethod6(), 6);
+ assertEquals(itf.defaultMethod7(), 7);
+ assertEquals(itf.defaultMethod8(), 8);
+ assertEquals(itf.defaultMethod9(), 9);
+ assertEquals(itf.defaultMethod10(), 10);
+ assertEquals(itf.defaultMethod11(), 11);
+ assertEquals(itf.defaultMethod12(), 12);
+ assertEquals(itf.defaultMethod13(), 13);
+ assertEquals(itf.defaultMethod14(), 14);
+ assertEquals(itf.defaultMethod15(), 15);
+ assertEquals(itf.defaultMethod16(), 16);
+ assertEquals(itf.defaultMethod17(), 17);
+ assertEquals(itf.defaultMethod18(), 18);
+ assertEquals(itf.defaultMethod19(), 19);
+ assertEquals(itf.defaultMethod20(), 20);
+ assertEquals(itf.defaultMethod21(), 21);
+ assertEquals(itf.defaultMethod22(), 22);
+ assertEquals(itf.defaultMethod23(), 23);
+ assertEquals(itf.defaultMethod24(), 24);
+ assertEquals(itf.defaultMethod25(), 25);
+ assertEquals(itf.defaultMethod26(), 26);
+ assertEquals(itf.defaultMethod27(), 27);
+ assertEquals(itf.defaultMethod28(), 28);
+ assertEquals(itf.defaultMethod29(), 29);
+ assertEquals(itf.defaultMethod30(), 30);
+ assertEquals(itf.defaultMethod31(), 31);
+ assertEquals(itf.defaultMethod32(), 32);
+ assertEquals(itf.defaultMethod33(), 33);
+ assertEquals(itf.defaultMethod34(), 34);
+ assertEquals(itf.defaultMethod35(), 35);
+ assertEquals(itf.defaultMethod36(), 36);
+ assertEquals(itf.defaultMethod37(), 37);
+ assertEquals(itf.defaultMethod38(), 38);
+ assertEquals(itf.defaultMethod39(), 39);
+ assertEquals(itf.defaultMethod40(), 40);
+ assertEquals(itf.defaultMethod41(), 41);
+ assertEquals(itf.defaultMethod42(), 42);
+ assertEquals(itf.defaultMethod43(), 43);
+ assertEquals(itf.defaultMethod44(), 44);
+ assertEquals(itf.defaultMethod45(), 45);
+ assertEquals(itf.defaultMethod46(), 46);
+ assertEquals(itf.defaultMethod47(), 47);
+ assertEquals(itf.defaultMethod48(), 48);
+ assertEquals(itf.defaultMethod49(), 49);
+ assertEquals(itf.defaultMethod50(), 50);
+ assertEquals(itf.defaultMethod51(), 51);
+ }
+}
diff --git a/test/842-vdex-hard-failure/run b/test/842-vdex-hard-failure/run
deleted file mode 100644
index 4a4ee37..0000000
--- a/test/842-vdex-hard-failure/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-# This test is for testing vdex when calling FastVerify and doing compilation.
-exec ${RUN} --vdex --vdex-filter speed "${@}"
diff --git a/test/842-vdex-hard-failure/run.py b/test/842-vdex-hard-failure/run.py
new file mode 100644
index 0000000..81ce172
--- /dev/null
+++ b/test/842-vdex-hard-failure/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+
+def run(ctx, args):
+ # This test is for testing vdex when calling FastVerify and doing compilation.
+ ctx.default_run(args, vdex=True, vdex_filter="speed")
diff --git a/test/843-default-interface/Android.bp b/test/843-default-interface/Android.bp
new file mode 100644
index 0000000..ff942e6
--- /dev/null
+++ b/test/843-default-interface/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `843-default-interface`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-843-default-interface-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-843-default-interface",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-843-default-interface-src"
+ ],
+ data: [
+ ":art-run-test-843-default-interface-expected-stdout",
+ ":art-run-test-843-default-interface-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-843-default-interface-expected-stdout",
+ out: ["art-run-test-843-default-interface-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-843-default-interface-expected-stderr",
+ out: ["art-run-test-843-default-interface-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/843-default-interface/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/843-default-interface/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/843-default-interface/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/843-default-interface/expected-stdout.txt
diff --git a/test/843-default-interface/info.txt b/test/843-default-interface/info.txt
new file mode 100644
index 0000000..3586e7f
--- /dev/null
+++ b/test/843-default-interface/info.txt
@@ -0,0 +1,2 @@
+Regression test for ArtMethod::CopyFrom, which used to wrongly override the
+imt_index_ of abstract methods with 0.
diff --git a/test/843-default-interface/src/Impl.java b/test/843-default-interface/src/Impl.java
new file mode 100644
index 0000000..8c9d76c
--- /dev/null
+++ b/test/843-default-interface/src/Impl.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Impl implements SubItf {
+ public String foo() {
+ return "Impl";
+ }
+}
diff --git a/test/843-default-interface/src/Itf.java b/test/843-default-interface/src/Itf.java
new file mode 100644
index 0000000..0625429
--- /dev/null
+++ b/test/843-default-interface/src/Itf.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public interface Itf {
+ public default String bar() {
+ return "Itf";
+ }
+}
diff --git a/test/843-default-interface/src/Main.java b/test/843-default-interface/src/Main.java
new file mode 100644
index 0000000..8b2b2da
--- /dev/null
+++ b/test/843-default-interface/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ static SubItf itf = new Impl();
+ public static void main(String[] args) throws Exception {
+ // Loop enough to trigger the native OOME.
+ for (int i = 0; i < 50000; ++i) {
+ // Because the imt index was overwritten to 0, this call ended up
+ // in the conflict trampoline which wrongly updated the 0th entry
+ // of the imt table. This lead to this call always calling the
+ // conflict trampoline.
+ itf.foo();
+ }
+ }
+}
diff --git a/test/843-default-interface/src/OtherItf.java b/test/843-default-interface/src/OtherItf.java
new file mode 100644
index 0000000..368631c
--- /dev/null
+++ b/test/843-default-interface/src/OtherItf.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public interface OtherItf {
+ // javac will complain when compiling SubItf if both superinterfaces Itf and OtherItf
+ // define a default method bar(), so we do not define bar() here.
+ // What will be loaded at runtime will actually be src2/OtherItf.
+ // public default String bar() {
+ // return "OtherItf";
+ // }
+}
diff --git a/test/843-default-interface/src/SubItf.java b/test/843-default-interface/src/SubItf.java
new file mode 100644
index 0000000..6983b18
--- /dev/null
+++ b/test/843-default-interface/src/SubItf.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// The method bar will be a default conflict for this class. That used to make
+// the class linker re-allocate its ArtMethod array, and calling CopyFrom.
+// The bug was that CopyFrom was overwriting the imt index of interface methods,
+// and for this example `foo`.
+public interface SubItf extends Itf, OtherItf {
+ public String foo();
+}
diff --git a/test/843-default-interface/src2/OtherItf.java b/test/843-default-interface/src2/OtherItf.java
new file mode 100644
index 0000000..08028f4
--- /dev/null
+++ b/test/843-default-interface/src2/OtherItf.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public interface OtherItf {
+ public default String bar() {
+ return "OtherItf";
+ }
+}
diff --git a/test/844-exception/Android.bp b/test/844-exception/Android.bp
new file mode 100644
index 0000000..92ecef4
--- /dev/null
+++ b/test/844-exception/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `844-exception`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-844-exception",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-844-exception-expected-stdout",
+ ":art-run-test-844-exception-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-844-exception-expected-stdout",
+ out: ["art-run-test-844-exception-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-844-exception-expected-stderr",
+ out: ["art-run-test-844-exception-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/844-exception/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/844-exception/expected-stderr.txt
diff --git a/test/844-exception/expected-stdout.txt b/test/844-exception/expected-stdout.txt
new file mode 100644
index 0000000..7ddf8e3
--- /dev/null
+++ b/test/844-exception/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Caught exception
diff --git a/test/844-exception/info.txt b/test/844-exception/info.txt
new file mode 100644
index 0000000..a825fbb
--- /dev/null
+++ b/test/844-exception/info.txt
@@ -0,0 +1,2 @@
+Regression test for Thread::QuickDeliverException which used to expect a quick
+frame to be the caller.
diff --git a/test/844-exception/src/Main.java b/test/844-exception/src/Main.java
new file mode 100644
index 0000000..6237844
--- /dev/null
+++ b/test/844-exception/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ static Main empty;
+
+ static class MyThread extends Thread {
+ public void run() {
+ // This will throw at `callMethodThatThrows` and trigger deoptimization checks which we used
+ // to crash on.
+ new Inner();
+ }
+ }
+
+ public static class Inner {
+ // Have a <clinit> method invoke another <clinit> method to ensure we execute in the
+ // interpreter.
+ static {
+ new Inner2();
+ }
+ }
+
+ public static class Inner2 {
+ static {
+ Main.callMethodThatThrows();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Disables use of nterp.
+ Main.setAsyncExceptionsThrown();
+
+ // Execute the test in a different thread, to ensure we still
+ // return a 0 exit status.
+ Thread t = new MyThread();
+ t.setUncaughtExceptionHandler((th, e) -> {
+ System.out.println("Caught exception");
+ });
+ t.start();
+ t.join();
+ }
+
+ public static void callMethodThatThrows() {
+ // Ensures we get deoptimization requests.
+ Main.forceInterpreterOnThread();
+ throw new Error("");
+ }
+
+ public static native void forceInterpreterOnThread();
+ public static native void setAsyncExceptionsThrown();
+
+}
diff --git a/test/844-exception2/Android.bp b/test/844-exception2/Android.bp
new file mode 100644
index 0000000..50568b1
--- /dev/null
+++ b/test/844-exception2/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `844-exception2`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-844-exception2",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-844-exception2-expected-stdout",
+ ":art-run-test-844-exception2-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-844-exception2-expected-stdout",
+ out: ["art-run-test-844-exception2-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-844-exception2-expected-stderr",
+ out: ["art-run-test-844-exception2-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/844-exception2/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/844-exception2/expected-stderr.txt
diff --git a/test/844-exception2/expected-stdout.txt b/test/844-exception2/expected-stdout.txt
new file mode 100644
index 0000000..7ddf8e3
--- /dev/null
+++ b/test/844-exception2/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Caught exception
diff --git a/test/844-exception2/info.txt b/test/844-exception2/info.txt
new file mode 100644
index 0000000..641dc72
--- /dev/null
+++ b/test/844-exception2/info.txt
@@ -0,0 +1,2 @@
+Regression test for Thread::DeoptimizeWithDeoptimizationException which always
+expected to have a shadow frame to execute.
diff --git a/test/844-exception2/src/Main.java b/test/844-exception2/src/Main.java
new file mode 100644
index 0000000..400b337
--- /dev/null
+++ b/test/844-exception2/src/Main.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Main {
+ public static class Inner {
+ // Use a <clinit> method to ensure we execute in the
+ // interpreter.
+ static {
+ Main.callMethodThatThrows();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Disables use of nterp.
+ Main.setAsyncExceptionsThrown();
+
+ Thread.currentThread().setUncaughtExceptionHandler((th, e) -> {
+ System.out.println("Caught exception");
+ // Exit the test gracefully.
+ System.exit(0);
+ });
+ // This will throw at `callMethodThatThrows` and trigger deoptimization checks which we used
+ // to crash on.
+ new Inner();
+ }
+
+ public static void callMethodThatThrows() {
+ // Ensures we get deoptimization requests.
+ Main.forceInterpreterOnThread();
+ throw new Error("");
+ }
+
+ public static native void forceInterpreterOnThread();
+ public static native void setAsyncExceptionsThrown();
+
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/845-data-image/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/845-data-image/expected-stderr.txt
diff --git a/test/845-data-image/expected-stdout.txt b/test/845-data-image/expected-stdout.txt
new file mode 100644
index 0000000..8db7853
--- /dev/null
+++ b/test/845-data-image/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+JNI_OnLoad called
diff --git a/test/845-data-image/info.txt b/test/845-data-image/info.txt
new file mode 100644
index 0000000..dfcd0dd
--- /dev/null
+++ b/test/845-data-image/info.txt
@@ -0,0 +1 @@
+Test the generation of app image at runtime.
diff --git a/test/845-data-image/run.py b/test/845-data-image/run.py
new file mode 100644
index 0000000..37c990f
--- /dev/null
+++ b/test/845-data-image/run.py
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 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 sys
+
+# We run the tests by disabling compilation with app image and forcing
+# relocation for better testing.
+# Run the test twice: one run for generating the image, a second run for using
+# the image.
+def run(ctx, args):
+ ctx.default_run(args, app_image=False, relocate=True)
+ # Pass another argument to let the test know it should now expect an image.
+ ctx.default_run(args, app_image=False, relocate=True, test_args=["--second-run"])
diff --git a/test/845-data-image/src-art/Main.java b/test/845-data-image/src-art/Main.java
new file mode 100644
index 0000000..71dd770
--- /dev/null
+++ b/test/845-data-image/src-art/Main.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2022 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 dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.concurrent.CyclicBarrier;
+
+// Add an interface for testing generating classes and interfaces.
+interface Itf {
+ public int someMethod();
+ public default int someDefaultMethod() { return 42; }
+}
+
+// Add a second interface with many methods to force a conflict in the IMT. We want a second
+// interface to make sure `Itf` gets entries with the imt_unimplemented_method runtime method.
+interface Itf2 {
+ default int defaultMethod1() { return 1; }
+ default int defaultMethod2() { return 2; }
+ default int defaultMethod3() { return 3; }
+ default int defaultMethod4() { return 4; }
+ default int defaultMethod5() { return 5; }
+ default int defaultMethod6() { return 6; }
+ default int defaultMethod7() { return 7; }
+ default int defaultMethod8() { return 8; }
+ default int defaultMethod9() { return 9; }
+ default int defaultMethod10() { return 10; }
+ default int defaultMethod11() { return 11; }
+ default int defaultMethod12() { return 12; }
+ default int defaultMethod13() { return 13; }
+ default int defaultMethod14() { return 14; }
+ default int defaultMethod15() { return 15; }
+ default int defaultMethod16() { return 16; }
+ default int defaultMethod17() { return 17; }
+ default int defaultMethod18() { return 18; }
+ default int defaultMethod19() { return 19; }
+ default int defaultMethod20() { return 20; }
+ default int defaultMethod21() { return 21; }
+ default int defaultMethod22() { return 22; }
+ default int defaultMethod23() { return 23; }
+ default int defaultMethod24() { return 24; }
+ default int defaultMethod25() { return 25; }
+ default int defaultMethod26() { return 26; }
+ default int defaultMethod27() { return 27; }
+ default int defaultMethod28() { return 28; }
+ default int defaultMethod29() { return 29; }
+ default int defaultMethod30() { return 30; }
+ default int defaultMethod31() { return 31; }
+ default int defaultMethod32() { return 32; }
+ default int defaultMethod33() { return 33; }
+ default int defaultMethod34() { return 34; }
+ default int defaultMethod35() { return 35; }
+ default int defaultMethod36() { return 36; }
+ default int defaultMethod37() { return 37; }
+ default int defaultMethod38() { return 38; }
+ default int defaultMethod39() { return 39; }
+ default int defaultMethod40() { return 40; }
+ default int defaultMethod41() { return 41; }
+ default int defaultMethod42() { return 42; }
+ default int defaultMethod43() { return 43; }
+ default int defaultMethod44() { return 44; }
+ default int defaultMethod45() { return 45; }
+ default int defaultMethod46() { return 46; }
+ default int defaultMethod47() { return 47; }
+ default int defaultMethod48() { return 48; }
+ default int defaultMethod49() { return 49; }
+ default int defaultMethod50() { return 50; }
+ default int defaultMethod51() { return 51; }
+}
+
+class Itf2Impl implements Itf2 {
+}
+
+public class Main implements Itf {
+ static String myString = "MyString";
+
+ static class MyThread extends Thread {
+ CyclicBarrier barrier;
+
+ public MyThread(CyclicBarrier barrier) {
+ this.barrier = barrier;
+ }
+ public void run() {
+ try {
+ synchronized (Main.myString) {
+ barrier.await();
+ barrier.reset();
+ // Infinite wait.
+ barrier.await();
+ }
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ // Register the dex file so that the runtime can pick up which
+ // dex file to compile for the image.
+ File file = null;
+ try {
+ file = createTempFile();
+ String codePath = DEX_LOCATION + "/845-data-image.jar";
+ VMRuntime.registerAppInfo(
+ "test.app",
+ file.getPath(),
+ file.getPath(),
+ new String[] {codePath},
+ VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ if (!hasOatFile() || !hasImage()) {
+ // We only generate an app image if there is at least a vdex file and a boot image.
+ return;
+ }
+
+ if (args.length == 2 && "--second-run".equals(args[1])) {
+ DexFile.OptimizationInfo info = VMRuntime.getBaseApkOptimizationInfo();
+ if (!info.isOptimized()) {
+ throw new Error("Expected image to be loaded");
+ }
+ }
+
+ runClassTests();
+
+ // Test that we emit an empty lock word. If we are not, then this synchronized call here would
+ // block on a run with the runtime image.
+ synchronized (myString) {
+ }
+
+ // Create a thread that makes sure `myString` is locked while the main thread is generating
+ // the runtime image.
+ CyclicBarrier barrier = new CyclicBarrier(2);
+ Thread t = new MyThread(barrier);
+ t.setDaemon(true);
+ t.start();
+ barrier.await();
+
+ VMRuntime runtime = VMRuntime.getRuntime();
+ runtime.notifyStartupCompleted();
+
+ String filter = getCompilerFilter(Main.class);
+ if ("speed-profile".equals(filter) || "speed".equals(filter)) {
+ // We only generate an app image for filters that don't compile.
+ return;
+ }
+
+ // Wait for the file to be generated.
+ File image = new File(
+ DEX_LOCATION + "/845-data-image.art" + (runtime.is64Bit() ? "64" : "32"));
+ while (!image.exists()) {
+ Thread.yield();
+ }
+ }
+
+ static class MyProxy implements InvocationHandler {
+
+ private Object obj;
+
+ public static Object newInstance(Object obj) {
+ return java.lang.reflect.Proxy.newProxyInstance(
+ obj.getClass().getClassLoader(),
+ obj.getClass().getInterfaces(),
+ new MyProxy(obj));
+ }
+
+ private MyProxy(Object obj) {
+ this.obj = obj;
+ }
+
+ public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
+ return m.invoke(obj, args);
+ }
+ }
+
+ public static Itf itf = new Main();
+ public static Itf2 itf2 = new Itf2Impl();
+
+ public static void runClassTests() {
+ // Test Class.getName, app images expect all strings to have hash codes.
+ assertEquals("Main", Main.class.getName());
+
+ // Basic tests for invokes with a copied method.
+ assertEquals(3, new Main().someMethod());
+ assertEquals(42, new Main().someDefaultMethod());
+
+ assertEquals(3, itf.someMethod());
+ assertEquals(42, itf.someDefaultMethod());
+
+ // Test with a proxy class.
+ Itf foo = (Itf) MyProxy.newInstance(new Main());
+ assertEquals(3, foo.someMethod());
+ assertEquals(42, foo.someDefaultMethod());
+
+ // Call all interface methods to trigger the creation of a imt conflict method.
+ itf2.defaultMethod1();
+ itf2.defaultMethod2();
+ itf2.defaultMethod3();
+ itf2.defaultMethod4();
+ itf2.defaultMethod5();
+ itf2.defaultMethod6();
+ itf2.defaultMethod7();
+ itf2.defaultMethod8();
+ itf2.defaultMethod9();
+ itf2.defaultMethod10();
+ itf2.defaultMethod11();
+ itf2.defaultMethod12();
+ itf2.defaultMethod13();
+ itf2.defaultMethod14();
+ itf2.defaultMethod15();
+ itf2.defaultMethod16();
+ itf2.defaultMethod17();
+ itf2.defaultMethod18();
+ itf2.defaultMethod19();
+ itf2.defaultMethod20();
+ itf2.defaultMethod21();
+ itf2.defaultMethod22();
+ itf2.defaultMethod23();
+ itf2.defaultMethod24();
+ itf2.defaultMethod25();
+ itf2.defaultMethod26();
+ itf2.defaultMethod27();
+ itf2.defaultMethod28();
+ itf2.defaultMethod29();
+ itf2.defaultMethod30();
+ itf2.defaultMethod31();
+ itf2.defaultMethod32();
+ itf2.defaultMethod33();
+ itf2.defaultMethod34();
+ itf2.defaultMethod35();
+ itf2.defaultMethod36();
+ itf2.defaultMethod37();
+ itf2.defaultMethod38();
+ itf2.defaultMethod39();
+ itf2.defaultMethod40();
+ itf2.defaultMethod41();
+ itf2.defaultMethod42();
+ itf2.defaultMethod43();
+ itf2.defaultMethod44();
+ itf2.defaultMethod45();
+ itf2.defaultMethod46();
+ itf2.defaultMethod47();
+ itf2.defaultMethod48();
+ itf2.defaultMethod49();
+ itf2.defaultMethod50();
+ itf2.defaultMethod51();
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ private static void assertEquals(String expected, String actual) {
+ if (!expected.equals(actual)) {
+ throw new Error("Expected \"" + expected + "\", got \"" + actual + "\"");
+ }
+ }
+
+ public int someMethod() {
+ return 3;
+ }
+
+ private static native boolean hasOatFile();
+ private static native boolean hasImage();
+ private static native String getCompilerFilter(Class<?> cls);
+
+ private static final String TEMP_FILE_NAME_PREFIX = "temp";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+ private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}
diff --git a/test/845-fast-verify/845-fast-verify.jar b/test/845-fast-verify/845-fast-verify.jar
new file mode 100644
index 0000000..d94777a
--- /dev/null
+++ b/test/845-fast-verify/845-fast-verify.jar
Binary files differ
diff --git a/test/845-fast-verify/build.py b/test/845-fast-verify/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/845-fast-verify/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/845-fast-verify/classes.dm b/test/845-fast-verify/classes.dm
new file mode 100644
index 0000000..c2c8559
--- /dev/null
+++ b/test/845-fast-verify/classes.dm
Binary files differ
diff --git a/test/089-many-methods/expected-stderr.txt b/test/845-fast-verify/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/845-fast-verify/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/845-fast-verify/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/845-fast-verify/expected-stdout.txt
diff --git a/test/845-fast-verify/info.txt b/test/845-fast-verify/info.txt
new file mode 100644
index 0000000..678ec3f
--- /dev/null
+++ b/test/845-fast-verify/info.txt
@@ -0,0 +1,46 @@
+Regression test for the combination of dex2oat using:
+- jar with multidex
+- vdex file where one dex file fails to fast verify (for example because of a
+ boot classpath change)
+- dex files being compiled individually
+
+We used to crash in CompilerDriver::FastVerify, assuming that only FastVerify
+can update the compiled_classes_ map. However, this isn't the case if one of the
+dex file ended up needing full verification.
+
+We need prebuilts of the .jar and .dm file as we rely on the bootclasspath to
+change which isn't expressable in a run-test. So we locally modified
+android.system.Int32Ref to inherit java.util.HashMap.
+
+The code that was used to generate the prebuilts is as follows:
+
+
+file Main.java in classes.dex:
+
+import java.util.HashMap;
+import android.system.Int32Ref;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try {
+ FailVerification.foo();
+ throw new Exception("Expected error");
+ } catch (Error expected) {
+ }
+ }
+}
+
+class FailVerification extends Foo {
+
+ public static void foo() {
+ Int32Ref ref = new Int32Ref(42);
+ takeHashMap(ref);
+ }
+
+ public static void takeHashMap(HashMap m) {}
+}
+
+file Foo.java in classes2.dex:
+
+public class Foo {
+}
diff --git a/test/845-fast-verify/run.py b/test/845-fast-verify/run.py
new file mode 100644
index 0000000..f27ec80
--- /dev/null
+++ b/test/845-fast-verify/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ # Disable app image to make sure we compile dex files individually.
+ app_image=False,
+ # Pass a .dm file to run FastVerify and ask to compile dex files
+ # individually in order to run the problematic code.
+ Xcompiler_option=[f"--dm-file={ctx.env.DEX_LOCATION}/classes.dm", "--compile-individually"])
diff --git a/test/089-many-methods/expected-stderr.txt b/test/846-multidex-data-image/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/846-multidex-data-image/expected-stderr.txt
diff --git a/test/846-multidex-data-image/expected-stdout.txt b/test/846-multidex-data-image/expected-stdout.txt
new file mode 100644
index 0000000..8db7853
--- /dev/null
+++ b/test/846-multidex-data-image/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+JNI_OnLoad called
diff --git a/test/846-multidex-data-image/info.txt b/test/846-multidex-data-image/info.txt
new file mode 100644
index 0000000..3b83d2c
--- /dev/null
+++ b/test/846-multidex-data-image/info.txt
@@ -0,0 +1,2 @@
+Test the generation of app image at runtime, when the primary APK contains
+multiple dex files.
diff --git a/test/846-multidex-data-image/run.py b/test/846-multidex-data-image/run.py
new file mode 100644
index 0000000..3976af2
--- /dev/null
+++ b/test/846-multidex-data-image/run.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 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 sys
+
+# We run the tests by disabling compilation with app image.
+# Run the test twice: one run for generating the image, a second run for using
+# the image.
+def run(ctx, args):
+ ctx.default_run(args, app_image=False)
+ # Pass another argument to let the test know it should now expect an image.
+ ctx.default_run(args, app_image=False, test_args=["--second-run"])
diff --git a/test/846-multidex-data-image/src-art/Main.java b/test/846-multidex-data-image/src-art/Main.java
new file mode 100644
index 0000000..1169c53
--- /dev/null
+++ b/test/846-multidex-data-image/src-art/Main.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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 dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+import java.io.File;
+import java.io.IOException;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ // Register the dex file so that the runtime can pick up which
+ // dex file to compile for the image.
+ File file = null;
+ try {
+ file = createTempFile();
+ String codePath = DEX_LOCATION + "/846-multidex-data-image.jar";
+ VMRuntime.registerAppInfo(
+ "test.app",
+ file.getPath(),
+ file.getPath(),
+ new String[] {codePath},
+ VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ if (!hasOatFile() || !hasImage()) {
+ // We only generate an app image if there is at least a vdex file and a boot image.
+ return;
+ }
+
+ if (args.length == 2 && "--second-run".equals(args[1])) {
+ DexFile.OptimizationInfo info = VMRuntime.getBaseApkOptimizationInfo();
+ if (!info.isOptimized()) {
+ throw new Error("Expected image to be loaded");
+ }
+ }
+
+ VMRuntime runtime = VMRuntime.getRuntime();
+ runtime.notifyStartupCompleted();
+
+ String filter = getCompilerFilter(Main.class);
+ if ("speed-profile".equals(filter) || "speed".equals(filter)) {
+ // We only generate an app image for filters that don't compile.
+ return;
+ }
+
+ // Wait for the file to be generated.
+ File image = new File(
+ DEX_LOCATION + "/846-multidex-data-image.art" + (runtime.is64Bit() ? "64" : "32"));
+ while (!image.exists()) {
+ Thread.yield();
+ }
+
+ // Test that we can load a class from the other dex file. We do this after creating the image to
+ // check that the runtime can deal with a missing dex cache.
+ Class.forName("Foo");
+ }
+
+ private static native boolean hasOatFile();
+ private static native boolean hasImage();
+ private static native String getCompilerFilter(Class<?> cls);
+
+ private static final String TEMP_FILE_NAME_PREFIX = "temp";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+ private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}
diff --git a/test/846-multidex-data-image/src-multidex/Foo.java b/test/846-multidex-data-image/src-multidex/Foo.java
new file mode 100644
index 0000000..4e36e88
--- /dev/null
+++ b/test/846-multidex-data-image/src-multidex/Foo.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+public class Foo {
+}
diff --git a/test/900-hello-plugin/run b/test/900-hello-plugin/run
deleted file mode 100755
index a19a38c..0000000
--- a/test/900-hello-plugin/run
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-plugin=libartagentd.so
-if [[ "$@" == *"-O"* ]]; then
- plugin=libartagent.so
-fi
-
-# Adjust the agent path when running on device.
-if [[ "$@" != *"--host"* ]]; then
- if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- echo 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
- fi
-
- bitness_flag=--32
- if [[ "$@" == *"--64"* ]]; then
- bitness_flag=--64
- fi
-
- # Path to native libraries installed on the device for testing purposes.
- test_native_lib_path=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-test-native-lib-path" \
- "$bitness_flag")
-
- # The linker configuration used for dalvikvm(64) in the ART APEX requires us
- # to pass the full path to the agent to the runtime when running on device.
- plugin=${test_native_lib_path}/${plugin}
-fi
-
-./default-run "$@" --runtime-option -agentpath:${plugin}=test_900 \
- --runtime-option -agentpath:${plugin}=test_900_round_2 \
- --android-runtime-option -Xplugin:${plugin}
diff --git a/test/900-hello-plugin/run.py b/test/900-hello-plugin/run.py
new file mode 100644
index 0000000..607a0e5
--- /dev/null
+++ b/test/900-hello-plugin/run.py
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ plugin = "libartagent.so" if args.O else "libartagentd.so"
+
+ # Adjust the agent path when running on device.
+ if not args.host:
+ for i, opt in enumerate(args.runtime_option):
+ if opt.startswith("-Djava.library.path="):
+ libpath = opt.split("=")[-1]
+ assert libpath.startswith("/data/nativetest"), libpath
+
+ # The linker configuration used for dalvikvm(64) in the ART APEX requires us
+ # to pass the full path to the agent to the runtime when running on device.
+ plugin = f"{libpath}/{plugin}"
+ break
+
+ ctx.default_run(
+ args,
+ runtime_option=[
+ f"-agentpath:{plugin}=test_900",
+ f"-agentpath:{plugin}=test_900_round_2"
+ ],
+ android_runtime_option=[f"-Xplugin:{plugin}"])
diff --git a/test/901-hello-ti-agent/run b/test/901-hello-ti-agent/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/901-hello-ti-agent/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/901-hello-ti-agent/run.py b/test/901-hello-ti-agent/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/901-hello-ti-agent/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/902-hello-transformation/run b/test/902-hello-transformation/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/902-hello-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/902-hello-transformation/run.py b/test/902-hello-transformation/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/902-hello-transformation/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/903-hello-tagging/run b/test/903-hello-tagging/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/903-hello-tagging/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/903-hello-tagging/run.py b/test/903-hello-tagging/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/903-hello-tagging/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/904-object-allocation/run b/test/904-object-allocation/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/904-object-allocation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/904-object-allocation/run.py b/test/904-object-allocation/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/904-object-allocation/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/905-object-free/expected-stdout.txt b/test/905-object-free/expected-stdout.txt
index dfcd7b6..50f11e0 100644
--- a/test/905-object-free/expected-stdout.txt
+++ b/test/905-object-free/expected-stdout.txt
@@ -10,4 +10,4 @@
---
[]
---
-Free counts 200000 200000
+Free counts as expected
diff --git a/test/905-object-free/run b/test/905-object-free/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/905-object-free/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/905-object-free/run.py b/test/905-object-free/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/905-object-free/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/905-object-free/src/art/Test905.java b/test/905-object-free/src/art/Test905.java
index 367da99..efd83c6 100644
--- a/test/905-object-free/src/art/Test905.java
+++ b/test/905-object-free/src/art/Test905.java
@@ -23,6 +23,8 @@
import java.util.function.BiConsumer;
public class Test905 {
+ private static final boolean DALVIK_RUN = "Dalvik".equals(System.getProperty("java.vm.name"));
+
// Taken from jdwp tests.
public static class MarkerObj {
public static int cnt = 0;
@@ -81,7 +83,13 @@
run(l);
enableFreeTracking(true);
- stress();
+ if (DALVIK_RUN) {
+ stress(400000);
+ } else {
+ // For JVM the JVMTI tag handling is not running as expected for the stress test
+ // (b/252990223).
+ stress(10000);
+ }
}
private static void run(ArrayList<Object> l) {
@@ -111,6 +119,8 @@
System.out.println("---");
}
+ private static int errors = 0;
+
private static void stressAllocate(int i, BiConsumer<Integer, Object> saver) {
Object obj = new Object();
Main.setTag(obj, i);
@@ -118,10 +128,10 @@
saver.accept(i, obj);
}
- private static void stress() {
+ private static void stress(int allocations) {
getCollectedTags(0);
getCollectedTags(1);
- final int num_obj = 400000;
+ final int num_obj = allocations;
final Object[] saved = new Object[num_obj/2];
// Allocate objects, Save every other one. We want to be sure that it's only the deleted objects
// that get their tags cleared and non-deleted objects correctly keep track of their tags.
@@ -139,10 +149,15 @@
Arrays.sort(freedTags1);
Arrays.sort(freedTags2);
// Make sure we freed all the ones we expect to and both envs agree on this.
- System.out.println("Free counts " + freedTags1.length + " " + freedTags2.length);
+ if (freedTags1.length == num_obj / 2 && freedTags2.length == num_obj / 2) {
+ System.out.println("Free counts as expected");
+ } else {
+ System.out.println("Free counts " + freedTags1.length + " " + freedTags2.length);
+ }
for (int i = 0; i < freedTags1.length; ++i) {
if (freedTags1[i] + 1 != freedTags2[i]) {
System.out.println("Mismatched tags " + (freedTags1[i] + 1) + " " + freedTags2[i]);
+ break;
}
}
// Make sure the saved-tags aren't present.
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 521f9a6..f0a6624 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -198,7 +198,7 @@
void* user_data) {
FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
if (*tag_ptr == p->tag_to_find) {
- size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
+ size_t utf_byte_count = ti::CountModifiedUtf8BytesInUtf16(value, value_length);
std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
memset(mod_utf.get(), 0, utf_byte_count + 1);
ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
diff --git a/test/906-iterate-heap/run b/test/906-iterate-heap/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/906-iterate-heap/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/906-iterate-heap/run.py b/test/906-iterate-heap/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/906-iterate-heap/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/907-get-loaded-classes/run b/test/907-get-loaded-classes/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/907-get-loaded-classes/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/907-get-loaded-classes/run.py b/test/907-get-loaded-classes/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/907-get-loaded-classes/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/908-gc-start-finish/run b/test/908-gc-start-finish/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/908-gc-start-finish/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/908-gc-start-finish/run.py b/test/908-gc-start-finish/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/908-gc-start-finish/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/909-attach-agent/expected-stdout.interpreter.txt b/test/909-attach-agent/expected-stdout.interpreter.txt
new file mode 100644
index 0000000..fef7cc9
--- /dev/null
+++ b/test/909-attach-agent/expected-stdout.interpreter.txt
@@ -0,0 +1,26 @@
+JNI_OnLoad called
+Hello, world!
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Goodbye!
+JNI_OnLoad called
+Hello, world!
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Goodbye!
+JNI_OnLoad called
+Hello, world!
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Goodbye!
+JNI_OnLoad called
+Hello, world!
+Can't attach agent, process is not debuggable.
+Can't attach agent, process is not debuggable.
+Goodbye!
diff --git a/test/909-attach-agent/interpreter-expected.patch b/test/909-attach-agent/interpreter-expected.patch
deleted file mode 100644
index 5035c6a..0000000
--- a/test/909-attach-agent/interpreter-expected.patch
+++ /dev/null
@@ -1,4 +0,0 @@
-19d18
-< version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0
-22d20
-< version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
deleted file mode 100755
index 71b1e1c..0000000
--- a/test/909-attach-agent/run
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-plugin=libopenjdkjvmtid.so
-agent=libtiagentd.so
-if [[ "$@" == *"-O"* ]]; then
- agent=libtiagent.so
- plugin=libopenjdkjvmti.so
-fi
-
-if [[ "$@" == *"--interpreter"* ]]; then
- # On interpreter we are fully capable of providing the full jvmti api so we
- # have a slightly different expected output.
- # TODO We should really be changing this in the 'check' script.
- patch -s expected-stdout.txt <interpreter-expected.patch
-fi
-
-# Provide additional runtime options when running on device.
-extra_runtime_options=
-if [[ "$@" != *"--host"* ]]; then
- if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- echo 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
- fi
-
- bitness_flag=--32
- if [[ "$@" == *"--64"* ]]; then
- bitness_flag=--64
- fi
-
- # Path to native libraries installed on the device for testing purposes.
- test_native_lib_path=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-test-native-lib-path" \
- "$bitness_flag")
-
- # The linker configuration used for dalvikvm(64) in the ART APEX requires us
- # to pass the full path to the agent to the runtime when running on device.
- agent=${test_native_lib_path}/${agent}
-
- # The above agent path is an absolute one; append the root directory to the
- # library path so that the agent can be found via the `java.library.path`
- # system property (see method `Main.find` in
- # test/909-attach-agent/src-art/Main.java).
- extra_runtime_options="--runtime-option -Djava.library.path=${test_native_lib_path}:/"
-fi
-
-export ANDROID_LOG_TAGS='*:f'
-./default-run "$@" --android-runtime-option -Xplugin:${plugin} \
- --android-runtime-option -Xcompiler-option \
- --android-runtime-option --debuggable \
- $extra_runtime_options \
- --args agent:${agent}=909-attach-agent
-return_status1=$?
-
-./default-run "$@" --android-runtime-option -Xcompiler-option \
- --android-runtime-option --debuggable \
- $extra_runtime_options \
- --args agent:${agent}=909-attach-agent
-return_status2=$?
-
-./default-run "$@" $extra_runtime_options \
- --args agent:${agent}=909-attach-agent \
- --external-log-tags
-return_status3=$?
-
-./default-run "$@" $extra_runtime_options \
- --args agent:${agent}=909-attach-agent \
- --args disallow-debugging \
- --external-log-tags
-return_status4=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && \
- (exit $return_status2) && \
- (exit $return_status3) && \
- (exit $return_status4)
diff --git a/test/909-attach-agent/run.py b/test/909-attach-agent/run.py
new file mode 100644
index 0000000..b5e2337
--- /dev/null
+++ b/test/909-attach-agent/run.py
@@ -0,0 +1,62 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ agent = "libtiagent.so" if args.O else "libtiagentd.so"
+ plugin = "libopenjdkjvmti.so" if args.O else "libopenjdkjvmtid.so"
+
+ if args.interpreter:
+ # On interpreter we are fully capable of providing the full jvmti api so we
+ # have a slightly different expected output.
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".interpreter.txt")
+
+ # Provide additional runtime options when running on device.
+ if not args.host:
+ for i, opt in enumerate(args.runtime_option):
+ if opt.startswith("-Djava.library.path="):
+ libpath = opt.split("=")[-1]
+ assert libpath.startswith("/data/nativetest"), libpath
+
+ # The linker configuration used for dalvikvm(64) in the ART APEX requires us
+ # to pass the full path to the agent to the runtime when running on device.
+ agent = f"{libpath}/{agent}"
+
+ # The above agent path is an absolute one; append the root directory to the
+ # library path so that the agent can be found via the `java.library.path`
+ # system property (see method `Main.find` in
+ # test/909-attach-agent/src-art/Main.java).
+ args.runtime_option[i] += ":/"
+ break
+
+ ctx.default_run(
+ args,
+ android_runtime_option=[
+ f"-Xplugin:{plugin}", "-Xcompiler-option", "--debuggable"
+ ],
+ test_args=[f"agent:{agent}=909-attach-agent"])
+
+ ctx.default_run(args, test_args=[f"agent:{agent}=909-attach-agent"])
+
+ ctx.default_run(
+ args,
+ test_args=[f"agent:{agent}=909-attach-agent"],
+ android_log_tags="*:f")
+
+ ctx.default_run(
+ args,
+ test_args=[f"agent:{agent}=909-attach-agent", "disallow-debugging"],
+ android_log_tags="*:f")
diff --git a/test/910-methods/run b/test/910-methods/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/910-methods/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/910-methods/run.py b/test/910-methods/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/910-methods/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/911-get-stack-trace/expected-cts-version.txt b/test/911-get-stack-trace/expected-cts-version.txt
index 728ea88..25f29aa 100644
--- a/test/911-get-stack-trace/expected-cts-version.txt
+++ b/test/911-get-stack-trace/expected-cts-version.txt
@@ -79,8 +79,8 @@
From top
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -99,8 +99,8 @@
foo (IIILart/ControlData;)I 0 21
run ()V 4 28
---------
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -120,14 +120,14 @@
run ()V 4 28
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
---------
- wait ()V 2 568
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -135,7 +135,7 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
bar (IIILart/ControlData;)J 0 26
---------
- wait ()V 2 568
+ wait ()V 2 524
From bottom
---------
run ()V 4 28
@@ -251,8 +251,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -261,8 +261,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -271,8 +271,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -281,8 +281,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -291,8 +291,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -308,8 +308,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -331,8 +331,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -354,8 +354,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -377,8 +377,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -400,8 +400,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
diff --git a/test/911-get-stack-trace/expected-stdout.txt b/test/911-get-stack-trace/expected-stdout.txt
index 19735d2..1109b3f 100644
--- a/test/911-get-stack-trace/expected-stdout.txt
+++ b/test/911-get-stack-trace/expected-stdout.txt
@@ -79,8 +79,8 @@
From top
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -99,8 +99,8 @@
foo (IIILart/ControlData;)I 0 21
run ()V 4 28
---------
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -120,14 +120,14 @@
run ()V 4 28
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
---------
- wait ()V 2 568
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -135,7 +135,7 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
bar (IIILart/ControlData;)J 0 26
---------
- wait ()V 2 568
+ wait ()V 2 524
From bottom
---------
run ()V 4 28
@@ -276,8 +276,8 @@
---------
AllTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -286,8 +286,8 @@
---------
AllTraces Thread 1
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -296,8 +296,8 @@
---------
AllTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -306,8 +306,8 @@
---------
AllTraces Thread 3
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -316,8 +316,8 @@
---------
AllTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -326,8 +326,8 @@
---------
AllTraces Thread 5
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -336,8 +336,8 @@
---------
AllTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -346,8 +346,8 @@
---------
AllTraces Thread 7
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -356,8 +356,8 @@
---------
AllTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -366,8 +366,8 @@
---------
AllTraces Thread 9
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -401,8 +401,8 @@
---------
AllTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -424,8 +424,8 @@
---------
AllTraces Thread 1
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -447,8 +447,8 @@
---------
AllTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -470,8 +470,8 @@
---------
AllTraces Thread 3
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -493,8 +493,8 @@
---------
AllTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -516,8 +516,8 @@
---------
AllTraces Thread 5
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -539,8 +539,8 @@
---------
AllTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -562,8 +562,8 @@
---------
AllTraces Thread 7
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -585,8 +585,8 @@
---------
AllTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -608,8 +608,8 @@
---------
AllTraces Thread 9
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -685,8 +685,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -695,8 +695,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -705,8 +705,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -715,8 +715,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -725,8 +725,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -742,8 +742,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -765,8 +765,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -788,8 +788,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -811,8 +811,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -834,8 +834,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
diff --git a/test/911-get-stack-trace/run b/test/911-get-stack-trace/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/911-get-stack-trace/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/911-get-stack-trace/run.py b/test/911-get-stack-trace/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/911-get-stack-trace/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/912-classes/expected-stdout.txt b/test/912-classes/expected-stdout.txt
index 5da9f8c..787efc5 100644
--- a/test/912-classes/expected-stdout.txt
+++ b/test/912-classes/expected-stdout.txt
@@ -23,7 +23,7 @@
[public static final int java.lang.Integer.BYTES, static final byte[] java.lang.Integer.DigitOnes, static final byte[] java.lang.Integer.DigitTens, public static final int java.lang.Integer.MAX_VALUE, public static final int java.lang.Integer.MIN_VALUE, public static final int java.lang.Integer.SIZE, private static final java.lang.String[] java.lang.Integer.SMALL_NEG_VALUES, private static final java.lang.String[] java.lang.Integer.SMALL_NONNEG_VALUES, public static final java.lang.Class java.lang.Integer.TYPE, static final char[] java.lang.Integer.digits, private static final long java.lang.Integer.serialVersionUID, static final int[] java.lang.Integer.sizeTable, private final int java.lang.Integer.value]
[]
[]
-[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), static void java.lang.Integer.formatUnsignedInt(int,int,byte[],int,int), static void java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static int java.lang.Integer.getChars(int,int,byte[]), static int java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()]
+[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), private static void java.lang.Integer.formatUnsignedInt(int,int,byte[],int), static void java.lang.Integer.formatUnsignedInt(int,int,byte[],int,int), static void java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static int java.lang.Integer.getChars(int,int,byte[]), static int java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()]
[]
[]
int 100000
diff --git a/test/912-classes/run b/test/912-classes/run
deleted file mode 100755
index f24db40..0000000
--- a/test/912-classes/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This test checks which classes are initiated by a classloader. App images preload classes.
-# In certain configurations, the app images may be valid even in a new classloader. Turn off
-# app images to avoid the issue.
-
-./default-run "$@" --jvmti \
- --no-app-image
diff --git a/test/912-classes/run.py b/test/912-classes/run.py
new file mode 100644
index 0000000..2f3b9fa
--- /dev/null
+++ b/test/912-classes/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test checks which classes are initiated by a classloader. App images preload classes.
+ # In certain configurations, the app images may be valid even in a new classloader. Turn off
+ # app images to avoid the issue.
+
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/913-heaps/expected-stdout.txt b/test/913-heaps/expected-stdout.txt
index 9684503..e7a372a 100644
--- a/test/913-heaps/expected-stdout.txt
+++ b/test/913-heaps/expected-stdout.txt
@@ -1,6 +1,5 @@
---
true true
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
@@ -44,7 +43,6 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
@@ -97,7 +95,6 @@
---
3@1001 --(class)--> 1001@0 [size=123456780016, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
@@ -109,7 +106,6 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
@@ -195,9 +191,7 @@
---
---
---- untagged objects
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780050, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780050, length=-1]
@@ -240,12 +234,10 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780055, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780055, length=-1]
@@ -287,8 +279,6 @@
6@1000 --(class)--> 1000@0 [size=123456780055, length=-1]
---
---- tagged classes
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780060, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1]
@@ -315,8 +305,6 @@
5@1002 --(field@8)--> 500@0 [size=20, length=2]
6@1000 --(class)--> 1000@0 [size=123456780060, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780065, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 28a737d..671cff8 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -191,6 +191,12 @@
return 0;
}
+ // Ignore system classes, which may come from the JIT compiling a method
+ // in these classes.
+ if (reference_kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) {
+ return 0;
+ }
+
// Only check tagged objects.
if (tag == 0) {
return JVMTI_VISIT_OBJECTS;
@@ -260,6 +266,7 @@
std::vector<std::string> GetLines() const {
std::vector<std::string> ret;
+ ret.reserve(lines_.size());
for (const std::unique_ptr<Elem>& e : lines_) {
ret.push_back(e->Print());
}
@@ -586,7 +593,7 @@
void* user_data) {
FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
if (*tag_ptr != 0) {
- size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
+ size_t utf_byte_count = ti::CountModifiedUtf8BytesInUtf16(value, value_length);
std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
memset(mod_utf.get(), 0, utf_byte_count + 1);
ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
diff --git a/test/913-heaps/run b/test/913-heaps/run
deleted file mode 100755
index dd35526..0000000
--- a/test/913-heaps/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti -Xcompiler-option -g
diff --git a/test/913-heaps/run.py b/test/913-heaps/run.py
new file mode 100644
index 0000000..d5731ca
--- /dev/null
+++ b/test/913-heaps/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, Xcompiler_option=["-g"])
diff --git a/test/914-hello-obsolescence/run b/test/914-hello-obsolescence/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/914-hello-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/914-hello-obsolescence/run.py b/test/914-hello-obsolescence/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/914-hello-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/915-obsolete-2/run b/test/915-obsolete-2/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/915-obsolete-2/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/915-obsolete-2/run.py b/test/915-obsolete-2/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/915-obsolete-2/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/915-obsolete-2/src/Main.java b/test/915-obsolete-2/src/Main.java
index be51234..58c4ab9 100644
--- a/test/915-obsolete-2/src/Main.java
+++ b/test/915-obsolete-2/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test915.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test915.run();
+ }
}
diff --git a/test/915-obsolete-2/src/art/Test915.java b/test/915-obsolete-2/src/art/Test915.java
index 63c7f34..2cb3c8f 100644
--- a/test/915-obsolete-2/src/art/Test915.java
+++ b/test/915-obsolete-2/src/art/Test915.java
@@ -19,105 +19,104 @@
import java.util.Base64;
public class Test915 {
+ static class Transform {
+ private void Start() {
+ System.out.println("hello - private");
+ }
- static class Transform {
- private void Start() {
- System.out.println("hello - private");
+ private void Finish() {
+ System.out.println("goodbye - private");
+ }
+
+ public void sayHi(Runnable r) {
+ System.out.println("Pre Start private method call");
+ Start();
+ System.out.println("Post Start private method call");
+ r.run();
+ System.out.println("Pre Finish private method call");
+ Finish();
+ System.out.println("Post Finish private method call");
+ }
}
- private void Finish() {
- System.out.println("goodbye - private");
+ // static class Transform {
+ // private void Start() {
+ // System.out.println("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish() {
+ // System.out.println("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r) {
+ // System.out.println("Pre Start private method call - Transformed");
+ // Start();
+ // System.out.println("Post Start private method call - Transformed");
+ // r.run();
+ // System.out.println("Pre Finish private method call - Transformed");
+ // Finish();
+ // System.out.println("Post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQANgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
+ "JwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
+ "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAM" +
+ "VGVzdDkxNS5qYXZhDAAPABAHAC0MAC4ALwEAHUhlbGxvIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVk" +
+ "BwAwDAAxADIBAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAQArUHJlIFN0YXJ0IHBy" +
+ "aXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAEwAQAQAsUG9zdCBTdGFydCBwcml2YXRl" +
+ "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHADMMADQAEAEALFByZSBGaW5pc2ggcHJpdmF0ZSBt" +
+ "ZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAUABABAC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
+ "ZCBjYWxsIC0gVHJhbnNmb3JtZWQHADUBABVhcnQvVGVzdDkxNSRUcmFuc2Zvcm0BAAlUcmFuc2Zv" +
+ "cm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEA" +
+ "A291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmlu" +
+ "dGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuAQAL" +
+ "YXJ0L1Rlc3Q5MTUAIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIA" +
+ "AAAGAAEAAAAFAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAAAAcA" +
+ "CAAIAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAoACAALAAEA" +
+ "FQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQqtwAL" +
+ "sgACEgy2AASxAAAAAQASAAAAIgAIAAAADQAIAA4ADAAPABQAEAAaABEAIgASACYAEwAuABQAAgAX" +
+ "AAAAAgAYACsAAAAKAAEADQAoACoACA==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAQ+GYcAAAAAAAAAAAAAAAAAAAAAAAAAADUBQAAcAAAAHhWNBIAAAAAAAAAABAFAAAd" +
+ "AAAAcAAAAAoAAADkAAAAAwAAAAwBAAABAAAAMAEAAAcAAAA4AQAAAQAAAHABAABEBAAAkAEAAJAB" +
+ "AACYAQAAoAEAAMEBAADgAQAA+QEAAAgCAAAsAgAATAIAAGMCAAB3AgAAjQIAAKECAAC1AgAA5AIA" +
+ "ABIDAABAAwAAbQMAAHQDAACCAwAAjQMAAJADAACUAwAAoQMAAKcDAACsAwAAtQMAALoDAADBAwAA" +
+ "BAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAAFAAAABQAAAAJAAAAAAAAABUAAAAJ" +
+ "AAAA0AMAABUAAAAJAAAAyAMAAAgABAAYAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAARAAAAAAABABsA" +
+ "AAAEAAIAGQAAAAUAAAAAAAAABgAAABoAAAAAAAAAAAAAAAUAAAAAAAAAEgAAAAAFAADMBAAAAAAA" +
+ "AAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8g" +
+ "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAF0xhcnQvVGVzdDkxNSRUcmFuc2Zvcm07AA1MYXJ0L1Rl" +
+ "c3Q5MTU7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
+ "YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" +
+ "Y3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5n" +
+ "L1N5c3RlbTsALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAs" +
+ "UG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALFByZSBGaW5pc2gg" +
+ "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtQcmUgU3RhcnQgcHJpdmF0ZSBtZXRo" +
+ "b2QgY2FsbCAtIFRyYW5zZm9ybWVkAAVTdGFydAAMVGVzdDkxNS5qYXZhAAlUcmFuc2Zvcm0AAVYA" +
+ "AlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAANydW4ABXNheUhpAAV2YWx1ZQAB" +
+ "AAAABwAAAAEAAAAGAAAABQAHDgAKAAcOAQgPAAcABw4BCA8ADQEABw4BCA8BAw8BCA8BAw8BCA8B" +
+ "Aw8BCA8AAQABAAEAAADYAwAABAAAAHAQBQAAAA4AAwABAAIAAADdAwAACQAAAGIAAAAbAQIAAABu" +
+ "IAQAEAAOAAAAAwABAAIAAADlAwAACQAAAGIAAAAbAQMAAABuIAQAEAAOAAAABAACAAIAAADtAwAA" +
+ "KgAAAGIAAAAbARAAAABuIAQAEABwEAIAAgBiAAAAGwEOAAAAbiAEABAAchAGAAMAYgAAABsBDwAA" +
+ "AG4gBAAQAHAQAQACAGIAAAAbAQ0AAABuIAQAEAAOAAAAAwEAgIAEiAgBAqAIAQLECAMB6AgAAAIC" +
+ "ARwYAQIDAhYECBcXEwACAAAA5AQAAOoEAAD0BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAA" +
+ "AAEAAAAdAAAAcAAAAAIAAAAKAAAA5AAAAAMAAAADAAAADAEAAAQAAAABAAAAMAEAAAUAAAAHAAAA" +
+ "OAEAAAYAAAABAAAAcAEAAAIgAAAdAAAAkAEAAAEQAAACAAAAyAMAAAMgAAAEAAAA2AMAAAEgAAAE" +
+ "AAAACAQAAAAgAAABAAAAzAQAAAQgAAACAAAA5AQAAAMQAAABAAAA9AQAAAYgAAABAAAAAAUAAAAQ" +
+ "AAABAAAAEAUAAA==");
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform());
}
- public void sayHi(Runnable r) {
- System.out.println("Pre Start private method call");
- Start();
- System.out.println("Post Start private method call");
- r.run();
- System.out.println("Pre Finish private method call");
- Finish();
- System.out.println("Post Finish private method call");
+ public static void doTest(Transform t) {
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ t.sayHi(() -> {
+ System.out.println("transforming calling function");
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ });
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
}
- }
-
- // static class Transform {
- // private void Start() {
- // System.out.println("Hello - private - Transformed");
- // }
- //
- // private void Finish() {
- // System.out.println("Goodbye - private - Transformed");
- // }
- //
- // public void sayHi(Runnable r) {
- // System.out.println("Pre Start private method call - Transformed");
- // Start();
- // System.out.println("Post Start private method call - Transformed");
- // r.run();
- // System.out.println("Pre Finish private method call - Transformed");
- // Finish();
- // System.out.println("Post Finish private method call - Transformed");
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQANgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
- "JwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
- "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAM" +
- "VGVzdDkxNS5qYXZhDAAPABAHAC0MAC4ALwEAHUhlbGxvIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVk" +
- "BwAwDAAxADIBAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAQArUHJlIFN0YXJ0IHBy" +
- "aXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAEwAQAQAsUG9zdCBTdGFydCBwcml2YXRl" +
- "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHADMMADQAEAEALFByZSBGaW5pc2ggcHJpdmF0ZSBt" +
- "ZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAUABABAC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
- "ZCBjYWxsIC0gVHJhbnNmb3JtZWQHADUBABVhcnQvVGVzdDkxNSRUcmFuc2Zvcm0BAAlUcmFuc2Zv" +
- "cm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEA" +
- "A291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmlu" +
- "dGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuAQAL" +
- "YXJ0L1Rlc3Q5MTUAIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIA" +
- "AAAGAAEAAAAFAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAAAAcA" +
- "CAAIAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAoACAALAAEA" +
- "FQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQqtwAL" +
- "sgACEgy2AASxAAAAAQASAAAAIgAIAAAADQAIAA4ADAAPABQAEAAaABEAIgASACYAEwAuABQAAgAX" +
- "AAAAAgAYACsAAAAKAAEADQAoACoACA==");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQAQ+GYcAAAAAAAAAAAAAAAAAAAAAAAAAADUBQAAcAAAAHhWNBIAAAAAAAAAABAFAAAd" +
- "AAAAcAAAAAoAAADkAAAAAwAAAAwBAAABAAAAMAEAAAcAAAA4AQAAAQAAAHABAABEBAAAkAEAAJAB" +
- "AACYAQAAoAEAAMEBAADgAQAA+QEAAAgCAAAsAgAATAIAAGMCAAB3AgAAjQIAAKECAAC1AgAA5AIA" +
- "ABIDAABAAwAAbQMAAHQDAACCAwAAjQMAAJADAACUAwAAoQMAAKcDAACsAwAAtQMAALoDAADBAwAA" +
- "BAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAAFAAAABQAAAAJAAAAAAAAABUAAAAJ" +
- "AAAA0AMAABUAAAAJAAAAyAMAAAgABAAYAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAARAAAAAAABABsA" +
- "AAAEAAIAGQAAAAUAAAAAAAAABgAAABoAAAAAAAAAAAAAAAUAAAAAAAAAEgAAAAAFAADMBAAAAAAA" +
- "AAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8g" +
- "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAF0xhcnQvVGVzdDkxNSRUcmFuc2Zvcm07AA1MYXJ0L1Rl" +
- "c3Q5MTU7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
- "YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" +
- "Y3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5n" +
- "L1N5c3RlbTsALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAs" +
- "UG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALFByZSBGaW5pc2gg" +
- "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtQcmUgU3RhcnQgcHJpdmF0ZSBtZXRo" +
- "b2QgY2FsbCAtIFRyYW5zZm9ybWVkAAVTdGFydAAMVGVzdDkxNS5qYXZhAAlUcmFuc2Zvcm0AAVYA" +
- "AlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAANydW4ABXNheUhpAAV2YWx1ZQAB" +
- "AAAABwAAAAEAAAAGAAAABQAHDgAKAAcOAQgPAAcABw4BCA8ADQEABw4BCA8BAw8BCA8BAw8BCA8B" +
- "Aw8BCA8AAQABAAEAAADYAwAABAAAAHAQBQAAAA4AAwABAAIAAADdAwAACQAAAGIAAAAbAQIAAABu" +
- "IAQAEAAOAAAAAwABAAIAAADlAwAACQAAAGIAAAAbAQMAAABuIAQAEAAOAAAABAACAAIAAADtAwAA" +
- "KgAAAGIAAAAbARAAAABuIAQAEABwEAIAAgBiAAAAGwEOAAAAbiAEABAAchAGAAMAYgAAABsBDwAA" +
- "AG4gBAAQAHAQAQACAGIAAAAbAQ0AAABuIAQAEAAOAAAAAwEAgIAEiAgBAqAIAQLECAMB6AgAAAIC" +
- "ARwYAQIDAhYECBcXEwACAAAA5AQAAOoEAAD0BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAA" +
- "AAEAAAAdAAAAcAAAAAIAAAAKAAAA5AAAAAMAAAADAAAADAEAAAQAAAABAAAAMAEAAAUAAAAHAAAA" +
- "OAEAAAYAAAABAAAAcAEAAAIgAAAdAAAAkAEAAAEQAAACAAAAyAMAAAMgAAAEAAAA2AMAAAEgAAAE" +
- "AAAACAQAAAAgAAABAAAAzAQAAAQgAAACAAAA5AQAAAMQAAABAAAA9AQAAAYgAAABAAAAAAUAAAAQ" +
- "AAABAAAAEAUAAA==");
-
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new Transform());
- }
-
- public static void doTest(Transform t) {
- t.sayHi(() -> { System.out.println("Not doing anything here"); });
- t.sayHi(() -> {
- System.out.println("transforming calling function");
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- });
- t.sayHi(() -> { System.out.println("Not doing anything here"); });
- }
}
diff --git a/test/916-obsolete-jit/run b/test/916-obsolete-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/916-obsolete-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/916-obsolete-jit/run.py b/test/916-obsolete-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/916-obsolete-jit/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java
index d7b32ba..f893c8e 100644
--- a/test/916-obsolete-jit/src/Main.java
+++ b/test/916-obsolete-jit/src/Main.java
@@ -23,151 +23,151 @@
public class Main {
- // import java.util.function.Consumer;
- //
- // class Transform {
- // private void Start(Consumer<String> reporter) {
- // reporter.accept("Hello - private - Transformed");
- // }
- //
- // private void Finish(Consumer<String> reporter) {
- // reporter.accept("Goodbye - private - Transformed");
- // }
- //
- // public void sayHi(Runnable r, Consumer<String> reporter) {
- // reporter.accept("pre Start private method call - Transformed");
- // Start(reporter);
- // reporter.accept("post Start private method call - Transformed");
- // r.run();
- // reporter.accept("pre Finish private method call - Transformed");
- // Finish(reporter);
- // reporter.accept("post Finish private method call - Transformed");
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAMAoADQAcCAAdCwAeAB8IACAIACEKAAwAIggAIwsAJAAlCAAmCgAMACcIACgHACkH" +
- "ACoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFU3RhcnQBACAoTGph" +
- "dmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjspVgEACVNpZ25hdHVyZQEANChMamF2YS91dGlsL2Z1" +
- "bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47KVYBAAZGaW5pc2gBAAVzYXlIaQEA" +
- "NChMamF2YS9sYW5nL1J1bm5hYmxlO0xqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAEgo" +
- "TGphdmEvbGFuZy9SdW5uYWJsZTtMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xh" +
- "bmcvU3RyaW5nOz47KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAA4ADwEAHUhlbGxv" +
- "IC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkBwArDAAsAC0BAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRy" +
- "YW5zZm9ybWVkAQArcHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwA" +
- "EgATAQAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHAC4MAC8A" +
- "DwEALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAWABMBAC1w" +
- "b3N0IEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQBAAlUcmFuc2Zvcm0B" +
- "ABBqYXZhL2xhbmcvT2JqZWN0AQAbamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAQAGYWNjZXB0" +
- "AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAADAAN" +
- "AAAAAAAEAAAADgAPAAEAEAAAAB0AAQABAAAABSq3AAGxAAAAAQARAAAABgABAAAAEwACABIAEwAC" +
- "ABAAAAAlAAIAAgAAAAkrEgK5AAMCALEAAAABABEAAAAKAAIAAAAVAAgAFgAUAAAAAgAVAAIAFgAT" +
- "AAIAEAAAACUAAgACAAAACSsSBLkAAwIAsQAAAAEAEQAAAAoAAgAAABkACAAaABQAAAACABUAAQAX" +
- "ABgAAgAQAAAAZQACAAMAAAAxLBIFuQADAgAqLLcABiwSB7kAAwIAK7kACAEALBIJuQADAgAqLLcA" +
- "CiwSC7kAAwIAsQAAAAEAEQAAACIACAAAAB0ACAAeAA0AHwAVACAAGwAhACMAIgAoACMAMAAkABQA" +
- "AAACABkAAQAaAAAAAgAb");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBc8wr9PcHqnOR61m+0kimXTSddVMToJPuYBQAAcAAAAHhWNBIAAAAAAAAAAOAEAAAc" +
- "AAAAcAAAAAYAAADgAAAABAAAAPgAAAAAAAAAAAAAAAcAAAAoAQAAAQAAAGABAAAYBAAAgAEAAHoC" +
- "AAB9AgAAgAIAAIgCAACOAgAAlgIAALcCAADWAgAA4wIAAAIDAAAWAwAALAMAAEADAABeAwAAfQMA" +
- "AIQDAACUAwAAlwMAAJsDAACgAwAAqAMAALwDAADrAwAAGQQAAEcEAAB0BAAAeQQAAIAEAAAHAAAA" +
- "CAAAAAkAAAAKAAAADQAAABAAAAAQAAAABQAAAAAAAAARAAAABQAAAGQCAAASAAAABQAAAGwCAAAR" +
- "AAAABQAAAHQCAAAAAAAAAgAAAAAAAwAEAAAAAAADAA4AAAAAAAIAGgAAAAIAAAACAAAAAwAAABkA" +
- "AAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAPAAAAPAIAAMoEAAAAAAAAAQAAAKgEAAABAAAAuAQA" +
- "AAEAAQABAAAAhwQAAAQAAABwEAQAAAAOAAMAAgACAAAAjAQAAAcAAAAbAAUAAAByIAYAAgAOAAAA" +
- "AwACAAIAAACTBAAABwAAABsABgAAAHIgBgACAA4AAAAEAAMAAgAAAJoEAAAiAAAAGwAYAAAAciAG" +
- "AAMAcCACADEAGwAWAAAAciAGAAMAchAFAAIAGwAXAAAAciAGAAMAcCABADEAGwAVAAAAciAGAAMA" +
- "DgAAAAAAAAAAAAMAAAAAAAAAAQAAAIABAAACAAAAgAEAAAMAAACIAQAAAQAAAAIAAAACAAAAAwAE" +
- "AAEAAAAEAAEoAAE8AAY8aW5pdD4ABD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
- "cmFuc2Zvcm1lZAAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1M" +
- "ZGFsdmlrL2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9s" +
- "YW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0Nv" +
- "bnN1bWVyAB1MamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5q" +
- "YXZhAAFWAAJWTAADVkxMAAZhY2NlcHQAEmVtaXR0ZXI6IGphY2stNC4xOQAtcG9zdCBGaW5pc2gg" +
- "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0" +
- "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0g" +
- "VHJhbnNmb3JtZWQAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQA" +
- "A3J1bgAFc2F5SGkABXZhbHVlABMABw4AGQEABw5pABUBAAcOaQAdAgAABw5pPGk8aTxpAAIBARsc" +
- "BRcAFwwXARcLFwMCAQEbHAYXABcKFwwXARcLFwMAAAMBAICABJADAQKoAwECyAMDAegDDwAAAAAA" +
- "AAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAGAAAA4AAAAAMAAAAEAAAA+AAAAAUAAAAHAAAAKAEA" +
- "AAYAAAABAAAAYAEAAAMQAAACAAAAgAEAAAEgAAAEAAAAkAEAAAYgAAABAAAAPAIAAAEQAAADAAAA" +
- "ZAIAAAIgAAAcAAAAegIAAAMgAAAEAAAAhwQAAAQgAAACAAAAqAQAAAAgAAABAAAAygQAAAAQAAAB" +
- "AAAA4AQAAA==");
+ // import java.util.function.Consumer;
+ //
+ // class Transform {
+ // private void Start(Consumer<String> reporter) {
+ // reporter.accept("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish(Consumer<String> reporter) {
+ // reporter.accept("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r, Consumer<String> reporter) {
+ // reporter.accept("pre Start private method call - Transformed");
+ // Start(reporter);
+ // reporter.accept("post Start private method call - Transformed");
+ // r.run();
+ // reporter.accept("pre Finish private method call - Transformed");
+ // Finish(reporter);
+ // reporter.accept("post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAMAoADQAcCAAdCwAeAB8IACAIACEKAAwAIggAIwsAJAAlCAAmCgAMACcIACgHACkH" +
+ "ACoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFU3RhcnQBACAoTGph" +
+ "dmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjspVgEACVNpZ25hdHVyZQEANChMamF2YS91dGlsL2Z1" +
+ "bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47KVYBAAZGaW5pc2gBAAVzYXlIaQEA" +
+ "NChMamF2YS9sYW5nL1J1bm5hYmxlO0xqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAEgo" +
+ "TGphdmEvbGFuZy9SdW5uYWJsZTtMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xh" +
+ "bmcvU3RyaW5nOz47KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAA4ADwEAHUhlbGxv" +
+ "IC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkBwArDAAsAC0BAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRy" +
+ "YW5zZm9ybWVkAQArcHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwA" +
+ "EgATAQAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHAC4MAC8A" +
+ "DwEALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAWABMBAC1w" +
+ "b3N0IEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQBAAlUcmFuc2Zvcm0B" +
+ "ABBqYXZhL2xhbmcvT2JqZWN0AQAbamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAQAGYWNjZXB0" +
+ "AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAADAAN" +
+ "AAAAAAAEAAAADgAPAAEAEAAAAB0AAQABAAAABSq3AAGxAAAAAQARAAAABgABAAAAEwACABIAEwAC" +
+ "ABAAAAAlAAIAAgAAAAkrEgK5AAMCALEAAAABABEAAAAKAAIAAAAVAAgAFgAUAAAAAgAVAAIAFgAT" +
+ "AAIAEAAAACUAAgACAAAACSsSBLkAAwIAsQAAAAEAEQAAAAoAAgAAABkACAAaABQAAAACABUAAQAX" +
+ "ABgAAgAQAAAAZQACAAMAAAAxLBIFuQADAgAqLLcABiwSB7kAAwIAK7kACAEALBIJuQADAgAqLLcA" +
+ "CiwSC7kAAwIAsQAAAAEAEQAAACIACAAAAB0ACAAeAA0AHwAVACAAGwAhACMAIgAoACMAMAAkABQA" +
+ "AAACABkAAQAaAAAAAgAb");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBc8wr9PcHqnOR61m+0kimXTSddVMToJPuYBQAAcAAAAHhWNBIAAAAAAAAAAOAEAAAc" +
+ "AAAAcAAAAAYAAADgAAAABAAAAPgAAAAAAAAAAAAAAAcAAAAoAQAAAQAAAGABAAAYBAAAgAEAAHoC" +
+ "AAB9AgAAgAIAAIgCAACOAgAAlgIAALcCAADWAgAA4wIAAAIDAAAWAwAALAMAAEADAABeAwAAfQMA" +
+ "AIQDAACUAwAAlwMAAJsDAACgAwAAqAMAALwDAADrAwAAGQQAAEcEAAB0BAAAeQQAAIAEAAAHAAAA" +
+ "CAAAAAkAAAAKAAAADQAAABAAAAAQAAAABQAAAAAAAAARAAAABQAAAGQCAAASAAAABQAAAGwCAAAR" +
+ "AAAABQAAAHQCAAAAAAAAAgAAAAAAAwAEAAAAAAADAA4AAAAAAAIAGgAAAAIAAAACAAAAAwAAABkA" +
+ "AAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAPAAAAPAIAAMoEAAAAAAAAAQAAAKgEAAABAAAAuAQA" +
+ "AAEAAQABAAAAhwQAAAQAAABwEAQAAAAOAAMAAgACAAAAjAQAAAcAAAAbAAUAAAByIAYAAgAOAAAA" +
+ "AwACAAIAAACTBAAABwAAABsABgAAAHIgBgACAA4AAAAEAAMAAgAAAJoEAAAiAAAAGwAYAAAAciAG" +
+ "AAMAcCACADEAGwAWAAAAciAGAAMAchAFAAIAGwAXAAAAciAGAAMAcCABADEAGwAVAAAAciAGAAMA" +
+ "DgAAAAAAAAAAAAMAAAAAAAAAAQAAAIABAAACAAAAgAEAAAMAAACIAQAAAQAAAAIAAAACAAAAAwAE" +
+ "AAEAAAAEAAEoAAE8AAY8aW5pdD4ABD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
+ "cmFuc2Zvcm1lZAAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1M" +
+ "ZGFsdmlrL2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9s" +
+ "YW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0Nv" +
+ "bnN1bWVyAB1MamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5q" +
+ "YXZhAAFWAAJWTAADVkxMAAZhY2NlcHQAEmVtaXR0ZXI6IGphY2stNC4xOQAtcG9zdCBGaW5pc2gg" +
+ "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0" +
+ "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0g" +
+ "VHJhbnNmb3JtZWQAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQA" +
+ "A3J1bgAFc2F5SGkABXZhbHVlABMABw4AGQEABw5pABUBAAcOaQAdAgAABw5pPGk8aTxpAAIBARsc" +
+ "BRcAFwwXARcLFwMCAQEbHAYXABcKFwwXARcLFwMAAAMBAICABJADAQKoAwECyAMDAegDDwAAAAAA" +
+ "AAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAGAAAA4AAAAAMAAAAEAAAA+AAAAAUAAAAHAAAAKAEA" +
+ "AAYAAAABAAAAYAEAAAMQAAACAAAAgAEAAAEgAAAEAAAAkAEAAAYgAAABAAAAPAIAAAEQAAADAAAA" +
+ "ZAIAAAIgAAAcAAAAegIAAAMgAAAEAAAAhwQAAAQgAAACAAAAqAQAAAAgAAABAAAAygQAAAAQAAAB" +
+ "AAAA4AQAAA==");
- // A class that we can use to keep track of the output of this test.
- private static class TestWatcher implements Consumer<String> {
- private StringBuilder sb;
- public TestWatcher() {
- sb = new StringBuilder();
+ // A class that we can use to keep track of the output of this test.
+ private static class TestWatcher implements Consumer<String> {
+ private StringBuilder sb;
+ public TestWatcher() {
+ sb = new StringBuilder();
+ }
+
+ @Override
+ public void accept(String s) {
+ sb.append(s);
+ sb.append('\n');
+ }
+
+ public String getOutput() {
+ return sb.toString();
+ }
+
+ public void clear() {
+ sb = new StringBuilder();
+ }
}
- @Override
- public void accept(String s) {
- sb.append(s);
- sb.append('\n');
+ public static void main(String[] args) {
+ doTest(new Transform(), new TestWatcher());
}
- public String getOutput() {
- return sb.toString();
+ private static boolean interpreting = true;
+ private static boolean retry = false;
+
+ public static void doTest(Transform t, TestWatcher w) {
+ // Get the methods that need to be optimized.
+ Method say_hi_method;
+ // Figure out if we can even JIT at all.
+ final boolean has_jit = hasJit();
+ try {
+ say_hi_method = Transform.class.getDeclaredMethod(
+ "sayHi", Runnable.class, Consumer.class);
+ } catch (Exception e) {
+ System.out.println("Unable to find methods!");
+ e.printStackTrace(System.out);
+ return;
+ }
+ // Makes sure the stack is the way we want it for the test and does the redefinition.
+ // It will set the retry boolean to true if the stack does not have a JIT-compiled
+ // sayHi entry. This can only happen if the method gets GC'd.
+ Runnable do_redefinition = () -> {
+ if (has_jit && Main.isInterpretedFunction(say_hi_method, true)) {
+ // Try again. We are not running the right jitted methods/cannot redefine them now.
+ retry = true;
+ } else {
+ // Actually do the redefinition. The stack looks good.
+ retry = false;
+ w.accept("transforming calling function");
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ }
+ };
+ // This just prints something out to show we are running the Runnable.
+ Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+ do {
+ // Run ensureJitCompiled here since it might get GCd
+ ensureJitCompiled(Transform.class, "sayHi");
+ // Clear output.
+ w.clear();
+ // Try and redefine.
+ t.sayHi(say_nothing, w);
+ t.sayHi(do_redefinition, w);
+ t.sayHi(say_nothing, w);
+ } while (retry);
+ // Print output of last run.
+ System.out.print(w.getOutput());
}
- public void clear() {
- sb = new StringBuilder();
- }
- }
+ private static native boolean hasJit();
- public static void main(String[] args) {
- doTest(new Transform(), new TestWatcher());
- }
+ private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
- private static boolean interpreting = true;
- private static boolean retry = false;
-
- public static void doTest(Transform t, TestWatcher w) {
- // Get the methods that need to be optimized.
- Method say_hi_method;
- // Figure out if we can even JIT at all.
- final boolean has_jit = hasJit();
- try {
- say_hi_method = Transform.class.getDeclaredMethod(
- "sayHi", Runnable.class, Consumer.class);
- } catch (Exception e) {
- System.out.println("Unable to find methods!");
- e.printStackTrace(System.out);
- return;
- }
- // Makes sure the stack is the way we want it for the test and does the redefinition. It will
- // set the retry boolean to true if the stack does not have a JIT-compiled sayHi entry. This can
- // only happen if the method gets GC'd.
- Runnable do_redefinition = () -> {
- if (has_jit && Main.isInterpretedFunction(say_hi_method, true)) {
- // Try again. We are not running the right jitted methods/cannot redefine them now.
- retry = true;
- } else {
- // Actually do the redefinition. The stack looks good.
- retry = false;
- w.accept("transforming calling function");
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- }
- };
- // This just prints something out to show we are running the Runnable.
- Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
- do {
- // Run ensureJitCompiled here since it might get GCd
- ensureJitCompiled(Transform.class, "sayHi");
- // Clear output.
- w.clear();
- // Try and redefine.
- t.sayHi(say_nothing, w);
- t.sayHi(do_redefinition, w);
- t.sayHi(say_nothing, w);
- } while (retry);
- // Print output of last run.
- System.out.print(w.getOutput());
- }
-
- private static native boolean hasJit();
-
- private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
-
- private static native void ensureJitCompiled(Class c, String name);
+ private static native void ensureJitCompiled(Class c, String name);
}
diff --git a/test/916-obsolete-jit/src/Transform.java b/test/916-obsolete-jit/src/Transform.java
index 9c9adbc..5dbc04e 100644
--- a/test/916-obsolete-jit/src/Transform.java
+++ b/test/916-obsolete-jit/src/Transform.java
@@ -17,21 +17,21 @@
import java.util.function.Consumer;
class Transform {
- private void Start(Consumer<String> reporter) {
- reporter.accept("hello - private");
- }
+ private void Start(Consumer<String> reporter) {
+ reporter.accept("hello - private");
+ }
- private void Finish(Consumer<String> reporter) {
- reporter.accept("goodbye - private");
- }
+ private void Finish(Consumer<String> reporter) {
+ reporter.accept("goodbye - private");
+ }
- public void sayHi(Runnable r, Consumer<String> reporter) {
- reporter.accept("Pre Start private method call");
- Start(reporter);
- reporter.accept("Post Start private method call");
- r.run();
- reporter.accept("Pre Finish private method call");
- Finish(reporter);
- reporter.accept("Post Finish private method call");
- }
+ public void sayHi(Runnable r, Consumer<String> reporter) {
+ reporter.accept("Pre Start private method call");
+ Start(reporter);
+ reporter.accept("Post Start private method call");
+ r.run();
+ reporter.accept("Pre Finish private method call");
+ Finish(reporter);
+ reporter.accept("Post Finish private method call");
+ }
}
diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/917-fields-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/917-fields-transformation/run.py b/test/917-fields-transformation/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/917-fields-transformation/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
index 289b89f..e277deb 100644
--- a/test/917-fields-transformation/src/Main.java
+++ b/test/917-fields-transformation/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test917.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test917.run();
+ }
}
diff --git a/test/917-fields-transformation/src/art/Test917.java b/test/917-fields-transformation/src/art/Test917.java
index 245e92e..ba9a06b 100644
--- a/test/917-fields-transformation/src/art/Test917.java
+++ b/test/917-fields-transformation/src/art/Test917.java
@@ -18,80 +18,78 @@
import java.util.Base64;
public class Test917 {
+ static class Transform {
+ public String take1;
+ public String take2;
- static class Transform {
- public String take1;
- public String take2;
+ public Transform(String take1, String take2) {
+ this.take1 = take1;
+ this.take2 = take2;
+ }
- public Transform(String take1, String take2) {
- this.take1 = take1;
- this.take2 = take2;
+ public String getResult() {
+ return take1;
+ }
}
- public String getResult() {
- return take1;
+
+ // base64 encoded class/dex file for
+ // static class Transform {
+ // public String take1;
+ // public String take2;
+ //
+ // public Transform(String a, String b) {
+ // take1 = a;
+ // take2 = b;
+ // }
+ //
+ // public String getResult() {
+ // return take2;
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAGwoABQARCQAEABIJAAQAEwcAFQcAGAEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
+ "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
+ "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
+ "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAMVGVzdDkxNy5qYXZhDAAJABkMAAYABwwACAAHBwAaAQAV" +
+ "YXJ0L1Rlc3Q5MTckVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9s" +
+ "YW5nL09iamVjdAEAAygpVgEAC2FydC9UZXN0OTE3ACAABAAFAAAAAgABAAYABwAAAAEACAAHAAAA" +
+ "AgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAJAAQA" +
+ "CgAJAAsADgAMAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAADwACAA8A" +
+ "AAACABAAFwAAAAoAAQAEABQAFgAI");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBdcPySAAAAAAAAAAAAAAAAAAAAAAAAAACQAwAAcAAAAHhWNBIAAAAAAAAAAMwCAAAS" +
+ "AAAAcAAAAAcAAAC4AAAAAwAAANQAAAACAAAA+AAAAAMAAAAIAQAAAQAAACABAABQAgAAQAEAAEAB" +
+ "AABIAQAASwEAAGQBAABzAQAAlwEAALcBAADLAQAA3wEAAO0BAAD4AQAA+wEAAAACAAANAgAAGAIA" +
+ "AB4CAAAlAgAALAIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAoAAAABAAAABQAAAAAAAAAKAAAA" +
+ "BgAAAAAAAAALAAAABgAAADQCAAAAAAUADwAAAAAABQAQAAAAAAACAAAAAAAAAAAADQAAAAQAAQAA" +
+ "AAAAAAAAAAAAAAAEAAAAAAAAAAgAAAC8AgAAjAIAAAAAAAAGPGluaXQ+AAFMABdMYXJ0L1Rlc3Q5" +
+ "MTckVHJhbnNmb3JtOwANTGFydC9UZXN0OTE3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu" +
+ "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09iamVj" +
+ "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAMVGVzdDkxNy5qYXZhAAlUcmFuc2Zvcm0AAVYAA1ZMTAAL" +
+ "YWNjZXNzRmxhZ3MACWdldFJlc3VsdAAEbmFtZQAFdGFrZTEABXRha2UyAAV2YWx1ZQAAAgAAAAUA" +
+ "BQAJAgAABw4BAw8BAg8BAg8ADwAHDgAAAAADAAMAAQAAADwCAAAIAAAAcBACAAAAWwEAAFsCAQAO" +
+ "AAIAAQAAAAAATAIAAAMAAABUEAEAEQAAAAACAQEAAQEBAIGABNQEAQH0BAAAAgIBERgBAgMCDAQI" +
+ "DhcJAAIAAACgAgAApgIAALACAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAABIAAABw" +
+ "AAAAAgAAAAcAAAC4AAAAAwAAAAMAAADUAAAABAAAAAIAAAD4AAAABQAAAAMAAAAIAQAABgAAAAEA" +
+ "AAAgAQAAAiAAABIAAABAAQAAARAAAAEAAAA0AgAAAyAAAAIAAAA8AgAAASAAAAIAAABUAgAAACAA" +
+ "AAEAAACMAgAABCAAAAIAAACgAgAAAxAAAAEAAACwAgAABiAAAAEAAAC8AgAAABAAAAEAAADMAgAA");
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform("Hello", "Goodbye"), new Transform("start", "end"));
}
- }
-
- // base64 encoded class/dex file for
- // static class Transform {
- // public String take1;
- // public String take2;
- //
- // public Transform(String a, String b) {
- // take1 = a;
- // take2 = b;
- // }
- //
- // public String getResult() {
- // return take2;
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAGwoABQARCQAEABIJAAQAEwcAFQcAGAEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
- "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
- "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
- "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAMVGVzdDkxNy5qYXZhDAAJABkMAAYABwwACAAHBwAaAQAV" +
- "YXJ0L1Rlc3Q5MTckVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9s" +
- "YW5nL09iamVjdAEAAygpVgEAC2FydC9UZXN0OTE3ACAABAAFAAAAAgABAAYABwAAAAEACAAHAAAA" +
- "AgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAJAAQA" +
- "CgAJAAsADgAMAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAADwACAA8A" +
- "AAACABAAFwAAAAoAAQAEABQAFgAI");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBdcPySAAAAAAAAAAAAAAAAAAAAAAAAAACQAwAAcAAAAHhWNBIAAAAAAAAAAMwCAAAS" +
- "AAAAcAAAAAcAAAC4AAAAAwAAANQAAAACAAAA+AAAAAMAAAAIAQAAAQAAACABAABQAgAAQAEAAEAB" +
- "AABIAQAASwEAAGQBAABzAQAAlwEAALcBAADLAQAA3wEAAO0BAAD4AQAA+wEAAAACAAANAgAAGAIA" +
- "AB4CAAAlAgAALAIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAoAAAABAAAABQAAAAAAAAAKAAAA" +
- "BgAAAAAAAAALAAAABgAAADQCAAAAAAUADwAAAAAABQAQAAAAAAACAAAAAAAAAAAADQAAAAQAAQAA" +
- "AAAAAAAAAAAAAAAEAAAAAAAAAAgAAAC8AgAAjAIAAAAAAAAGPGluaXQ+AAFMABdMYXJ0L1Rlc3Q5" +
- "MTckVHJhbnNmb3JtOwANTGFydC9UZXN0OTE3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu" +
- "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09iamVj" +
- "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAMVGVzdDkxNy5qYXZhAAlUcmFuc2Zvcm0AAVYAA1ZMTAAL" +
- "YWNjZXNzRmxhZ3MACWdldFJlc3VsdAAEbmFtZQAFdGFrZTEABXRha2UyAAV2YWx1ZQAAAgAAAAUA" +
- "BQAJAgAABw4BAw8BAg8BAg8ADwAHDgAAAAADAAMAAQAAADwCAAAIAAAAcBACAAAAWwEAAFsCAQAO" +
- "AAIAAQAAAAAATAIAAAMAAABUEAEAEQAAAAACAQEAAQEBAIGABNQEAQH0BAAAAgIBERgBAgMCDAQI" +
- "DhcJAAIAAACgAgAApgIAALACAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAABIAAABw" +
- "AAAAAgAAAAcAAAC4AAAAAwAAAAMAAADUAAAABAAAAAIAAAD4AAAABQAAAAMAAAAIAQAABgAAAAEA" +
- "AAAgAQAAAiAAABIAAABAAQAAARAAAAEAAAA0AgAAAyAAAAIAAAA8AgAAASAAAAIAAABUAgAAACAA" +
- "AAEAAACMAgAABCAAAAIAAACgAgAAAxAAAAEAAACwAgAABiAAAAEAAAC8AgAAABAAAAEAAADMAgAA");
-
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new Transform("Hello", "Goodbye"),
- new Transform("start", "end"));
- }
-
- private static void printTransform(Transform t) {
- System.out.println("Result is " + t.getResult());
- System.out.println("take1 is " + t.take1);
- System.out.println("take2 is " + t.take2);
- }
- public static void doTest(Transform t1, Transform t2) {
- printTransform(t1);
- printTransform(t2);
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- printTransform(t1);
- printTransform(t2);
- }
+ private static void printTransform(Transform t) {
+ System.out.println("Result is " + t.getResult());
+ System.out.println("take1 is " + t.take1);
+ System.out.println("take2 is " + t.take2);
+ }
+ public static void doTest(Transform t1, Transform t2) {
+ printTransform(t1);
+ printTransform(t2);
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ printTransform(t1);
+ printTransform(t2);
+ }
}
diff --git a/test/918-fields/run b/test/918-fields/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/918-fields/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/918-fields/run.py b/test/918-fields/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/918-fields/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java
index e0ed65c..e9f9bc9 100644
--- a/test/918-fields/src/Main.java
+++ b/test/918-fields/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test918.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test918.run();
+ }
}
diff --git a/test/918-fields/src/art/Test918.java b/test/918-fields/src/art/Test918.java
index 5328b0b..966c093 100644
--- a/test/918-fields/src/art/Test918.java
+++ b/test/918-fields/src/art/Test918.java
@@ -21,58 +21,57 @@
import java.util.Arrays;
public class Test918 {
- public static void run() throws Exception {
- doTest();
- }
-
- public static void doTest() throws Exception {
- testField(Math.class, "PI");
- testField(InterruptedIOException.class, "bytesTransferred");
- testField(Foo.class, "this$0");
- testField(Bar.class, "VAL");
- testField(Generics.class, "generics");
- testField(Generics.class, "privateValue");
- }
-
- private static void testField(Class<?> base, String fieldName)
- throws Exception {
- Field f = base.getDeclaredField(fieldName);
- String[] result = getFieldName(f);
- System.out.println(Arrays.toString(result));
-
- Class<?> declClass = getFieldDeclaringClass(f);
- if (base != declClass) {
- throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ public static void run() throws Exception {
+ doTest();
}
- System.out.println(declClass);
- int modifiers = getFieldModifiers(f);
- if (modifiers != f.getModifiers()) {
- throw new RuntimeException("Modifiers not equal: " + f.getModifiers() + " vs " + modifiers);
+ public static void doTest() throws Exception {
+ testField(Math.class, "PI");
+ testField(InterruptedIOException.class, "bytesTransferred");
+ testField(Foo.class, "this$0");
+ testField(Bar.class, "VAL");
+ testField(Generics.class, "generics");
+ testField(Generics.class, "privateValue");
}
- System.out.println(modifiers);
- boolean synth = isFieldSynthetic(f);
- if (synth != f.isSynthetic()) {
- throw new RuntimeException("Synthetic not equal: " + f.isSynthetic() + " vs " + synth);
+ private static void testField(Class<?> base, String fieldName) throws Exception {
+ Field f = base.getDeclaredField(fieldName);
+ String[] result = getFieldName(f);
+ System.out.println(Arrays.toString(result));
+
+ Class<?> declClass = getFieldDeclaringClass(f);
+ if (base != declClass) {
+ throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ }
+ System.out.println(declClass);
+
+ int modifiers = getFieldModifiers(f);
+ if (modifiers != f.getModifiers()) {
+ throw new RuntimeException(
+ "Modifiers not equal: " + f.getModifiers() + " vs " + modifiers);
+ }
+ System.out.println(modifiers);
+
+ boolean synth = isFieldSynthetic(f);
+ if (synth != f.isSynthetic()) {
+ throw new RuntimeException("Synthetic not equal: " + f.isSynthetic() + " vs " + synth);
+ }
+ System.out.println(synth);
}
- System.out.println(synth);
- }
- private static native String[] getFieldName(Field f);
- private static native Class<?> getFieldDeclaringClass(Field f);
- private static native int getFieldModifiers(Field f);
- private static native boolean isFieldSynthetic(Field f);
+ private static native String[] getFieldName(Field f);
+ private static native Class<?> getFieldDeclaringClass(Field f);
+ private static native int getFieldModifiers(Field f);
+ private static native boolean isFieldSynthetic(Field f);
- private class Foo {
- }
+ private class Foo {}
- private static interface Bar {
- public static int VAL = 1;
- }
+ private static interface Bar {
+ public static int VAL = 1;
+ }
- private static class Generics<T> {
- T generics;
- private int privateValue = 42;
- }
+ private static class Generics<T> {
+ T generics;
+ private int privateValue = 42;
+ }
}
diff --git a/test/919-obsolete-fields/run b/test/919-obsolete-fields/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/919-obsolete-fields/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/919-obsolete-fields/run.py b/test/919-obsolete-fields/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/919-obsolete-fields/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java
index 10eadb2..bb08324 100644
--- a/test/919-obsolete-fields/src/Main.java
+++ b/test/919-obsolete-fields/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test919.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test919.run();
+ }
}
diff --git a/test/919-obsolete-fields/src/art/Test919.java b/test/919-obsolete-fields/src/art/Test919.java
index 11971ef..0ae5305 100644
--- a/test/919-obsolete-fields/src/art/Test919.java
+++ b/test/919-obsolete-fields/src/art/Test919.java
@@ -20,154 +20,153 @@
import java.util.Base64;
public class Test919 {
+ static class Transform {
+ private Consumer<String> reporter;
+ public Transform(Consumer<String> reporter) {
+ this.reporter = reporter;
+ }
- static class Transform {
- private Consumer<String> reporter;
- public Transform(Consumer<String> reporter) {
- this.reporter = reporter;
+ private void Start() {
+ reporter.accept("hello - private");
+ }
+
+ private void Finish() {
+ reporter.accept("goodbye - private");
+ }
+
+ public void sayHi(Runnable r) {
+ reporter.accept("Pre Start private method call");
+ Start();
+ reporter.accept("Post Start private method call");
+ r.run();
+ reporter.accept("Pre Finish private method call");
+ Finish();
+ reporter.accept("Post Finish private method call");
+ }
}
- private void Start() {
- reporter.accept("hello - private");
+
+ // What follows is the base64 encoded representation of the following class:
+ //
+ // import java.util.function.Consumer;
+ //
+ // static class Transform {
+ // private Consumer<String> reporter;
+ // public Transform(Consumer<String> reporter) {
+ // this.reporter = reporter;
+ // }
+ //
+ // private void Start() {
+ // reporter.accept("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish() {
+ // reporter.accept("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r) {
+ // reporter.accept("pre Start private method call - Transformed");
+ // Start();
+ // reporter.accept("post Start private method call - Transformed");
+ // r.run();
+ // reporter.accept("pre Finish private method call - Transformed");
+ // Finish();
+ // reporter.accept("post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAOAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
+ "LAcALgcAMQEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
+ "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
+ "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
+ "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
+ "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
+ "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAAxUZXN0OTE5LmphdmEMABMAGQwADwAQAQAdSGVsbG8g" +
+ "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQHADIMADMANAEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJh" +
+ "bnNmb3JtZWQBACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAY" +
+ "ABkBACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANQwANgAZ" +
+ "AQAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABoAGQEALXBv" +
+ "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANwEAFWFydC9UZXN0" +
+ "OTE5JFRyYW5zZm9ybQEACVRyYW5zZm9ybQEADElubmVyQ2xhc3NlcwEAEGphdmEvbGFuZy9PYmpl" +
+ "Y3QBABtqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXIBAAZhY2NlcHQBABUoTGphdmEvbGFuZy9P" +
+ "YmplY3Q7KVYBABJqYXZhL2xhbmcvUnVubmFibGUBAANydW4BAAthcnQvVGVzdDkxOQAgAA0ADgAA" +
+ "AAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAAAAEA" +
+ "FgAAAA4AAwAAAAgABAAJAAkACgARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAISA7kA" +
+ "BAIAsQAAAAEAFgAAAAoAAgAAAA0ACwAOAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkABAIA" +
+ "sQAAAAEAFgAAAAoAAgAAABEACwASAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIAKrcA" +
+ "Byq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAAIgAI" +
+ "AAAAFQALABYADwAXABoAGAAgABkAKwAaAC8AGwA6ABwAAgAdAAAAAgAeADAAAAAKAAEADQAtAC8A" +
+ "CA==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBeEZYBAAAAAAAAAAAAAAAAAAAAAAAAAACMBgAAcAAAAHhWNBIAAAAAAAAAAMgFAAAi" +
+ "AAAAcAAAAAkAAAD4AAAABAAAABwBAAABAAAATAEAAAcAAABUAQAAAQAAAIwBAADgBAAArAEAAKwB" +
+ "AACvAQAAsgEAALoBAAC+AQAAxAEAAMwBAADtAQAADAIAACUCAAA0AgAAWAIAAHgCAACXAgAAqwIA" +
+ "AMECAADVAgAA8wIAABIDAAAZAwAAJwMAADIDAAA1AwAAOQMAAEEDAABOAwAAVAMAAIMDAACxAwAA" +
+ "3wMAAAwEAAAWBAAAGwQAACIEAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAAEQAAABUAAAAV" +
+ "AAAACAAAAAAAAAAWAAAACAAAADQEAAAWAAAACAAAADwEAAAWAAAACAAAACwEAAAAAAcAHgAAAAAA" +
+ "AwACAAAAAAAAAAUAAAAAAAAAEgAAAAAAAgAgAAAABQAAAAIAAAAGAAAAHwAAAAcAAQAXAAAAAAAA" +
+ "AAAAAAAFAAAAAAAAABMAAACoBQAARAUAAAAAAAABKAABPAAGPGluaXQ+AAI+OwAEPjspVgAGRmlu" +
+ "aXNoAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAB1IZWxsbyAtIHByaXZhdGUgLSBU" +
+ "cmFuc2Zvcm1lZAAXTGFydC9UZXN0OTE5JFRyYW5zZm9ybTsADUxhcnQvVGVzdDkxOTsAIkxkYWx2" +
+ "aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNs" +
+ "YXNzOwAdTGRhbHZpay9hbm5vdGF0aW9uL1NpZ25hdHVyZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAU" +
+ "TGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwAcTGphdmEvdXRpbC9mdW5j" +
+ "dGlvbi9Db25zdW1lcgAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsABVN0YXJ0AAxUZXN0" +
+ "OTE5LmphdmEACVRyYW5zZm9ybQABVgACVkwABmFjY2VwdAALYWNjZXNzRmxhZ3MABG5hbWUALXBv" +
+ "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscG9zdCBTdGFydCBw" +
+ "cml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRo" +
+ "b2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
+ "YW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFsdWUAAAAAAQAAAAcAAAABAAAABQAA" +
+ "AAEAAAAGAAAACAEABw4BAw8BAg8AEQAHDgEIDwANAAcOAQgPABUBAAcOAQgPAQMPAQgPAQMPAQgP" +
+ "AQMPAQgPAAACAAIAAQAAAEQEAAAGAAAAcBAEAAAAWwEAAA4AAwABAAIAAABQBAAACQAAAFQgAAAb" +
+ "AQYAAAByIAYAEAAOAAAAAwABAAIAAABYBAAACQAAAFQgAAAbAQcAAAByIAYAEAAOAAAABAACAAIA" +
+ "AABgBAAAKgAAAFQgAAAbAR0AAAByIAYAEABwEAIAAgBUIAAAGwEbAAAAciAGABAAchAFAAMAVCAA" +
+ "ABsBHAAAAHIgBgAQAHAQAQACAFQgAAAbARoAAAByIAYAEAAOAAABAwEAAgCBgAT8CAECmAkBArwJ" +
+ "AwHgCQICASEYAQIDAhgECBkXFAIEASEcBBcQFwEXDxcDAgQBIRwFFwAXEBcBFw8XBAAAAAIAAABc" +
+ "BQAAYgUAAAEAAABrBQAAAQAAAHkFAACMBQAAAQAAAAEAAAAAAAAAAAAAAJgFAAAAAAAAoAUAABAA" +
+ "AAAAAAAAAQAAAAAAAAABAAAAIgAAAHAAAAACAAAACQAAAPgAAAADAAAABAAAABwBAAAEAAAAAQAA" +
+ "AEwBAAAFAAAABwAAAFQBAAAGAAAAAQAAAIwBAAACIAAAIgAAAKwBAAABEAAAAwAAACwEAAADIAAA" +
+ "BAAAAEQEAAABIAAABAAAAHwEAAAAIAAAAQAAAEQFAAAEIAAABAAAAFwFAAADEAAAAwAAAIwFAAAG" +
+ "IAAAAQAAAKgFAAAAEAAAAQAAAMgFAAA=");
+
+ // A class that we can use to keep track of the output of this test.
+ private static class TestWatcher implements Consumer<String> {
+ private StringBuilder sb;
+ public TestWatcher() {
+ sb = new StringBuilder();
+ }
+
+ @Override
+ public void accept(String s) {
+ sb.append(s);
+ sb.append('\n');
+ }
+
+ public String getOutput() {
+ return sb.toString();
+ }
}
- private void Finish() {
- reporter.accept("goodbye - private");
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ TestWatcher w = new TestWatcher();
+ doTest(new Transform(w), w);
}
- public void sayHi(Runnable r) {
- reporter.accept("Pre Start private method call");
- Start();
- reporter.accept("Post Start private method call");
- r.run();
- reporter.accept("Pre Finish private method call");
- Finish();
- reporter.accept("Post Finish private method call");
+ public static void doTest(Transform t, TestWatcher w) {
+ Runnable do_redefinition = () -> {
+ w.accept("transforming calling function");
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ };
+ // This just prints something out to show we are running the Runnable.
+ Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+
+ // Try and redefine.
+ t.sayHi(say_nothing);
+ t.sayHi(do_redefinition);
+ t.sayHi(say_nothing);
+
+ // Print output of last run.
+ System.out.print(w.getOutput());
}
- }
-
-
- // What follows is the base64 encoded representation of the following class:
- //
- // import java.util.function.Consumer;
- //
- // static class Transform {
- // private Consumer<String> reporter;
- // public Transform(Consumer<String> reporter) {
- // this.reporter = reporter;
- // }
- //
- // private void Start() {
- // reporter.accept("Hello - private - Transformed");
- // }
- //
- // private void Finish() {
- // reporter.accept("Goodbye - private - Transformed");
- // }
- //
- // public void sayHi(Runnable r) {
- // reporter.accept("pre Start private method call - Transformed");
- // Start();
- // reporter.accept("post Start private method call - Transformed");
- // r.run();
- // reporter.accept("pre Finish private method call - Transformed");
- // Finish();
- // reporter.accept("post Finish private method call - Transformed");
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAOAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
- "LAcALgcAMQEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
- "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
- "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
- "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
- "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
- "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAAxUZXN0OTE5LmphdmEMABMAGQwADwAQAQAdSGVsbG8g" +
- "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQHADIMADMANAEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJh" +
- "bnNmb3JtZWQBACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAY" +
- "ABkBACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANQwANgAZ" +
- "AQAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABoAGQEALXBv" +
- "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANwEAFWFydC9UZXN0" +
- "OTE5JFRyYW5zZm9ybQEACVRyYW5zZm9ybQEADElubmVyQ2xhc3NlcwEAEGphdmEvbGFuZy9PYmpl" +
- "Y3QBABtqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXIBAAZhY2NlcHQBABUoTGphdmEvbGFuZy9P" +
- "YmplY3Q7KVYBABJqYXZhL2xhbmcvUnVubmFibGUBAANydW4BAAthcnQvVGVzdDkxOQAgAA0ADgAA" +
- "AAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAAAAEA" +
- "FgAAAA4AAwAAAAgABAAJAAkACgARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAISA7kA" +
- "BAIAsQAAAAEAFgAAAAoAAgAAAA0ACwAOAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkABAIA" +
- "sQAAAAEAFgAAAAoAAgAAABEACwASAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIAKrcA" +
- "Byq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAAIgAI" +
- "AAAAFQALABYADwAXABoAGAAgABkAKwAaAC8AGwA6ABwAAgAdAAAAAgAeADAAAAAKAAEADQAtAC8A" +
- "CA==");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBeEZYBAAAAAAAAAAAAAAAAAAAAAAAAAACMBgAAcAAAAHhWNBIAAAAAAAAAAMgFAAAi" +
- "AAAAcAAAAAkAAAD4AAAABAAAABwBAAABAAAATAEAAAcAAABUAQAAAQAAAIwBAADgBAAArAEAAKwB" +
- "AACvAQAAsgEAALoBAAC+AQAAxAEAAMwBAADtAQAADAIAACUCAAA0AgAAWAIAAHgCAACXAgAAqwIA" +
- "AMECAADVAgAA8wIAABIDAAAZAwAAJwMAADIDAAA1AwAAOQMAAEEDAABOAwAAVAMAAIMDAACxAwAA" +
- "3wMAAAwEAAAWBAAAGwQAACIEAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAAEQAAABUAAAAV" +
- "AAAACAAAAAAAAAAWAAAACAAAADQEAAAWAAAACAAAADwEAAAWAAAACAAAACwEAAAAAAcAHgAAAAAA" +
- "AwACAAAAAAAAAAUAAAAAAAAAEgAAAAAAAgAgAAAABQAAAAIAAAAGAAAAHwAAAAcAAQAXAAAAAAAA" +
- "AAAAAAAFAAAAAAAAABMAAACoBQAARAUAAAAAAAABKAABPAAGPGluaXQ+AAI+OwAEPjspVgAGRmlu" +
- "aXNoAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAB1IZWxsbyAtIHByaXZhdGUgLSBU" +
- "cmFuc2Zvcm1lZAAXTGFydC9UZXN0OTE5JFRyYW5zZm9ybTsADUxhcnQvVGVzdDkxOTsAIkxkYWx2" +
- "aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNs" +
- "YXNzOwAdTGRhbHZpay9hbm5vdGF0aW9uL1NpZ25hdHVyZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAU" +
- "TGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwAcTGphdmEvdXRpbC9mdW5j" +
- "dGlvbi9Db25zdW1lcgAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsABVN0YXJ0AAxUZXN0" +
- "OTE5LmphdmEACVRyYW5zZm9ybQABVgACVkwABmFjY2VwdAALYWNjZXNzRmxhZ3MABG5hbWUALXBv" +
- "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscG9zdCBTdGFydCBw" +
- "cml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRo" +
- "b2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
- "YW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFsdWUAAAAAAQAAAAcAAAABAAAABQAA" +
- "AAEAAAAGAAAACAEABw4BAw8BAg8AEQAHDgEIDwANAAcOAQgPABUBAAcOAQgPAQMPAQgPAQMPAQgP" +
- "AQMPAQgPAAACAAIAAQAAAEQEAAAGAAAAcBAEAAAAWwEAAA4AAwABAAIAAABQBAAACQAAAFQgAAAb" +
- "AQYAAAByIAYAEAAOAAAAAwABAAIAAABYBAAACQAAAFQgAAAbAQcAAAByIAYAEAAOAAAABAACAAIA" +
- "AABgBAAAKgAAAFQgAAAbAR0AAAByIAYAEABwEAIAAgBUIAAAGwEbAAAAciAGABAAchAFAAMAVCAA" +
- "ABsBHAAAAHIgBgAQAHAQAQACAFQgAAAbARoAAAByIAYAEAAOAAABAwEAAgCBgAT8CAECmAkBArwJ" +
- "AwHgCQICASEYAQIDAhgECBkXFAIEASEcBBcQFwEXDxcDAgQBIRwFFwAXEBcBFw8XBAAAAAIAAABc" +
- "BQAAYgUAAAEAAABrBQAAAQAAAHkFAACMBQAAAQAAAAEAAAAAAAAAAAAAAJgFAAAAAAAAoAUAABAA" +
- "AAAAAAAAAQAAAAAAAAABAAAAIgAAAHAAAAACAAAACQAAAPgAAAADAAAABAAAABwBAAAEAAAAAQAA" +
- "AEwBAAAFAAAABwAAAFQBAAAGAAAAAQAAAIwBAAACIAAAIgAAAKwBAAABEAAAAwAAACwEAAADIAAA" +
- "BAAAAEQEAAABIAAABAAAAHwEAAAAIAAAAQAAAEQFAAAEIAAABAAAAFwFAAADEAAAAwAAAIwFAAAG" +
- "IAAAAQAAAKgFAAAAEAAAAQAAAMgFAAA=");
-
- // A class that we can use to keep track of the output of this test.
- private static class TestWatcher implements Consumer<String> {
- private StringBuilder sb;
- public TestWatcher() {
- sb = new StringBuilder();
- }
-
- @Override
- public void accept(String s) {
- sb.append(s);
- sb.append('\n');
- }
-
- public String getOutput() {
- return sb.toString();
- }
- }
-
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- TestWatcher w = new TestWatcher();
- doTest(new Transform(w), w);
- }
-
- public static void doTest(Transform t, TestWatcher w) {
- Runnable do_redefinition = () -> {
- w.accept("transforming calling function");
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- };
- // This just prints something out to show we are running the Runnable.
- Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
-
- // Try and redefine.
- t.sayHi(say_nothing);
- t.sayHi(do_redefinition);
- t.sayHi(say_nothing);
-
- // Print output of last run.
- System.out.print(w.getOutput());
- }
}
diff --git a/test/920-objects/run b/test/920-objects/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/920-objects/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/920-objects/run.py b/test/920-objects/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/920-objects/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/921-hello-failure/run b/test/921-hello-failure/run
deleted file mode 100755
index 8be0ed4..0000000
--- a/test/921-hello-failure/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-./default-run "$@" --jvmti
diff --git a/test/921-hello-failure/run.py b/test/921-hello-failure/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/921-hello-failure/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/922-properties/run b/test/922-properties/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/922-properties/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/922-properties/run.py b/test/922-properties/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/922-properties/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/923-monitors/run b/test/923-monitors/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/923-monitors/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/923-monitors/run.py b/test/923-monitors/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/923-monitors/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/924-threads/run b/test/924-threads/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/924-threads/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/924-threads/run.py b/test/924-threads/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/924-threads/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/925-threadgroups/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/925-threadgroups/run.py b/test/925-threadgroups/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/925-threadgroups/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/926-multi-obsolescence/run b/test/926-multi-obsolescence/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/926-multi-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/926-multi-obsolescence/run.py b/test/926-multi-obsolescence/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/926-multi-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/927-timers/run b/test/927-timers/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/927-timers/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/927-timers/run.py b/test/927-timers/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/927-timers/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/928-jni-table/run b/test/928-jni-table/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/928-jni-table/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/928-jni-table/run.py b/test/928-jni-table/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/928-jni-table/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/929-search/run b/test/929-search/run
deleted file mode 100755
index fb6b1b8..0000000
--- a/test/929-search/run
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-# Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
-
-./default-run "$@" --jvmti \
- --no-app-image \
- --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]"
diff --git a/test/929-search/run.py b/test/929-search/run.py
new file mode 100644
index 0000000..012e279
--- /dev/null
+++ b/test/929-search/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+ # Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
+
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(
+ args, jvmti=True, app_image=False, secondary_class_loader_context=pcl)
diff --git a/test/930-hello-retransform/run b/test/930-hello-retransform/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/930-hello-retransform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/930-hello-retransform/run.py b/test/930-hello-retransform/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/930-hello-retransform/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/931-agent-thread/run b/test/931-agent-thread/run
deleted file mode 100755
index 67923a7..0000000
--- a/test/931-agent-thread/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-
-./default-run "$@" --jvmti \
- --no-app-image
diff --git a/test/931-agent-thread/run.py b/test/931-agent-thread/run.py
new file mode 100644
index 0000000..4ac9127
--- /dev/null
+++ b/test/931-agent-thread/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/932-transform-saves/run b/test/932-transform-saves/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/932-transform-saves/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/932-transform-saves/run.py b/test/932-transform-saves/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/932-transform-saves/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/933-misc-events/run b/test/933-misc-events/run
deleted file mode 100755
index 67923a7..0000000
--- a/test/933-misc-events/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-
-./default-run "$@" --jvmti \
- --no-app-image
diff --git a/test/933-misc-events/run.py b/test/933-misc-events/run.py
new file mode 100644
index 0000000..5a2efa9
--- /dev/null
+++ b/test/933-misc-events/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/934-load-transform/run b/test/934-load-transform/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/934-load-transform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --no-app-image
diff --git a/test/934-load-transform/run.py b/test/934-load-transform/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/934-load-transform/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/935-non-retransformable/run b/test/935-non-retransformable/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/935-non-retransformable/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --no-app-image
diff --git a/test/935-non-retransformable/run.py b/test/935-non-retransformable/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/935-non-retransformable/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/936-search-onload/run b/test/936-search-onload/run
deleted file mode 100755
index fb6b1b8..0000000
--- a/test/936-search-onload/run
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-# Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
-
-./default-run "$@" --jvmti \
- --no-app-image \
- --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]"
diff --git a/test/936-search-onload/run.py b/test/936-search-onload/run.py
new file mode 100644
index 0000000..012e279
--- /dev/null
+++ b/test/936-search-onload/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+ # Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
+
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(
+ args, jvmti=True, app_image=False, secondary_class_loader_context=pcl)
diff --git a/test/937-hello-retransform-package/run b/test/937-hello-retransform-package/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/937-hello-retransform-package/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/937-hello-retransform-package/run.py b/test/937-hello-retransform-package/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/937-hello-retransform-package/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/938-load-transform-bcp/run b/test/938-load-transform-bcp/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/938-load-transform-bcp/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --no-app-image
diff --git a/test/938-load-transform-bcp/run.py b/test/938-load-transform-bcp/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/938-load-transform-bcp/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/939-hello-transformation-bcp/run b/test/939-hello-transformation-bcp/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/939-hello-transformation-bcp/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/939-hello-transformation-bcp/run.py b/test/939-hello-transformation-bcp/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/939-hello-transformation-bcp/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/940-recursive-obsolete/run b/test/940-recursive-obsolete/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/940-recursive-obsolete/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/940-recursive-obsolete/run.py b/test/940-recursive-obsolete/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/940-recursive-obsolete/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/941-recursive-obsolete-jit/run b/test/941-recursive-obsolete-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/941-recursive-obsolete-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/941-recursive-obsolete-jit/run.py b/test/941-recursive-obsolete-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/941-recursive-obsolete-jit/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/942-private-recursive/run b/test/942-private-recursive/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/942-private-recursive/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/942-private-recursive/run.py b/test/942-private-recursive/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/942-private-recursive/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/943-private-recursive-jit/run b/test/943-private-recursive-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/943-private-recursive-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/943-private-recursive-jit/run.py b/test/943-private-recursive-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/943-private-recursive-jit/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/944-transform-classloaders/run b/test/944-transform-classloaders/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/944-transform-classloaders/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/944-transform-classloaders/run.py b/test/944-transform-classloaders/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/944-transform-classloaders/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/945-obsolete-native/run b/test/945-obsolete-native/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/945-obsolete-native/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/945-obsolete-native/run.py b/test/945-obsolete-native/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/945-obsolete-native/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/946-obsolete-throw/run b/test/946-obsolete-throw/run
deleted file mode 100755
index e92b873..0000000
--- a/test/946-obsolete-throw/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/946-obsolete-throw/run.py b/test/946-obsolete-throw/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/946-obsolete-throw/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/947-reflect-method/run b/test/947-reflect-method/run
deleted file mode 100755
index e92b873..0000000
--- a/test/947-reflect-method/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/947-reflect-method/run.py b/test/947-reflect-method/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/947-reflect-method/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/948-change-annotations/build b/test/948-change-annotations/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/948-change-annotations/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-build "$@" --experimental agents
diff --git a/test/948-change-annotations/build.py b/test/948-change-annotations/build.py
new file mode 100644
index 0000000..1613d3a
--- /dev/null
+++ b/test/948-change-annotations/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="agents")
diff --git a/test/948-change-annotations/run b/test/948-change-annotations/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/948-change-annotations/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/948-change-annotations/run.py b/test/948-change-annotations/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/948-change-annotations/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/949-in-memory-transform/run b/test/949-in-memory-transform/run
deleted file mode 100755
index e92b873..0000000
--- a/test/949-in-memory-transform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/949-in-memory-transform/run.py b/test/949-in-memory-transform/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/949-in-memory-transform/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/950-redefine-intrinsic/run b/test/950-redefine-intrinsic/run
deleted file mode 100755
index e92b873..0000000
--- a/test/950-redefine-intrinsic/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/950-redefine-intrinsic/run.py b/test/950-redefine-intrinsic/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/950-redefine-intrinsic/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/951-threaded-obsolete/run b/test/951-threaded-obsolete/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/951-threaded-obsolete/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/951-threaded-obsolete/run.py b/test/951-threaded-obsolete/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/951-threaded-obsolete/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/952-invoke-custom/build b/test/952-invoke-custom/build
deleted file mode 100755
index e835517..0000000
--- a/test/952-invoke-custom/build
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Stop on failure.
-set -e
-
-export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
-
-# Build the transformer to apply to compiled classes.
-mkdir classes
-${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
-${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
-rm -rf classes
-
-# Use API level 28 for invoke-custom bytecode support.
-USE_DESUGAR=false ./default-build "$@" --api-level 28
diff --git a/test/952-invoke-custom/build.py b/test/952-invoke-custom/build.py
new file mode 100644
index 0000000..166d3a8
--- /dev/null
+++ b/test/952-invoke-custom/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(use_desugar=False,
+ api_level=28,
+ javac_classpath=[ctx.test_dir / "transformer.jar"])
diff --git a/test/952-invoke-custom/generate-sources b/test/952-invoke-custom/generate-sources
new file mode 100755
index 0000000..4244f8c
--- /dev/null
+++ b/test/952-invoke-custom/generate-sources
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Stop on failure.
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Build the transformer to apply to compiled classes.
+mkdir classes
+${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
+${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
+rm -rf classes
diff --git a/test/952-invoke-custom/javac_post.sh b/test/952-invoke-custom/javac_post.sh
new file mode 100755
index 0000000..be5d8cf
--- /dev/null
+++ b/test/952-invoke-custom/javac_post.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Move original classes to intermediate location.
+mv classes intermediate-classes
+mkdir classes
+
+# Transform intermediate classes.
+transformer_args="-cp ${ASM_JAR}:transformer.jar transformer.IndyTransformer"
+for class in intermediate-classes/*.class ; do
+ transformed_class=classes/$(basename ${class})
+ ${JAVA:-java} ${transformer_args} $PWD/${class} ${transformed_class}
+done
diff --git a/test/952-invoke-custom/javac_wrapper.sh b/test/952-invoke-custom/javac_wrapper.sh
deleted file mode 100755
index 8659030..0000000
--- a/test/952-invoke-custom/javac_wrapper.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-# Update arguments to add transformer and ASM to the compiler classpath.
-classpath="./transformer.jar:$ASM_JAR"
-args=(-cp $classpath)
-while [ $# -ne 0 ] ; do
- case $1 in
- -cp|-classpath|--class-path)
- shift
- shift
- ;;
- *)
- args+=("$1")
- shift
- ;;
- esac
-done
-
-# Compile.
-$JAVAC "${args[@]}"
-
-# Move original classes to intermediate location.
-mv classes intermediate-classes
-mkdir classes
-
-# Transform intermediate classes.
-transformer_args="-cp ${ASM_JAR}:transformer.jar transformer.IndyTransformer"
-for class in intermediate-classes/*.class ; do
- transformed_class=classes/$(basename ${class})
- ${JAVA:-java} ${transformer_args} $PWD/${class} ${transformed_class}
-done
diff --git a/test/952-invoke-custom/util-src/transformer/IndyTransformer.java b/test/952-invoke-custom/util-src/transformer/IndyTransformer.java
index 6401c54..ee04ff2 100644
--- a/test/952-invoke-custom/util-src/transformer/IndyTransformer.java
+++ b/test/952-invoke-custom/util-src/transformer/IndyTransformer.java
@@ -189,7 +189,7 @@
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
try (InputStream is = Files.newInputStream(inputClassPath)) {
ClassReader cr = new ClassReader(is);
- cr.accept(new BootstrapBuilder(Opcodes.ASM7, cw, callsiteMap), 0);
+ cr.accept(new BootstrapBuilder(Opcodes.ASM9, cw, callsiteMap), 0);
}
try (OutputStream os = Files.newOutputStream(outputClassPath)) {
os.write(cw.toByteArray());
diff --git a/test/953-invoke-polymorphic-compiler/build b/test/953-invoke-polymorphic-compiler/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/953-invoke-polymorphic-compiler/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/953-invoke-polymorphic-compiler/build.py b/test/953-invoke-polymorphic-compiler/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/953-invoke-polymorphic-compiler/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/954-invoke-polymorphic-verifier/build b/test/954-invoke-polymorphic-verifier/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/954-invoke-polymorphic-verifier/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/954-invoke-polymorphic-verifier/build.py b/test/954-invoke-polymorphic-verifier/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/954-invoke-polymorphic-verifier/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/954-invoke-polymorphic-verifier/check b/test/954-invoke-polymorphic-verifier/check
deleted file mode 100755
index 3f9e6dc..0000000
--- a/test/954-invoke-polymorphic-verifier/check
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip out temporary file path information and indices from output.
-sed -e "s/ [(]declaration of.*//" -e "s/\[0x[0-9A-F]*\] //g" "$2" > "$2.tmp"
-diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/954-invoke-polymorphic-verifier/run.py b/test/954-invoke-polymorphic-verifier/run.py
new file mode 100644
index 0000000..bdb6301
--- /dev/null
+++ b/test/954-invoke-polymorphic-verifier/run.py
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip out temporary file path information and indices from output.
+ ctx.run(
+ fr"sed -i -e 's/ [(]declaration of.*//' -e 's/\[0x[0-9A-F]*\] //g' '{args.stdout_file}'"
+ )
diff --git a/test/955-methodhandles-smali/build b/test/955-methodhandles-smali/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/955-methodhandles-smali/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/955-methodhandles-smali/build.py b/test/955-methodhandles-smali/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/955-methodhandles-smali/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/956-methodhandles/build b/test/956-methodhandles/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/956-methodhandles/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/956-methodhandles/build.py b/test/956-methodhandles/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/956-methodhandles/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/957-methodhandle-transforms/build b/test/957-methodhandle-transforms/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/957-methodhandle-transforms/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/957-methodhandle-transforms/build.py b/test/957-methodhandle-transforms/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/957-methodhandle-transforms/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
index 2b16485..3ee270d 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -1557,7 +1557,7 @@
try {
adapter = MethodHandles.collectArguments(target, 3, filter);
fail();
- } catch (IndexOutOfBoundsException expected) {
+ } catch (IndexOutOfBoundsException | IllegalArgumentException expected) {
}
// Mismatch in filter return type.
diff --git a/test/958-methodhandle-stackframe/build b/test/958-methodhandle-stackframe/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/958-methodhandle-stackframe/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/958-methodhandle-stackframe/build.py b/test/958-methodhandle-stackframe/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/958-methodhandle-stackframe/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/959-invoke-polymorphic-accessors/build b/test/959-invoke-polymorphic-accessors/build
deleted file mode 100644
index 2b0b2c1..0000000
--- a/test/959-invoke-polymorphic-accessors/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/959-invoke-polymorphic-accessors/build.py b/test/959-invoke-polymorphic-accessors/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/959-invoke-polymorphic-accessors/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/959-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java
index 03fd285..73b7746 100644
--- a/test/959-invoke-polymorphic-accessors/src/Main.java
+++ b/test/959-invoke-polymorphic-accessors/src/Main.java
@@ -20,6 +20,8 @@
public class Main {
+ private static final boolean DALVIK_RUN = "Dalvik".equals(System.getProperty("java.vm.name"));
+
public static class ValueHolder {
public boolean m_z = false;
public byte m_b = 0;
@@ -793,6 +795,10 @@
Long z = (Long) h0.invoke(valueHolder);
fail();
} catch (WrongMethodTypeException expected) {}
+ try {
+ int x = (int) h0.invokeExact((ValueHolder) null);
+ fail();
+ } catch (NullPointerException expected) {}
}
/*package*/ static Number getDoubleAsNumber() {
@@ -822,6 +828,10 @@
h0.invoke(valueHolder, (Float) null);
fail();
} catch (NullPointerException expected) {}
+ try {
+ h0.invoke((ValueHolder) null, Float.valueOf(1.0f));
+ fail();
+ } catch (NullPointerException expected) {}
// Test that type conversion checks work on small field types.
short temp = (short) s0.invoke(valueHolder, new Byte((byte) 45));
@@ -951,8 +961,10 @@
MethodHandles.lookup().unreflectSetter(f).invokeExact(v, 'A');
assertEquals('A', (char) MethodHandles.lookup().unreflectGetter(f).invokeExact(v));
}
- {
+ if (DALVIK_RUN) {
// public static final field test
+ // for JVM it is not possible to get the unreflected setter for a static final
+ // field, see b/242985782
Field f = ValueHolder.class.getDeclaredField("s_fi");
try {
MethodHandles.lookup().unreflectSetter(f);
@@ -1003,8 +1015,10 @@
fail();
} catch (IllegalAccessException expected) {}
}
- {
+ if (DALVIK_RUN) {
// private static final field test
+ // for JVM it is not possible to get the unreflected setter for a static final
+ // field, see b/242985782
Field f = ValueHolder.class.getDeclaredField("s_fz"); // private static final field
try {
MethodHandles.lookup().unreflectSetter(f);
diff --git a/test/960-default-smali/build b/test/960-default-smali/build
deleted file mode 100755
index 44d6bd2..0000000
--- a/test/960-default-smali/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-# Generate the Main.java file or fail
-${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
-
-./default-build "$@" --experimental default-methods
diff --git a/test/960-default-smali/build.py b/test/960-default-smali/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/960-default-smali/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/960-default-smali/generate-sources b/test/960-default-smali/generate-sources
new file mode 100755
index 0000000..1fffc71
--- /dev/null
+++ b/test/960-default-smali/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+# Generate the Main.java file or fail
+${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
diff --git a/test/961-default-iface-resolution-gen/build b/test/961-default-iface-resolution-gen/build
deleted file mode 100755
index b9b36d0..0000000
--- a/test/961-default-iface-resolution-gen/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-mkdir -p ./src
-
-# Generate the smali files and expected-stdout.txt or fail
-./util-src/generate_java.py ./src ./expected-stdout.txt
-
-./default-build "$@" --experimental default-methods
diff --git a/test/961-default-iface-resolution-gen/build.py b/test/961-default-iface-resolution-gen/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/961-default-iface-resolution-gen/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/961-default-iface-resolution-gen/expected-stdout.txt b/test/961-default-iface-resolution-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/961-default-iface-resolution-gen/expected-stdout.txt
+++ b/test/961-default-iface-resolution-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/961-default-iface-resolution-gen/generate-sources b/test/961-default-iface-resolution-gen/generate-sources
new file mode 100755
index 0000000..4d12e5f
--- /dev/null
+++ b/test/961-default-iface-resolution-gen/generate-sources
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+mkdir -p ./src
+
+# Generate the smali files and expected-stdout.txt or fail
+./util-src/generate_java.py ./src ./expected-stdout.txt
diff --git a/test/961-default-iface-resolution-gen/run b/test/961-default-iface-resolution-gen/run
deleted file mode 100755
index fdcd2a8..0000000
--- a/test/961-default-iface-resolution-gen/run
+++ /dev/null
@@ -1,18 +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.
-
-# Run with a 2 minute default dex2oat timeout and a 2.5 minute hard dex2oat timeout.
-./default-run "$@" --dex2oat-timeout 120 --dex2oat-rt-timeout 180
diff --git a/test/961-default-iface-resolution-gen/run.py b/test/961-default-iface-resolution-gen/run.py
new file mode 100644
index 0000000..7de21e3
--- /dev/null
+++ b/test/961-default-iface-resolution-gen/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Run with a 2 minute default dex2oat timeout and a 2.5 minute hard dex2oat timeout.
+ ctx.default_run(args, dex2oat_timeout=120, dex2oat_rt_timeout=180)
diff --git a/test/962-iface-static/build b/test/962-iface-static/build
deleted file mode 100644
index 82f4931..0000000
--- a/test/962-iface-static/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/962-iface-static/build.py b/test/962-iface-static/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/962-iface-static/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/964-default-iface-init-gen/build b/test/964-default-iface-init-gen/build
deleted file mode 100755
index b9b36d0..0000000
--- a/test/964-default-iface-init-gen/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-mkdir -p ./src
-
-# Generate the smali files and expected-stdout.txt or fail
-./util-src/generate_java.py ./src ./expected-stdout.txt
-
-./default-build "$@" --experimental default-methods
diff --git a/test/964-default-iface-init-gen/build.py b/test/964-default-iface-init-gen/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/964-default-iface-init-gen/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/964-default-iface-init-gen/expected-stdout.txt b/test/964-default-iface-init-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/964-default-iface-init-gen/expected-stdout.txt
+++ b/test/964-default-iface-init-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/964-default-iface-init-gen/generate-sources b/test/964-default-iface-init-gen/generate-sources
new file mode 100755
index 0000000..4d12e5f
--- /dev/null
+++ b/test/964-default-iface-init-gen/generate-sources
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+mkdir -p ./src
+
+# Generate the smali files and expected-stdout.txt or fail
+./util-src/generate_java.py ./src ./expected-stdout.txt
diff --git a/test/965-default-verify/Android.bp b/test/965-default-verify/Android.bp
new file mode 100644
index 0000000..680c3a2
--- /dev/null
+++ b/test/965-default-verify/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `965-default-verify`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-965-default-verify-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-965-default-verify",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-965-default-verify-src"
+ ],
+ data: [
+ ":art-run-test-965-default-verify-expected-stdout",
+ ":art-run-test-965-default-verify-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-965-default-verify-expected-stdout",
+ out: ["art-run-test-965-default-verify-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-965-default-verify-expected-stderr",
+ out: ["art-run-test-965-default-verify-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/966-default-conflict/Android.bp b/test/966-default-conflict/Android.bp
new file mode 100644
index 0000000..19217fc
--- /dev/null
+++ b/test/966-default-conflict/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `966-default-conflict`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-966-default-conflict-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-966-default-conflict",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-966-default-conflict-src"
+ ],
+ data: [
+ ":art-run-test-966-default-conflict-expected-stdout",
+ ":art-run-test-966-default-conflict-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-966-default-conflict-expected-stdout",
+ out: ["art-run-test-966-default-conflict-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-966-default-conflict-expected-stderr",
+ out: ["art-run-test-966-default-conflict-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/967-default-ame/Android.bp b/test/967-default-ame/Android.bp
new file mode 100644
index 0000000..0268bf5
--- /dev/null
+++ b/test/967-default-ame/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `967-default-ame`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-967-default-ame-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-967-default-ame",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-967-default-ame-src"
+ ],
+ data: [
+ ":art-run-test-967-default-ame-expected-stdout",
+ ":art-run-test-967-default-ame-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-967-default-ame-expected-stdout",
+ out: ["art-run-test-967-default-ame-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-967-default-ame-expected-stderr",
+ out: ["art-run-test-967-default-ame-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/968-default-partial-compile-gen/build b/test/968-default-partial-compile-gen/build
deleted file mode 100755
index 04532b0..0000000
--- a/test/968-default-partial-compile-gen/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
- mkdir -p classes
- mkdir -p src
- echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
- # This will use java_exec.sh to execute the javac compiler. It will place the
- # compiled class files in ./classes and the expected values in expected-stdout.txt
- #
- # After this the src directory will contain the final versions of all files.
- ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
-else
- mkdir -p ./smali
- # Generate the smali files and expected-stdout.txt or fail
- ./util-src/generate_smali.py ./smali ./expected-stdout.txt
- # Use the default build script
- ./default-build "$@" --experimental default-methods
-fi
diff --git a/test/968-default-partial-compile-gen/build.py b/test/968-default-partial-compile-gen/build.py
new file mode 100644
index 0000000..435be54
--- /dev/null
+++ b/test/968-default-partial-compile-gen/build.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ if ctx.jvm:
+ return
+ ctx.default_build(api_level="default-methods")
diff --git a/test/968-default-partial-compile-gen/expected-stdout.txt b/test/968-default-partial-compile-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/968-default-partial-compile-gen/expected-stdout.txt
+++ b/test/968-default-partial-compile-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/968-default-partial-compile-gen/generate-sources b/test/968-default-partial-compile-gen/generate-sources
new file mode 100755
index 0000000..83aad65
--- /dev/null
+++ b/test/968-default-partial-compile-gen/generate-sources
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p classes
+ mkdir -p src
+ echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
+ # This will use java_exec.sh to execute the javac compiler. It will place the
+ # compiled class files in ./classes and the expected values in expected-stdout.txt
+ #
+ # After this the src directory will contain the final versions of all files.
+ ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
+else
+ mkdir -p ./smali
+ # Generate the smali files and expected-stdout.txt or fail
+ ./util-src/generate_smali.py ./smali ./expected-stdout.txt
+fi
diff --git a/test/968-default-partial-compile-gen/util-src/generate_java.py b/test/968-default-partial-compile-gen/util-src/generate_java.py
index a4a4a4d..6027df9 100755
--- a/test/968-default-partial-compile-gen/util-src/generate_java.py
+++ b/test/968-default-partial-compile-gen/util-src/generate_java.py
@@ -69,7 +69,6 @@
files = list(map(str, files))
cmd = ['sh', '-a', '-e', '--', str(self.javac)] + args + sorted(files)
subprocess.check_call(cmd)
- print("Compiled {} files".format(len(files)))
def execute(self):
"""
@@ -104,7 +103,6 @@
self.compile_files("-d {outdir} -cp {outdir}".format(outdir = self.classes_dir), files)
# Remove these from the set of interfaces to be compiled.
ifaces -= tops
- print("Finished compiling all files.")
return
def main(argv):
@@ -125,7 +123,6 @@
with expected_txt.open('w') as out:
print(mainclass.get_expected(), file=out)
- print("Wrote expected output")
Compiler(all_files, javac_exec, temp_dir, classes_dir).execute()
diff --git a/test/969-iface-super/build b/test/969-iface-super/build
deleted file mode 100755
index 44d6bd2..0000000
--- a/test/969-iface-super/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-# Generate the Main.java file or fail
-${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
-
-./default-build "$@" --experimental default-methods
diff --git a/test/969-iface-super/build.py b/test/969-iface-super/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/969-iface-super/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/969-iface-super/generate-sources b/test/969-iface-super/generate-sources
new file mode 100755
index 0000000..1fffc71
--- /dev/null
+++ b/test/969-iface-super/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+# Generate the Main.java file or fail
+${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
diff --git a/test/970-iface-super-resolution-gen/build b/test/970-iface-super-resolution-gen/build
deleted file mode 100755
index 6eecd71..0000000
--- a/test/970-iface-super-resolution-gen/build
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-# Should we compile with Java source code. By default we will use Smali.
-USES_JAVA_SOURCE="false"
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files
- mkdir -p src
- mkdir -p src2
- ./util-src/generate_java.py ./src2 ./src ./expected-stdout.txt
-else
- # Generate the smali files and expected-stdout.txt or fail
- mkdir -p smali
- ./util-src/generate_smali.py ./smali ./expected-stdout.txt
-fi
-
-./default-build "$@" --experimental default-methods
diff --git a/test/970-iface-super-resolution-gen/build.py b/test/970-iface-super-resolution-gen/build.py
new file mode 100644
index 0000000..114e452
--- /dev/null
+++ b/test/970-iface-super-resolution-gen/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ ctx.default_build(api_level="default-methods")
diff --git a/test/970-iface-super-resolution-gen/expected-stdout.txt b/test/970-iface-super-resolution-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/970-iface-super-resolution-gen/expected-stdout.txt
+++ b/test/970-iface-super-resolution-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/970-iface-super-resolution-gen/generate-sources b/test/970-iface-super-resolution-gen/generate-sources
new file mode 100755
index 0000000..175497f
--- /dev/null
+++ b/test/970-iface-super-resolution-gen/generate-sources
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+# Should we compile with Java source code. By default we will use Smali.
+USES_JAVA_SOURCE="false"
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files
+ mkdir -p src
+ mkdir -p src2
+ ./util-src/generate_java.py ./src2 ./src ./expected-stdout.txt
+else
+ # Generate the smali files and expected-stdout.txt or fail
+ mkdir -p smali
+ ./util-src/generate_smali.py ./smali ./expected-stdout.txt
+fi
diff --git a/test/971-iface-super/build b/test/971-iface-super/build
deleted file mode 100755
index 04532b0..0000000
--- a/test/971-iface-super/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
- mkdir -p classes
- mkdir -p src
- echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
- # This will use java_exec.sh to execute the javac compiler. It will place the
- # compiled class files in ./classes and the expected values in expected-stdout.txt
- #
- # After this the src directory will contain the final versions of all files.
- ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
-else
- mkdir -p ./smali
- # Generate the smali files and expected-stdout.txt or fail
- ./util-src/generate_smali.py ./smali ./expected-stdout.txt
- # Use the default build script
- ./default-build "$@" --experimental default-methods
-fi
diff --git a/test/971-iface-super/build.py b/test/971-iface-super/build.py
new file mode 100644
index 0000000..435be54
--- /dev/null
+++ b/test/971-iface-super/build.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2022 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 os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ if ctx.jvm:
+ return
+ ctx.default_build(api_level="default-methods")
diff --git a/test/971-iface-super/expected-stdout.txt b/test/971-iface-super/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/971-iface-super/expected-stdout.txt
+++ b/test/971-iface-super/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/971-iface-super/generate-sources b/test/971-iface-super/generate-sources
new file mode 100755
index 0000000..83aad65
--- /dev/null
+++ b/test/971-iface-super/generate-sources
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p classes
+ mkdir -p src
+ echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
+ # This will use java_exec.sh to execute the javac compiler. It will place the
+ # compiled class files in ./classes and the expected values in expected-stdout.txt
+ #
+ # After this the src directory will contain the final versions of all files.
+ ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
+else
+ mkdir -p ./smali
+ # Generate the smali files and expected-stdout.txt or fail
+ ./util-src/generate_smali.py ./smali ./expected-stdout.txt
+fi
diff --git a/test/971-iface-super/util-src/generate_java.py b/test/971-iface-super/util-src/generate_java.py
index dafe7f5..c043a4a 100755
--- a/test/971-iface-super/util-src/generate_java.py
+++ b/test/971-iface-super/util-src/generate_java.py
@@ -69,7 +69,6 @@
files = list(map(str, files))
cmd = ['sh', '-a', '-e', '--', str(self.javac)] + args + sorted(files)
subprocess.check_call(cmd)
- print("Compiled {} files".format(len(files)))
def execute(self):
"""
@@ -108,7 +107,6 @@
self.compile_files("-d {outdir} -cp {outdir}".format(outdir = self.classes_dir), files)
# Remove these from the set of interfaces to be compiled.
ifaces -= tops
- print("Finished compiling all files.")
return
def main(argv):
@@ -129,7 +127,6 @@
with expected_txt.open('w') as out:
print(mainclass.get_expected(), file=out)
- print("Wrote expected output")
Compiler(all_files, javac_exec, temp_dir, classes_dir).execute()
diff --git a/test/975-iface-private/build b/test/975-iface-private/build
deleted file mode 100755
index 14230c2..0000000
--- a/test/975-iface-private/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/975-iface-private/build.py b/test/975-iface-private/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/975-iface-private/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/978-virtual-interface/build b/test/978-virtual-interface/build
deleted file mode 100755
index 14230c2..0000000
--- a/test/978-virtual-interface/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/978-virtual-interface/build.py b/test/978-virtual-interface/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/978-virtual-interface/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/979-const-method-handle/build b/test/979-const-method-handle/build
deleted file mode 100755
index fa6a0ea..0000000
--- a/test/979-const-method-handle/build
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# make us exit on a failure
-set -e
-
-export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
-
-# Build the transformer to apply to compiled classes.
-mkdir classes
-${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
-${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
-rm -rf classes
-
-# Use API level 28 for DEX file support constant method handles.
-./default-build "$@" --api-level 28
diff --git a/test/979-const-method-handle/build.py b/test/979-const-method-handle/build.py
new file mode 100644
index 0000000..4eb0ccb
--- /dev/null
+++ b/test/979-const-method-handle/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level=28)
diff --git a/test/979-const-method-handle/generate-sources b/test/979-const-method-handle/generate-sources
new file mode 100755
index 0000000..0cd94d1
--- /dev/null
+++ b/test/979-const-method-handle/generate-sources
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Build the transformer to apply to compiled classes.
+mkdir classes
+${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
+${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
+rm -rf classes
+
+# Add annotation src files to our compiler inputs.
+cp -r util-src/annotations src/
diff --git a/test/979-const-method-handle/javac_post.sh b/test/979-const-method-handle/javac_post.sh
new file mode 100755
index 0000000..1be31d5
--- /dev/null
+++ b/test/979-const-method-handle/javac_post.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Move original classes to intermediate location.
+mv classes intermediate-classes
+mkdir classes
+
+# Transform intermediate classes.
+transformer_args="-cp ${ASM_JAR}:$PWD/transformer.jar transformer.ConstantTransformer"
+for class in intermediate-classes/*.class ; do
+ transformed_class=classes/$(basename ${class})
+ ${JAVA:-java} ${transformer_args} ${class} ${transformed_class}
+done
+
+# Remove class which we want missing at runtime.
+rm classes/MissingType.class
diff --git a/test/979-const-method-handle/javac_wrapper.sh b/test/979-const-method-handle/javac_wrapper.sh
deleted file mode 100755
index 77b6bc3..0000000
--- a/test/979-const-method-handle/javac_wrapper.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e
-
-# Add annotation src files to our compiler inputs.
-asrcs=util-src/annotations/*.java
-
-# Compile.
-$JAVAC "$@" $asrcs
-
-# Move original classes to intermediate location.
-mv classes intermediate-classes
-mkdir classes
-
-# Transform intermediate classes.
-transformer_args="-cp ${ASM_JAR}:$PWD/transformer.jar transformer.ConstantTransformer"
-for class in intermediate-classes/*.class ; do
- transformed_class=classes/$(basename ${class})
- ${JAVA:-java} ${transformer_args} ${class} ${transformed_class}
-done
diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java
index 5368a22..72d529b 100644
--- a/test/979-const-method-handle/src/Main.java
+++ b/test/979-const-method-handle/src/Main.java
@@ -72,6 +72,14 @@
return null;
}
+ @ConstantMethodType(
+ returnType = void.class,
+ parameterTypes = {MissingType.class})
+ private static MethodType missingType() {
+ unreachable();
+ return null;
+ }
+
private static void repeatConstMethodType0(MethodType expected) {
System.out.print("repeatConstMethodType0(");
System.out.print(expected);
@@ -189,6 +197,16 @@
return null;
}
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_GET,
+ owner = "PrivateMember",
+ fieldOrMethodName = "privateField",
+ descriptor = "I")
+ private static MethodHandle getPrivateField() {
+ unreachable();
+ return null;
+ }
+
private static void repeatConstMethodHandle() throws Throwable {
System.out.println("repeatConstMethodHandle()");
String[] values = {"A", "B", "C"};
@@ -243,5 +261,37 @@
System.out.println("Stack: capacity was " + stack.capacity());
stackTrim().invokeExact(stack);
System.out.println("Stack: capacity is " + stack.capacity());
+
+ // We used to not report in the compiler that loading a ConstMethodHandle/ConstMethodType
+ // can throw, which meant we were not catching the exception in the situation where we
+ // inline the loading.
+ try {
+ $inline$getPrivateField();
+ System.out.println("Expected IllegalAccessError");
+ } catch (IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ $inline$missingType();
+ System.out.println("Expected NoClassDefFoundError");
+ } catch (NoClassDefFoundError e) {
+ // expected
+ }
}
+
+ public static void $inline$getPrivateField() {
+ getPrivateField();
+ }
+
+ public static void $inline$missingType() {
+ missingType();
+ }
+}
+
+class PrivateMember {
+ private static int privateField;
+}
+
+class MissingType {
}
diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check
deleted file mode 100755
index 80a3cd7..0000000
--- a/test/980-redefine-object/check
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The number of paused background threads (and therefore InterruptedExceptions)
-# can change so we will just delete their lines from the log.
-
-cat "$2" \
- | sed "/Object allocated of type 'java\.lang\.InterruptedException'/d" \
- | sed "/Object allocated of type 'java\.lang\.Long'/d" \
- | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/980-redefine-object/run b/test/980-redefine-object/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/980-redefine-object/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/980-redefine-object/run.py b/test/980-redefine-object/run.py
new file mode 100644
index 0000000..c45595d
--- /dev/null
+++ b/test/980-redefine-object/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # The number of paused background threads (and therefore InterruptedExceptions)
+ # can change so we will just delete their lines from the log.
+ ctx.run(
+ fr"""sed -i -E "/Object allocated of type 'java\.lang\.(InterruptedException|Long)'/d" '{args.stdout_file}'"""
+ )
diff --git a/test/981-dedup-original-dex/run b/test/981-dedup-original-dex/run
deleted file mode 100755
index e92b873..0000000
--- a/test/981-dedup-original-dex/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/981-dedup-original-dex/run.py b/test/981-dedup-original-dex/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/981-dedup-original-dex/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/982-ok-no-retransform/run b/test/982-ok-no-retransform/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/982-ok-no-retransform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/982-ok-no-retransform/run.py b/test/982-ok-no-retransform/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/982-ok-no-retransform/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/983-source-transform-verify/run b/test/983-source-transform-verify/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/983-source-transform-verify/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/983-source-transform-verify/run.py b/test/983-source-transform-verify/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/983-source-transform-verify/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/984-obsolete-invoke/run b/test/984-obsolete-invoke/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/984-obsolete-invoke/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/984-obsolete-invoke/run.py b/test/984-obsolete-invoke/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/984-obsolete-invoke/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/985-re-obsolete/run b/test/985-re-obsolete/run
deleted file mode 100755
index e92b873..0000000
--- a/test/985-re-obsolete/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/985-re-obsolete/run.py b/test/985-re-obsolete/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/985-re-obsolete/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/986-native-method-bind/run b/test/986-native-method-bind/run
deleted file mode 100755
index e92b873..0000000
--- a/test/986-native-method-bind/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/986-native-method-bind/run.py b/test/986-native-method-bind/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/986-native-method-bind/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/987-agent-bind/run b/test/987-agent-bind/run
deleted file mode 100755
index e92b873..0000000
--- a/test/987-agent-bind/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/987-agent-bind/run.py b/test/987-agent-bind/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/987-agent-bind/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/988-method-trace/expected-stdout.txt b/test/988-method-trace/expected-stdout.txt
index 59077b9..9460503 100644
--- a/test/988-method-trace/expected-stdout.txt
+++ b/test/988-method-trace/expected-stdout.txt
@@ -13,13 +13,14 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibResult(java.lang.String,int,int) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(30)=832040
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$RecurOp()
.=> public java.lang.Object()
@@ -62,13 +63,14 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibResult(java.lang.String,int,int) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(5)=5
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$NativeOp()
.=> public java.lang.Object()
@@ -83,13 +85,14 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibResult(java.lang.String,int,int) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(5)=5
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
@@ -110,32 +113,42 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(int)
....=> public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int)
.....=> static int java.lang.Integer.stringSize(int)
-.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 2>
+.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 3>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
......=> private int java.lang.AbstractStringBuilder.newCapacity(int)
......<= private int java.lang.AbstractStringBuilder.newCapacity(int) -> <class java.lang.Integer: 34>
-......=> public static char[] java.util.Arrays.copyOf(char[],int)
+......=> public static byte[] java.util.Arrays.copyOf(byte[],int)
.......=> public static int java.lang.Math.min(int,int)
.......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16>
.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
-......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d, , a, r, g, u, m, e, n, t, :, , -, 1, 9, , <, , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]>
+......<= public static byte[] java.util.Arrays.copyOf(byte[],int) -> <class [B: [66, 97, 100, 32, 97, 114, 103, 117, 109, 101, 110, 116, 58, 32, 45, 49, 57, 32, 60, 32, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]>
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> static int java.lang.Integer.getChars(int,int,char[])
-.....<= static int java.lang.Integer.getChars(int,int,char[]) -> <class java.lang.Integer: 14>
+.....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+.....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+.....=> static int java.lang.Integer.getChars(int,int,byte[])
+.....<= static int java.lang.Integer.getChars(int,int,byte[]) -> <class java.lang.Integer: 14>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
@@ -144,21 +157,31 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int)
-.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
-....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+....=> public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int)
+.....=> public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int)
+.....<= public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....<= public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -170,10 +193,10 @@
......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- art.Test988.iter_fibonacci(Test988.java:269)
- art.Test988$IterOp.applyAsInt(Test988.java:264)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:358)
+ art.Test988.iter_fibonacci(Test988.java:280)
+ art.Test988$IterOp.applyAsInt(Test988.java:275)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:369)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -184,19 +207,20 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- art.Test988.iter_fibonacci(Test988.java:269)
- art.Test988$IterOp.applyAsInt(Test988.java:264)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:358)
+ art.Test988.iter_fibonacci(Test988.java:280)
+ art.Test988$IterOp.applyAsInt(Test988.java:275)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:369)
<additional hidden frames>
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$RecurOp()
.=> public java.lang.Object()
@@ -217,32 +241,42 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(int)
....=> public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int)
.....=> static int java.lang.Integer.stringSize(int)
-.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 2>
+.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 3>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
......=> private int java.lang.AbstractStringBuilder.newCapacity(int)
......<= private int java.lang.AbstractStringBuilder.newCapacity(int) -> <class java.lang.Integer: 34>
-......=> public static char[] java.util.Arrays.copyOf(char[],int)
+......=> public static byte[] java.util.Arrays.copyOf(byte[],int)
.......=> public static int java.lang.Math.min(int,int)
.......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16>
.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
-......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d, , a, r, g, u, m, e, n, t, :, , -, 1, 9, , <, , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]>
+......<= public static byte[] java.util.Arrays.copyOf(byte[],int) -> <class [B: [66, 97, 100, 32, 97, 114, 103, 117, 109, 101, 110, 116, 58, 32, 45, 49, 57, 32, 60, 32, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]>
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> static int java.lang.Integer.getChars(int,int,char[])
-.....<= static int java.lang.Integer.getChars(int,int,char[]) -> <class java.lang.Integer: 14>
+.....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+.....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+.....=> static int java.lang.Integer.getChars(int,int,byte[])
+.....<= static int java.lang.Integer.getChars(int,int,byte[]) -> <class java.lang.Integer: 14>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
@@ -251,21 +285,31 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int)
-.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
-....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+....=> public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int)
+.....=> public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int)
+.....<= public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....<= public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -277,10 +321,10 @@
......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- art.Test988.fibonacci(Test988.java:291)
- art.Test988$RecurOp.applyAsInt(Test988.java:286)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:359)
+ art.Test988.fibonacci(Test988.java:302)
+ art.Test988$RecurOp.applyAsInt(Test988.java:297)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:370)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -291,19 +335,20 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- art.Test988.fibonacci(Test988.java:291)
- art.Test988$RecurOp.applyAsInt(Test988.java:286)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:359)
+ art.Test988.fibonacci(Test988.java:302)
+ art.Test988$RecurOp.applyAsInt(Test988.java:297)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:370)
<additional hidden frames>
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$NativeOp()
.=> public java.lang.Object()
@@ -323,9 +368,9 @@
......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: bad argument
art.Test988.nativeFibonacci(Native Method)
- art.Test988$NativeOp.applyAsInt(Test988.java:301)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:360)
+ art.Test988$NativeOp.applyAsInt(Test988.java:312)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:371)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -336,19 +381,20 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(-19) -> java.lang.Error: bad argument
art.Test988.nativeFibonacci(Native Method)
- art.Test988$NativeOp.applyAsInt(Test988.java:301)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:360)
+ art.Test988$NativeOp.applyAsInt(Test988.java:312)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:371)
<additional hidden frames>
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> public final void <non-deterministic-type 0>.run()
.=> private static java.lang.Object java.lang.reflect.Proxy.invoke(java.lang.reflect.Proxy,java.lang.reflect.Method,java.lang.Object[]) throws java.lang.Throwable
diff --git a/test/988-method-trace/run b/test/988-method-trace/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/988-method-trace/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/988-method-trace/run.py b/test/988-method-trace/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/988-method-trace/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index 9c8ce4a..227ce62 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -253,11 +253,22 @@
}
}
- private static List<Printable> results = new ArrayList<>();
+ private static ArrayList<Printable> results = new ArrayList<>();
+ private static int results_index = 0;
// Starts with => enableMethodTracing
// .=> enableTracing
private static int cnt = 2;
+ static void addToResults(Printable obj) {
+ // Reserve space for the current object. If any other method entry callbacks are called they
+ // will reserve more space. Without this we may get into strange problems where ArrayList::add
+ // cecks there is enough space (which involves a couple of method calls) which then use up the
+ // space and by the time we actually add this record there is no capacity left.
+ results_index++;
+ results.ensureCapacity(results_index + 1);
+ results.add(obj);
+ }
+
// Iterative version
static final class IterOp implements IntUnaryOperator {
public int applyAsInt(int x) {
@@ -319,7 +330,7 @@
if ((cnt - 1) > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
return;
}
- results.add(new MethodEntry(m, cnt - 1));
+ addToResults(new MethodEntry(m, cnt - 1));
}
public static void notifyMethodExit(Executable m, boolean exception, Object result) {
@@ -330,9 +341,9 @@
}
if (exception) {
- results.add(new MethodThrownThrough(m, cnt));
+ addToResults(new MethodThrownThrough(m, cnt));
} else {
- results.add(new MethodReturn(m, result, cnt));
+ addToResults(new MethodReturn(m, result, cnt));
}
}
@@ -400,9 +411,9 @@
public static void doFibTest(int x, IntUnaryOperator op) {
try {
int y = op.applyAsInt(x);
- results.add(new FibResult("fibonacci(%d)=%d\n", x, y));
+ addToResults(new FibResult("fibonacci(%d)=%d\n", x, y));
} catch (Throwable t) {
- results.add(new FibThrow("fibonacci(%d) -> %s\n", x, t));
+ addToResults(new FibThrow("fibonacci(%d) -> %s\n", x, t));
}
}
diff --git a/test/989-method-trace-throw/run b/test/989-method-trace-throw/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/989-method-trace-throw/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/989-method-trace-throw/run.py b/test/989-method-trace-throw/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/989-method-trace-throw/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/990-field-trace/run b/test/990-field-trace/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/990-field-trace/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/990-field-trace/run.py b/test/990-field-trace/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/990-field-trace/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/991-field-trace-2/run b/test/991-field-trace-2/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/991-field-trace-2/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/991-field-trace-2/run.py b/test/991-field-trace-2/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/991-field-trace-2/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/992-source-data/run b/test/992-source-data/run
deleted file mode 100755
index e92b873..0000000
--- a/test/992-source-data/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-run "$@" --jvmti
diff --git a/test/992-source-data/run.py b/test/992-source-data/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/992-source-data/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/993-breakpoints-non-debuggable/Android.bp b/test/993-breakpoints-non-debuggable/Android.bp
new file mode 100644
index 0000000..53a9f28
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `993-breakpoints-non-debuggable`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-993-breakpoints-non-debuggable",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-993-breakpoints-non-debuggable-expected-stdout",
+ ":art-run-test-993-breakpoints-non-debuggable-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-993-breakpoints-non-debuggable-expected-stdout",
+ out: ["art-run-test-993-breakpoints-non-debuggable-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-993-breakpoints-non-debuggable-expected-stderr",
+ out: ["art-run-test-993-breakpoints-non-debuggable-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/993-breakpoints-non-debuggable/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/993-breakpoints-non-debuggable/expected-stderr.txt
diff --git a/test/993-breakpoints-non-debuggable/expected-stdout.txt b/test/993-breakpoints-non-debuggable/expected-stdout.txt
new file mode 100644
index 0000000..a4bd24b
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/expected-stdout.txt
@@ -0,0 +1,715 @@
+JNI_OnLoad called
+Running static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Invoking "Test993::breakpoint"
+ Breaking on [public static void art.Test993.breakpoint() @ 41]
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Invoking "Test993::breakpoint"
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+Running private static invoke
+ Breaking on []
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Invoking "Test993::privateBreakpoint"
+ Breaking on [private static void art.Test993.privateBreakpoint() @ 45]
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+ Invoking "Test993::privateBreakpoint"
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+Running interface static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Invoking "Breakable::iBreakpoint"
+ Breaking on [public static void art.Test993$Breakable.iBreakpoint() @ 51]
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Invoking "Breakable::iBreakpoint"
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+Running TestClass1 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Invoking "new TestClass1().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass1ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Invoking "new TestClass1ext().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass2 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass2ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass3 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Invoking "new TestClass3().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass3ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Invoking "new TestClass3ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running private instance invoke
+ Breaking on []
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breaking on [private void art.Test993$TestClass4.privateMethod() @ 118]
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running Vector constructor
+ Breaking on []
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Created: []
+ Constructing: new Vector()
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running Stack constructor
+ Breaking on []
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Created: []
+ Constructing: new Stack()
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>, public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running bcp static invoke
+ Breaking on []
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Invoking "Optional::empty"
+ Breaking on [public static java.util.Optional java.util.Optional.empty() @ <NON-DETERMINISTIC>]
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::empty"
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+Running bcp private static invoke
+ Breaking on []
+ Native invoking: private static long java.util.Random.seedUniquifier() args: [this: null]
+ Invoking "Random::seedUniquifier"
+ Breaking on [private static long java.util.Random.seedUniquifier() @ <NON-DETERMINISTIC>]
+ Native invoking: private static long java.util.Random.seedUniquifier() args: [this: null]
+ Breakpoint: private static long java.util.Random.seedUniquifier() @ line=<NON-DETERMINISTIC>
+ Invoking "Random::seedUniquifier"
+ Breakpoint: private static long java.util.Random.seedUniquifier() @ line=<NON-DETERMINISTIC>
+Running bcp private invoke
+ Breaking on []
+ Native invoking: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() args: [this: PT336H]
+ Invoking "Duration::toBigDecimalSeconds"
+ Breaking on [private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() @ <NON-DETERMINISTIC>]
+ Native invoking: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() args: [this: PT336H]
+ Breakpoint: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() @ line=<NON-DETERMINISTIC>
+ Invoking "Duration::toBigDecimalSeconds"
+ Breakpoint: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() @ line=<NON-DETERMINISTIC>
+Running bcp invoke
+ Breaking on []
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Invoking "Optional::isPresent"
+ Breaking on [public boolean java.util.Optional.isPresent() @ <NON-DETERMINISTIC>]
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::isPresent"
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+Running TestClass1 constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Created: TestClass1
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+Running TestClass1ext constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62, public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
diff --git a/test/993-breakpoints-non-debuggable/expected_cts.txt b/test/993-breakpoints-non-debuggable/expected_cts.txt
new file mode 100644
index 0000000..6c4e881
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/expected_cts.txt
@@ -0,0 +1,696 @@
+Running static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Invoking "Test993::breakpoint"
+ Breaking on [public static void art.Test993.breakpoint() @ 41]
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Invoking "Test993::breakpoint"
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+Running private static invoke
+ Breaking on []
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Invoking "Test993::privateBreakpoint"
+ Breaking on [private static void art.Test993.privateBreakpoint() @ 45]
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+ Invoking "Test993::privateBreakpoint"
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+Running interface static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Invoking "Breakable::iBreakpoint"
+ Breaking on [public static void art.Test993$Breakable.iBreakpoint() @ 51]
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Invoking "Breakable::iBreakpoint"
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+Running TestClass1 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Invoking "new TestClass1().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass1ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Invoking "new TestClass1ext().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass2 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass2ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass3 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Invoking "new TestClass3().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass3ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Invoking "new TestClass3ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running private instance invoke
+ Breaking on []
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breaking on [private void art.Test993$TestClass4.privateMethod() @ 118]
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running Vector constructor
+ Breaking on []
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Created: []
+ Constructing: new Vector()
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running Stack constructor
+ Breaking on []
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Created: []
+ Constructing: new Stack()
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>, public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running bcp static invoke
+ Breaking on []
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Invoking "Optional::empty"
+ Breaking on [public static java.util.Optional java.util.Optional.empty() @ <NON-DETERMINISTIC>]
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::empty"
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+Running bcp invoke
+ Breaking on []
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Invoking "Optional::isPresent"
+ Breaking on [public boolean java.util.Optional.isPresent() @ <NON-DETERMINISTIC>]
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::isPresent"
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+Running TestClass1 constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Created: TestClass1
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+Running TestClass1ext constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62, public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
diff --git a/test/993-breakpoints-non-debuggable/info.txt b/test/993-breakpoints-non-debuggable/info.txt
new file mode 100644
index 0000000..b5eb546
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/info.txt
@@ -0,0 +1,7 @@
+Test basic JVMTI breakpoint functionality.
+
+This test places a breakpoint on the first instruction of a number of functions
+that are entered in every way possible for the given class of method.
+
+It also tests that breakpoints don't interfere with each other by having
+multiple breakpoints be set at once.
diff --git a/test/993-breakpoints-non-debuggable/native_attach_agent.cc b/test/993-breakpoints-non-debuggable/native_attach_agent.cc
new file mode 100644
index 0000000..71d30eb
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/native_attach_agent.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 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 <sstream>
+
+#include "jni.h"
+#include "runtime.h"
+
+namespace art {
+namespace Test993BreakpointsNonDebuggable {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test993AttachAgent_setupJvmti(JNIEnv* env, jclass) {
+ Runtime* runtime = Runtime::Current();
+ std::ostringstream oss;
+ oss << (kIsDebugBuild ? "libtiagentd.so" : "libtiagent.so") << "=993-non-debuggable,art";
+ LOG(INFO) << "agent " << oss.str();
+ runtime->AttachAgent(env, oss.str(), nullptr);
+}
+
+} // namespace Test993BreakpointsNonDebuggable
+} // namespace art
diff --git a/test/993-breakpoints-non-debuggable/onload.cc b/test/993-breakpoints-non-debuggable/onload.cc
new file mode 100644
index 0000000..dbbcadc
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/onload.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 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 "jvmti.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test993BreakpointsNonDebuggable {
+
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
+static const jvmtiCapabilities limited_caps = {
+ .can_tag_objects = 1,
+ .can_generate_field_modification_events = 1,
+ .can_generate_field_access_events = 1,
+ .can_get_bytecodes = 1,
+ .can_get_synthetic_attribute = 1,
+ .can_get_owned_monitor_info = 0,
+ .can_get_current_contended_monitor = 1,
+ .can_get_monitor_info = 1,
+ .can_pop_frame = 0,
+ .can_redefine_classes = 0,
+ .can_signal_thread = 1,
+ .can_get_source_file_name = 1,
+ .can_get_line_numbers = 1,
+ .can_get_source_debug_extension = 1,
+ .can_access_local_variables = 0,
+ .can_maintain_original_method_order = 1,
+ .can_generate_single_step_events = 1,
+ .can_generate_exception_events = 0,
+ .can_generate_frame_pop_events = 0,
+ .can_generate_breakpoint_events = 1,
+ .can_suspend = 1,
+ .can_redefine_any_class = 0,
+ .can_get_current_thread_cpu_time = 0,
+ .can_get_thread_cpu_time = 0,
+ .can_generate_method_entry_events = 1,
+ .can_generate_method_exit_events = 1,
+ .can_generate_all_class_hook_events = 0,
+ .can_generate_compiled_method_load_events = 0,
+ .can_generate_monitor_events = 0,
+ .can_generate_vm_object_alloc_events = 1,
+ .can_generate_native_method_bind_events = 1,
+ .can_generate_garbage_collection_events = 1,
+ .can_generate_object_free_events = 1,
+ .can_force_early_return = 0,
+ .can_get_owned_monitor_stack_depth_info = 0,
+ .can_get_constant_pool = 0,
+ .can_set_native_method_prefix = 0,
+ .can_retransform_classes = 0,
+ .can_retransform_any_class = 0,
+ .can_generate_resource_exhaustion_heap_events = 0,
+ .can_generate_resource_exhaustion_threads_events = 0,
+};
+
+jint OnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), kArtTiVersion) != 0) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+
+ CheckJvmtiError(jvmti_env, jvmti_env->AddCapabilities(&limited_caps));
+ return 0;
+}
+
+} // namespace Test993BreakpointsNonDebuggable
+} // namespace art
diff --git a/test/993-breakpoints-non-debuggable/onload.h b/test/993-breakpoints-non-debuggable/onload.h
new file mode 100644
index 0000000..99ce0d2b
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/onload.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_993_BREAKPOINTS_NON_DEBUGGABLE_ONLOAD_H_
+#define ART_TEST_993_BREAKPOINTS_NON_DEBUGGABLE_ONLOAD_H_
+
+#include "jni.h"
+
+namespace art {
+namespace Test993BreakpointsNonDebuggable {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test993BreakpointsNonDebuggable
+} // namespace art
+
+#endif // ART_TEST_993_BREAKPOINTS_NON_DEBUGGABLE_ONLOAD_H_
diff --git a/test/993-breakpoints-non-debuggable/src/Main.java b/test/993-breakpoints-non-debuggable/src/Main.java
new file mode 100644
index 0000000..32fca46
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/Main.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ art.Test993AttachAgent.loadAgent();
+ art.Test993.run(true);
+ }
+}
diff --git a/test/993-breakpoints-non-debuggable/src/art/Breakpoint.java b/test/993-breakpoints-non-debuggable/src/art/Breakpoint.java
new file mode 120000
index 0000000..3673916
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/art/Breakpoint.java
@@ -0,0 +1 @@
+../../../jvmti-common/Breakpoint.java
\ No newline at end of file
diff --git a/test/993-breakpoints-non-debuggable/src/art/Test993.java b/test/993-breakpoints-non-debuggable/src/art/Test993.java
new file mode 120000
index 0000000..7466288
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/art/Test993.java
@@ -0,0 +1 @@
+../../../993-breakpoints/src/art/Test993.java
\ No newline at end of file
diff --git a/test/993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java b/test/993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java
new file mode 100644
index 0000000..ac82c84
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Test993AttachAgent {
+ public static native void setupJvmti();
+
+ public static void loadAgent() throws Exception {
+ setupJvmti();
+ }
+}
diff --git a/test/993-breakpoints/run b/test/993-breakpoints/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/993-breakpoints/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/993-breakpoints/run.py b/test/993-breakpoints/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/993-breakpoints/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/994-breakpoint-line/run b/test/994-breakpoint-line/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/994-breakpoint-line/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/994-breakpoint-line/run.py b/test/994-breakpoint-line/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/994-breakpoint-line/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/995-breakpoints-throw/run b/test/995-breakpoints-throw/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/995-breakpoints-throw/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/995-breakpoints-throw/run.py b/test/995-breakpoints-throw/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/995-breakpoints-throw/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/995-breakpoints-throw/src/Main.java b/test/995-breakpoints-throw/src/Main.java
index 6f80b43..e92cd9b 100644
--- a/test/995-breakpoints-throw/src/Main.java
+++ b/test/995-breakpoints-throw/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test995.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test995.run();
+ }
}
diff --git a/test/995-breakpoints-throw/src/art/Test995.java b/test/995-breakpoints-throw/src/art/Test995.java
index a4023fb..5fb5e08 100644
--- a/test/995-breakpoints-throw/src/art/Test995.java
+++ b/test/995-breakpoints-throw/src/art/Test995.java
@@ -21,116 +21,119 @@
import java.lang.reflect.Method;
public class Test995 {
- public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
- public static BreakpointHandler HANDLER = null;
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static BreakpointHandler HANDLER = null;
- public static void doNothing() { }
+ public static void doNothing() { }
- public static interface BreakpointHandler {
- public void breakpointReached(Executable e, long loc);
- }
-
- public static void breakpoint() {
- return;
- }
-
- public static void breakpointCatchLate() {
- doNothing();
- try {
- doNothing();
- } catch (Throwable t) {
- System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ public static interface BreakpointHandler {
+ public void breakpointReached(Executable e, long loc);
}
- }
- public static void breakpointCatch() {
- try {
- doNothing();
- } catch (Throwable t) {
- System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ public static void breakpoint() {
+ return;
}
- }
- public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
- System.out.println("\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
- HANDLER.breakpointReached(e, loc);
- }
-
-
- public static BreakpointHandler makeHandler(String name, BreakpointHandler h) {
- return new BreakpointHandler() {
- public String toString() {
- return name;
- }
- public void breakpointReached(Executable e, long loc) {
- h.breakpointReached(e, loc);
- }
- };
- }
-
- public static Runnable makeTest(String name, Runnable test) {
- return new Runnable() {
- public String toString() { return name; }
- public void run() { test.run(); }
- };
- }
-
- public static void run() throws Exception {
- // Set up breakpoints
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- Breakpoint.startBreakpointWatch(
- Test995.class,
- Test995.class.getDeclaredMethod(
- "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
- Thread.currentThread());
-
- Method breakpoint_method = Test995.class.getDeclaredMethod("breakpoint");
- Method breakpoint_catch_method = Test995.class.getDeclaredMethod("breakpointCatch");
- Method breakpoint_catch_late_method = Test995.class.getDeclaredMethod("breakpointCatchLate");
- MANAGER.setBreakpoint(breakpoint_method, Breakpoint.getStartLocation(breakpoint_method));
- MANAGER.setBreakpoint(
- breakpoint_catch_method, Breakpoint.getStartLocation(breakpoint_catch_method));
- MANAGER.setBreakpoint(
- breakpoint_catch_late_method, Breakpoint.getStartLocation(breakpoint_catch_late_method));
-
- BreakpointHandler[] handlers = new BreakpointHandler[] {
- makeHandler("do nothing", (e, l) -> {}),
- makeHandler("throw", (e, l) -> { throw new Error("throwing error!"); }),
- };
-
- Runnable[] tests = new Runnable[] {
- makeTest("call Test995::breakpoint", Test995::breakpoint),
- makeTest("call Test995::breakpointCatch", Test995::breakpointCatch),
- makeTest("call Test995::breakpointCatchLate", Test995::breakpointCatchLate),
- makeTest("catch subroutine Test995::breakpoint",
- () -> {
- try {
- breakpoint();
- } catch (Throwable t) {
- System.out.printf("Caught %s:\"%s\"\n", t.getClass().getName(), t.getMessage());
- }
- }),
- };
-
- for (BreakpointHandler handler : handlers) {
- for (Runnable test : tests) {
+ public static void breakpointCatchLate() {
+ doNothing();
try {
- HANDLER = handler;
- System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
- test, handler);
- test.run();
- System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
- test, handler);
- } catch (Throwable e) {
- System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
- test, e.getClass().getName(), e.getMessage(), handler);
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
}
- System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n", test, handler);
- HANDLER = null;
- }
}
- MANAGER.clearAllBreakpoints();
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- }
+ public static void breakpointCatch() {
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ HANDLER.breakpointReached(e, loc);
+ }
+
+
+ public static BreakpointHandler makeHandler(String name, BreakpointHandler h) {
+ return new BreakpointHandler() {
+ public String toString() {
+ return name;
+ }
+ public void breakpointReached(Executable e, long loc) {
+ h.breakpointReached(e, loc);
+ }
+ };
+ }
+
+ public static Runnable makeTest(String name, Runnable test) {
+ return new Runnable() {
+ public String toString() { return name; }
+ public void run() { test.run(); }
+ };
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test995.class,
+ Test995.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method breakpoint_method = Test995.class.getDeclaredMethod("breakpoint");
+ Method breakpoint_catch_method = Test995.class.getDeclaredMethod("breakpointCatch");
+ Method breakpoint_catch_late_method =
+ Test995.class.getDeclaredMethod("breakpointCatchLate");
+ MANAGER.setBreakpoint(breakpoint_method, Breakpoint.getStartLocation(breakpoint_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_method, Breakpoint.getStartLocation(breakpoint_catch_method));
+ MANAGER.setBreakpoint(breakpoint_catch_late_method,
+ Breakpoint.getStartLocation(breakpoint_catch_late_method));
+
+ BreakpointHandler[] handlers = new BreakpointHandler[] {
+ makeHandler("do nothing", (e, l) -> {}),
+ makeHandler("throw", (e, l) -> { throw new Error("throwing error!"); }),
+ };
+
+ Runnable[] tests = new Runnable[] {
+ makeTest("call Test995::breakpoint", Test995::breakpoint),
+ makeTest("call Test995::breakpointCatch", Test995::breakpointCatch),
+ makeTest("call Test995::breakpointCatchLate", Test995::breakpointCatchLate),
+ makeTest("catch subroutine Test995::breakpoint",
+ () -> {
+ try {
+ breakpoint();
+ } catch (Throwable t) {
+ System.out.printf("Caught %s:\"%s\"\n", t.getClass().getName(),
+ t.getMessage());
+ }
+ }),
+ };
+
+ for (BreakpointHandler handler : handlers) {
+ for (Runnable test : tests) {
+ try {
+ HANDLER = handler;
+ System.out.printf(
+ "Test \"%s\": Running breakpoint with handler \"%s\"\n", test, handler);
+ test.run();
+ System.out.printf(
+ "Test \"%s\": No error caught with handler \"%s\"\n", test, handler);
+ } catch (Throwable e) {
+ System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
+ test, e.getClass().getName(), e.getMessage(), handler);
+ }
+ System.out.printf(
+ "Test \"%s\": Finished running with handler \"%s\"\n", test, handler);
+ HANDLER = null;
+ }
+ }
+
+ MANAGER.clearAllBreakpoints();
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
}
diff --git a/test/996-breakpoint-obsolete/run b/test/996-breakpoint-obsolete/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/996-breakpoint-obsolete/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/996-breakpoint-obsolete/run.py b/test/996-breakpoint-obsolete/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/996-breakpoint-obsolete/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/996-breakpoint-obsolete/src/Main.java b/test/996-breakpoint-obsolete/src/Main.java
index 1b9b0a9..521d7fa 100644
--- a/test/996-breakpoint-obsolete/src/Main.java
+++ b/test/996-breakpoint-obsolete/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test996.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test996.run();
+ }
}
diff --git a/test/996-breakpoint-obsolete/src/art/Test996.java b/test/996-breakpoint-obsolete/src/art/Test996.java
index f3166c3..29dfff2 100644
--- a/test/996-breakpoint-obsolete/src/art/Test996.java
+++ b/test/996-breakpoint-obsolete/src/art/Test996.java
@@ -21,132 +21,133 @@
import java.util.Base64;
public class Test996 {
- // The line we are going to break on. This should be the println in the Transform class. We set a
- // breakpoint here after we have redefined the class.
- public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
+ // The line we are going to break on. This should be the println in the Transform class.
+ // We set a breakpoint here after we have redefined the class.
+ public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
- // The line we initially set a breakpoint on. This should be the doNothing call. This should be
- // cleared by the redefinition and should only be caught on the initial run.
- public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
+ // The line we initially set a breakpoint on. This should be the doNothing call. This should be
+ // cleared by the redefinition and should only be caught on the initial run.
+ public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
- // A function that doesn't do anything. Used for giving places to break on in a function.
- public static void doNothing() {}
+ // A function that doesn't do anything. Used for giving places to break on in a function.
+ public static void doNothing() {}
- public static final class Transform {
- public void run(Runnable r) {
- r.run();
- // Make sure we don't change anything above this line to keep all the breakpoint stuff
- // working. We will be putting a breakpoint before this line in the runnable.
- System.out.println("Should be after first breakpoint.");
- // This is set as a breakpoint prior to redefinition. It should not be hit.
- doNothing();
+ public static final class Transform {
+ public void run(Runnable r) {
+ r.run();
+ // Make sure we don't change anything above this line to keep all the breakpoint stuff
+ // working. We will be putting a breakpoint before this line in the runnable.
+ System.out.println("Should be after first breakpoint.");
+ // This is set as a breakpoint prior to redefinition. It should not be hit.
+ doNothing();
+ }
}
- }
- /* ******************************************************************************************** */
- // Try to keep all edits to this file below the above line. If edits need to be made above this
- // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
- // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
+ /* ****************************************************************************************** */
+ // Try to keep all edits to this file below the above line. If edits need to be made above this
+ // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
+ // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
- public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
+ public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
- // The base64 encoding of the following class. The redefined 'run' method should have the same
- // instructions as the original. This means that the locations of each line should stay the same
- // and the set of valid locations will not change. We use this to ensure that breakpoints are
- // removed from the redefined method.
- // public static final class Transform {
- // public void run(Runnable r) {
- // r.run();
- // System.out.println("Doing nothing transformed");
- // doNothing(); // try to catch non-removed breakpoints
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
- "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
- "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
- "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
- "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
- "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
- "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
- "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
- "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
- "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
- "AAEABwAZABwAGQ==");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
- "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
- "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
- "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
- "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
- "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
- "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
- "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
- "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
- "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
- "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
- "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
- "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
- "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
- "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
- "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
- "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
- "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
- "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
+ // The base64 encoding of the following class. The redefined 'run' method should have the same
+ // instructions as the original. This means that the locations of each line should stay the same
+ // and the set of valid locations will not change. We use this to ensure that breakpoints are
+ // removed from the redefined method.
+ // public static final class Transform {
+ // public void run(Runnable r) {
+ // r.run();
+ // System.out.println("Doing nothing transformed");
+ // doNothing(); // try to catch non-removed breakpoints
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
+ "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
+ "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
+ "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
+ "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
+ "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
+ "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
+ "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
+ "AAEABwAZABwAGQ==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
+ "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
+ "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
+ "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+ "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
+ "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
+ "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
+ "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
+ "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
+ "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
+ "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
+ "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
+ "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
+ "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
+ "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
+ "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
+ "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
+ "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
+ "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
- public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
- int line = Breakpoint.locationToLine(e, loc);
- if (line == -1 && e.getName().equals("run") && e.getDeclaringClass().equals(Transform.class)) {
- // RI always reports line = -1 for obsolete methods. Just replace it with the real line for
- // consistency.
- line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ int line = Breakpoint.locationToLine(e, loc);
+ if (line == -1 && e.getName().equals("run")
+ && e.getDeclaringClass().equals(Transform.class)) {
+ // RI always reports line = -1 for obsolete methods. Just replace it with the real line
+ // for consistency.
+ line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
+ }
+ System.out.println("Breakpoint reached: " + e + " @ line=" + line);
}
- System.out.println("Breakpoint reached: " + e + " @ line=" + line);
- }
- public static void run() throws Exception {
- // Set up breakpoints
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- Breakpoint.startBreakpointWatch(
- Test996.class,
- Test996.class.getDeclaredMethod(
- "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
- Thread.currentThread());
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test996.class,
+ Test996.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
- Transform t = new Transform();
- Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
- final long obsolete_breakpoint_location =
- Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+ Transform t = new Transform();
+ Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
+ final long obsolete_breakpoint_location = Breakpoint.lineToLocation(
+ non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
- System.out.println("Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
- long initial_breakpoint_location =
- Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
- Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
+ System.out.println(
+ "Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ long initial_breakpoint_location = Breakpoint.lineToLocation(
+ non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
- System.out.println("Running transform without redefinition.");
- t.run(() -> {});
+ System.out.println("Running transform without redefinition.");
+ t.run(() -> {});
- System.out.println("Running transform with redefinition.");
- t.run(() -> {
- System.out.println("Redefining calling function!");
- // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- System.out.println("Setting breakpoint on now obsolete method to line " +
- TRANSFORM_BREAKPOINT_REDEFINED_LINE);
- setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
- });
- System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
- t.run(() -> {});
+ System.out.println("Running transform with redefinition.");
+ t.run(() -> {
+ System.out.println("Redefining calling function!");
+ // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ System.out.println("Setting breakpoint on now obsolete method to line " +
+ TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+ setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
+ });
+ System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
+ t.run(() -> {});
- System.out.println("Setting initial breakpoint on redefined method.");
- long final_breakpoint_location =
- Breakpoint.lineToLocation(non_obsolete_run_method,
- TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
- Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
- t.run(() -> {});
+ System.out.println("Setting initial breakpoint on redefined method.");
+ long final_breakpoint_location = Breakpoint.lineToLocation(
+ non_obsolete_run_method, TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
+ t.run(() -> {});
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- }
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
- public static native void setBreakpointOnObsoleteMethod(long location);
+ public static native void setBreakpointOnObsoleteMethod(long location);
}
diff --git a/test/997-single-step/run b/test/997-single-step/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/997-single-step/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
diff --git a/test/997-single-step/run.py b/test/997-single-step/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/997-single-step/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/997-single-step/src/Main.java b/test/997-single-step/src/Main.java
index 1927f04..a3e911d 100644
--- a/test/997-single-step/src/Main.java
+++ b/test/997-single-step/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test997.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test997.run();
+ }
}
diff --git a/test/997-single-step/src/art/Test997.java b/test/997-single-step/src/art/Test997.java
index a7a522d..88fbb87 100644
--- a/test/997-single-step/src/art/Test997.java
+++ b/test/997-single-step/src/art/Test997.java
@@ -21,62 +21,63 @@
import java.lang.reflect.Method;
public class Test997 {
- static final int NO_LAST_LINE_NUMBER = -1;
- static int LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
- static Method DO_MULTIPATH_METHOD;
+ static final int NO_LAST_LINE_NUMBER = -1;
+ static int LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ static Method DO_MULTIPATH_METHOD;
- static {
- try {
- DO_MULTIPATH_METHOD = Test997.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
- } catch (Exception e) {
- throw new Error("could not find method doMultiPath", e);
- }
- }
-
- // Function that acts simply to ensure there are multiple lines.
- public static void doNothing() {}
-
- // Method with multiple paths we can break on.
- public static void doMultiPath(boolean bit) {
- doNothing();
- if (bit) {
- doNothing();
- } else {
- doNothing();
- }
- doNothing();
- }
-
- public static void notifySingleStep(Thread thr, Executable e, long loc) {
- if (!e.equals(DO_MULTIPATH_METHOD)) {
- // Only report steps in doMultiPath
- return;
- }
- int cur_line = Breakpoint.locationToLine(e, loc);
- // Only report anything when the line number changes. This is so we can run this test against
- // both the RI and ART and also to prevent front-end compiler changes from affecting output.
- if (LAST_LINE_NUMBER == NO_LAST_LINE_NUMBER || LAST_LINE_NUMBER != cur_line) {
- LAST_LINE_NUMBER = cur_line;
- System.out.println("Single step: " + e + " @ line=" + cur_line);
- }
- }
-
- public static void resetTest() {
- LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
- }
-
- public static void run() throws Exception {
- boolean[] values = new boolean[] { true, false };
- Trace.enableSingleStepTracing(Test997.class,
- Test997.class.getDeclaredMethod(
- "notifySingleStep", Thread.class, Executable.class, Long.TYPE),
- Thread.currentThread());
- for (boolean arg : values) {
- System.out.println("Stepping through doMultiPath(" + arg + ")");
- resetTest();
- doMultiPath(arg);
+ static {
+ try {
+ DO_MULTIPATH_METHOD = Test997.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+ } catch (Exception e) {
+ throw new Error("could not find method doMultiPath", e);
+ }
}
- Trace.disableTracing(Thread.currentThread());
- }
+ // Function that acts simply to ensure there are multiple lines.
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ doNothing();
+ } else {
+ doNothing();
+ }
+ doNothing();
+ }
+
+ public static void notifySingleStep(Thread thr, Executable e, long loc) {
+ if (!e.equals(DO_MULTIPATH_METHOD)) {
+ // Only report steps in doMultiPath
+ return;
+ }
+ int cur_line = Breakpoint.locationToLine(e, loc);
+ // Only report anything when the line number changes. This is so we can run this test
+ // against both the RI and ART and also to prevent front-end compiler changes from
+ // affecting output.
+ if (LAST_LINE_NUMBER == NO_LAST_LINE_NUMBER || LAST_LINE_NUMBER != cur_line) {
+ LAST_LINE_NUMBER = cur_line;
+ System.out.println("Single step: " + e + " @ line=" + cur_line);
+ }
+ }
+
+ public static void resetTest() {
+ LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ }
+
+ public static void run() throws Exception {
+ boolean[] values = new boolean[] { true, false };
+ Trace.enableSingleStepTracing(Test997.class,
+ Test997.class.getDeclaredMethod(
+ "notifySingleStep", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+ for (boolean arg : values) {
+ System.out.println("Stepping through doMultiPath(" + arg + ")");
+ resetTest();
+ doMultiPath(arg);
+ }
+
+ Trace.disableTracing(Thread.currentThread());
+ }
}
diff --git a/test/998-redefine-use-after-free/run b/test/998-redefine-use-after-free/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/998-redefine-use-after-free/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/998-redefine-use-after-free/run.py b/test/998-redefine-use-after-free/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/998-redefine-use-after-free/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java b/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
index 2193a63..349b73e 100644
--- a/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
+++ b/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
@@ -18,26 +18,24 @@
import java.util.Base64;
public class DexCacheSmash {
- static class Transform {
- public void foo() {}
- public void bar() {}
- public String getId() {
- return "TRANSFORM_INITIAL";
+ static class Transform {
+ public void foo() {}
+ public void bar() {}
+ public String getId() {
+ return "TRANSFORM_INITIAL";
+ }
}
- }
- static class Transform2 {
- public String getId() {
- return "TRANSFORM2_INITIAL";
+ static class Transform2 {
+ public String getId() {
+ return "TRANSFORM2_INITIAL";
+ }
}
- }
- /**
- * A base64 encoding of the dex/class file of the Transform class above.
- */
- static final Redefinition.CommonClassDefinition TRANSFORM_INITIAL =
- new Redefinition.CommonClassDefinition(Transform.class,
- Base64.getDecoder().decode(
+ /**
+ * A base64 encoding of the dex/class file of the Transform class above.
+ */
+ static final byte[] TRANSFORM_INITIAL_CLASS_FILE_BYTES = Base64.getDecoder().decode(
"yv66vgAAADQAFwoABAAPCAAQBwASBwAVAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" +
"ZXJUYWJsZQEAA2ZvbwEAA2JhcgEABWdldElkAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3Vy" +
"Y2VGaWxlAQASRGV4Q2FjaGVTbWFzaC5qYXZhDAAFAAYBABFUUkFOU0ZPUk1fSU5JVElBTAcAFgEA" +
@@ -46,8 +44,8 @@
"AAEAAAAFKrcAAbEAAAABAAgAAAAGAAEAAAATAAEACQAGAAEABwAAABkAAAABAAAAAbEAAAABAAgA" +
"AAAGAAEAAAAUAAEACgAGAAEABwAAABkAAAABAAAAAbEAAAABAAgAAAAGAAEAAAAVAAEACwAMAAEA" +
"BwAAABsAAQABAAAAAxICsAAAAAEACAAAAAYAAQAAABcAAgANAAAAAgAOABQAAAAKAAEAAwARABMA" +
- "CA=="),
- Base64.getDecoder().decode(
+ "CA==");
+ static final byte[] TRANSFORM_INITIAL_DEX_FILE_BYTES = Base64.getDecoder().decode(
"ZGV4CjAzNQDhg9CfghG1SRlLClguRuFYsqihr4F7NsGQAwAAcAAAAHhWNBIAAAAAAAAAAOQCAAAS" +
"AAAAcAAAAAcAAAC4AAAAAgAAANQAAAAAAAAAAAAAAAUAAADsAAAAAQAAABQBAABcAgAANAEAAKgB" +
"AACwAQAAxAEAAMcBAADiAQAA8wEAABcCAAA3AgAASwIAAF8CAAByAgAAfQIAAIACAACNAgAAkgIA" +
@@ -63,59 +61,60 @@
"AAcOABUABw4AFAAHDgAXAAcOAAICAREYAQIDAgwECBAXCgAAAQMAgIAEwAIBAdgCAQHsAgEBgAMO" +
"AAAAAAAAAAEAAAAAAAAAAQAAABIAAABwAAAAAgAAAAcAAAC4AAAAAwAAAAIAAADUAAAABQAAAAUA" +
"AADsAAAABgAAAAEAAAAUAQAAAxAAAAEAAAA0AQAAASAAAAQAAABAAQAABiAAAAEAAACYAQAAAiAA" +
- "ABIAAACoAQAAAyAAAAQAAACrAgAABCAAAAIAAAC/AgAAACAAAAEAAADOAgAAABAAAAEAAADkAgAA"));
+ "ABIAAACoAQAAAyAAAAQAAACrAgAABCAAAAIAAAC/AgAAACAAAAEAAADOAgAAABAAAAEAAADkAgAA");
+ static final Redefinition.CommonClassDefinition TRANSFORM_INITIAL =
+ new Redefinition.CommonClassDefinition(Transform.class,
+ TRANSFORM_INITIAL_CLASS_FILE_BYTES, TRANSFORM_INITIAL_DEX_FILE_BYTES);
- /**
- * A base64 encoding of the following (invalid) class.
- *
- * .class LDexCacheSmash$Transform2;
- * .super Ljava/lang/Object;
- * .source "DexCacheSmash.java"
- *
- * # annotations
- * .annotation system Ldalvik/annotation/EnclosingClass;
- * value = LDexCacheSmash;
- * .end annotation
- *
- * .annotation system Ldalvik/annotation/InnerClass;
- * accessFlags = 0x8
- * name = "Transform2"
- * .end annotation
- *
- *
- * # direct methods
- * .method constructor <init>()V
- * .registers 1
- *
- * .prologue
- * .line 26
- * invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- *
- * return-void
- * .end method
- *
- *
- * # virtual methods
- * .method public getId()Ljava/lang/String;
- * .registers 2
- *
- * .prologue
- * .line 28
- * # NB Fails verification due to this function not returning a String.
- * return-void
- * .end method
- */
- static final Redefinition.CommonClassDefinition TRANSFORM2_INVALID =
- new Redefinition.CommonClassDefinition(Transform2.class,
- Base64.getDecoder().decode(
+ /**
+ * A base64 encoding of the following (invalid) class.
+ *
+ * .class LDexCacheSmash$Transform2;
+ * .super Ljava/lang/Object;
+ * .source "DexCacheSmash.java"
+ *
+ * # annotations
+ * .annotation system Ldalvik/annotation/EnclosingClass;
+ * value = LDexCacheSmash;
+ * .end annotation
+ *
+ * .annotation system Ldalvik/annotation/InnerClass;
+ * accessFlags = 0x8
+ * name = "Transform2"
+ * .end annotation
+ *
+ *
+ * # direct methods
+ * .method constructor <init>()V
+ * .registers 1
+ *
+ * .prologue
+ * .line 26
+ * invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ *
+ * return-void
+ * .end method
+ *
+ *
+ * # virtual methods
+ * .method public getId()Ljava/lang/String;
+ * .registers 2
+ *
+ * .prologue
+ * .line 28
+ * # NB Fails verification due to this function not returning a String.
+ * return-void
+ * .end method
+ */
+ static final byte[] TRANSFORM2_INVALID_CLASS_FILE_BYTES = Base64.getDecoder().decode(
"yv66vgAAADQAEwcAEgcAEQEABjxpbml0PgEAAygpVgEABENvZGUKAAIAEAEAD0xpbmVOdW1iZXJU" +
"YWJsZQEABWdldElkAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQASRGV4Q2Fj" +
"aGVTbWFzaC5qYXZhAQAMSW5uZXJDbGFzc2VzBwAPAQAKVHJhbnNmb3JtMgEADURleENhY2hlU21h" +
"c2gMAAMABAEAEGphdmEvbGFuZy9PYmplY3QBABhEZXhDYWNoZVNtYXNoJFRyYW5zZm9ybTIAIAAB" +
"AAIAAAAAAAIAAAADAAQAAQAFAAAAHQABAAEAAAAFKrcABrEAAAABAAcAAAAGAAEAAAAaAAEACAAJ" +
"AAEABQAAABkAAQABAAAAAbEAAAABAAcAAAAGAAEAAAAcAAIACgAAAAIACwAMAAAACgABAAEADQAO" +
- "AAg="),
- Base64.getDecoder().decode(
+ "AAg=");
+ static final byte[] TRANSFORM2_INVALID_DEX_FILE_BYTES = Base64.getDecoder().decode(
"ZGV4CjAzNQCFcegr6Ns+I7iEF4uLRkUX4yGrLhP6soEgAwAAcAAAAHhWNBIAAAAAAAAAAHQCAAAP" +
"AAAAcAAAAAcAAACsAAAAAgAAAMgAAAAAAAAAAAAAAAMAAADgAAAAAQAAAPgAAAAIAgAAGAEAABgB" +
"AAAgAQAANAEAADcBAABTAQAAZAEAAIgBAACoAQAAvAEAANABAADcAQAA3wEAAOwBAADzAQAA+QEA" +
@@ -130,26 +129,31 @@
"BA4AAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAACAAAABwAAAKwAAAADAAAAAgAAAMgAAAAFAAAA" +
"AwAAAOAAAAAGAAAAAQAAAPgAAAACIAAADwAAABgBAAAEIAAAAgAAAAACAAADEAAAAgAAABACAAAG" +
"IAAAAQAAACACAAADIAAAAgAAADACAAABIAAAAgAAADwCAAAAIAAAAQAAAGYCAAAAEAAAAQAAAHQC" +
- "AAA="));
+ "AAA=");
+ static final Redefinition.CommonClassDefinition TRANSFORM2_INVALID =
+ new Redefinition.CommonClassDefinition(Transform2.class,
+ TRANSFORM2_INVALID_CLASS_FILE_BYTES, TRANSFORM2_INVALID_DEX_FILE_BYTES);
- public static void run() throws Exception {
- try {
- Redefinition.doMultiClassRedefinition(TRANSFORM2_INVALID);
- } catch (Exception e) {
- if (!e.getMessage().endsWith("JVMTI_ERROR_FAILS_VERIFICATION")) {
- throw new Error(
- "Unexpected error: Expected failure due to JVMTI_ERROR_FAILS_VERIFICATION", e);
- }
+ public static void run() throws Exception {
+ try {
+ Redefinition.doMultiClassRedefinition(TRANSFORM2_INVALID);
+ } catch (Exception e) {
+ if (!e.getMessage().endsWith("JVMTI_ERROR_FAILS_VERIFICATION")) {
+ throw new Error(
+ "Unexpected error: Expected failure due to JVMTI_ERROR_FAILS_VERIFICATION",
+ e);
+ }
+ }
+ // Doing this redefinition after a redefinition that failed due to FAILS_VERIFICATION could
+ // cause a use-after-free of the Transform2's DexCache by the redefinition code if it
+ // happens that the native pointer of the art::DexFile created for the Transform
+ // redefinition aliases the one created for Transform2's failed redefinition.
+ //
+ // Due to the order of checks performed by the redefinition code FAILS_VERIFICATION is the
+ // only failure mode that can cause Use-after-frees in this way.
+ //
+ // This should never throw any exceptions (except perhaps OOME in very strange
+ // circumstances).
+ Redefinition.doMultiClassRedefinition(TRANSFORM_INITIAL);
}
- // Doing this redefinition after a redefinition that failed due to FAILS_VERIFICATION could
- // cause a use-after-free of the Transform2's DexCache by the redefinition code if it happens
- // that the native pointer of the art::DexFile created for the Transform redefinition aliases
- // the one created for Transform2's failed redefinition.
- //
- // Due to the order of checks performed by the redefinition code FAILS_VERIFICATION is the only
- // failure mode that can cause Use-after-frees in this way.
- //
- // This should never throw any exceptions (except perhaps OOME in very strange circumstances).
- Redefinition.doMultiClassRedefinition(TRANSFORM_INITIAL);
- }
}
diff --git a/test/998-redefine-use-after-free/src-ex/art/Redefinition.java b/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
index 56d2938..7b37afd 100644
--- a/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
+++ b/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
@@ -19,73 +19,69 @@
import java.util.ArrayList;
// Common Redefinition functions. Placed here for use by CTS
public class Redefinition {
- public static final class CommonClassDefinition {
- public final Class<?> target;
- public final byte[] class_file_bytes;
- public final byte[] dex_file_bytes;
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
- public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
- this.target = target;
- this.class_file_bytes = class_file_bytes;
- this.dex_file_bytes = dex_file_bytes;
+ public CommonClassDefinition(
+ Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
}
- }
- // A set of possible test configurations. Test should set this if they need to.
- // This must be kept in sync with the defines in ti-agent/common_helper.cc
- public static enum Config {
- COMMON_REDEFINE(0),
- COMMON_RETRANSFORM(1),
- COMMON_TRANSFORM(2);
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
- private final int val;
- private Config(int val) {
- this.val = val;
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
}
- }
- public static void setTestConfiguration(Config type) {
- nativeSetTestConfiguration(type.val);
- }
-
- private static native void nativeSetTestConfiguration(int type);
-
- // Transforms the class
- public static native void doCommonClassRedefinition(Class<?> target,
- byte[] classfile,
- byte[] dexfile);
-
- public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
- ArrayList<Class<?>> classes = new ArrayList<>();
- ArrayList<byte[]> class_files = new ArrayList<>();
- ArrayList<byte[]> dex_files = new ArrayList<>();
-
- for (CommonClassDefinition d : defs) {
- classes.add(d.target);
- class_files.add(d.class_file_bytes);
- dex_files.add(d.dex_file_bytes);
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
}
- doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
- class_files.toArray(new byte[0][]),
- dex_files.toArray(new byte[0][]));
- }
- public static void addMultiTransformationResults(CommonClassDefinition... defs) {
- for (CommonClassDefinition d : defs) {
- addCommonTransformationResult(d.target.getCanonicalName(),
- d.class_file_bytes,
- d.dex_file_bytes);
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(
+ Class<?> target, byte[] classfile, byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]), dex_files.toArray(new byte[0][]));
}
- }
- public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
- byte[][] classfiles,
- byte[][] dexfiles);
- public static native void doCommonClassRetransformation(Class<?>... target);
- public static native void setPopRetransformations(boolean pop);
- public static native void popTransformationFor(String name);
- public static native void enableCommonRetransformation(boolean enable);
- public static native void addCommonTransformationResult(String target_name,
- byte[] class_bytes,
- byte[] dex_bytes);
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(
+ d.target.getCanonicalName(), d.class_file_bytes, d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(
+ Class<?>[] targets, byte[][] classfiles, byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(
+ String target_name, byte[] class_bytes, byte[] dex_bytes);
}
diff --git a/test/998-redefine-use-after-free/src/Main.java b/test/998-redefine-use-after-free/src/Main.java
index cd3babf..fcc759f 100644
--- a/test/998-redefine-use-after-free/src/Main.java
+++ b/test/998-redefine-use-after-free/src/Main.java
@@ -17,38 +17,39 @@
import java.lang.reflect.*;
public class Main {
- public static final String TEST_NAME = "998-redefine-use-after-free";
- public static final int REPS = 1000;
- public static final int STEP = 100;
+ public static final String TEST_NAME = "998-redefine-use-after-free";
+ public static final int REPS = 1000;
+ public static final int STEP = 100;
- public static void main(String[] args) throws Exception {
- for (int i = 0; i < REPS; i += STEP) {
- runSeveralTimes(STEP);
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < REPS; i += STEP) {
+ runSeveralTimes(STEP);
+ }
}
- }
- public static ClassLoader getClassLoaderFor(String location) throws Exception {
- try {
- Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
- Constructor<?> ctor = class_loader_class.getConstructor(String.class, ClassLoader.class);
- return (ClassLoader)ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar",
- Main.class.getClassLoader());
- } catch (ClassNotFoundException e) {
- // Running on RI. Use URLClassLoader.
- return new java.net.URLClassLoader(
- new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
+ public static ClassLoader getClassLoaderFor(String location) throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+ Constructor<?> ctor =
+ class_loader_class.getConstructor(String.class, ClassLoader.class);
+ return (ClassLoader) ctor.newInstance(
+ location + "/" + TEST_NAME + "-ex.jar", Main.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Running on RI. Use URLClassLoader.
+ return new java.net.URLClassLoader(
+ new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
+ }
}
- }
- // Run the redefinition several times on a single class-loader to try to trigger the
- // Use-after-free bug b/62237378
- public static void runSeveralTimes(int times) throws Exception {
- ClassLoader c = getClassLoaderFor(System.getenv("DEX_LOCATION"));
+ // Run the redefinition several times on a single class-loader to try to trigger the
+ // Use-after-free bug b/62237378
+ public static void runSeveralTimes(int times) throws Exception {
+ ClassLoader c = getClassLoaderFor(System.getenv("DEX_LOCATION"));
- Class<?> klass = (Class<?>)c.loadClass("DexCacheSmash");
- Method m = klass.getDeclaredMethod("run");
- for (int i = 0 ; i < times; i++) {
- m.invoke(null);
+ Class<?> klass = (Class<?>) c.loadClass("DexCacheSmash");
+ Method m = klass.getDeclaredMethod("run");
+ for (int i = 0; i < times; i++) {
+ m.invoke(null);
+ }
}
- }
}
diff --git a/test/999-redefine-hiddenapi/build b/test/999-redefine-hiddenapi/build
deleted file mode 100644
index f4b029f..0000000
--- a/test/999-redefine-hiddenapi/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/999-redefine-hiddenapi/build.py b/test/999-redefine-hiddenapi/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/999-redefine-hiddenapi/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 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.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/999-redefine-hiddenapi/run b/test/999-redefine-hiddenapi/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/999-redefine-hiddenapi/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti
diff --git a/test/999-redefine-hiddenapi/run.py b/test/999-redefine-hiddenapi/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/999-redefine-hiddenapi/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/999-redefine-hiddenapi/src-ex/Test999.java b/test/999-redefine-hiddenapi/src-ex/Test999.java
index 97495c5..d82619f 100644
--- a/test/999-redefine-hiddenapi/src-ex/Test999.java
+++ b/test/999-redefine-hiddenapi/src-ex/Test999.java
@@ -17,9 +17,9 @@
package art;
public class Test999 {
- public void foo() {
- System.out.println("hello");
- }
+ public void foo() {
+ System.out.println("hello");
+ }
- public int bar = 42;
+ public int bar = 42;
}
diff --git a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
index c1b838c..3bdb2b6 100644
--- a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
+++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
@@ -17,9 +17,9 @@
package art;
public class Test999 {
- public void foo() {
- System.out.println("Goodbye");
- }
+ public void foo() {
+ System.out.println("Goodbye");
+ }
- public int bar = 64;
+ public int bar = 64;
}
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
index 014ea16..70dc006 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -19,92 +19,92 @@
import java.util.Base64;
public class Main {
- public static void main(String[] args) throws ClassNotFoundException {
- System.loadLibrary(args[0]);
+ public static void main(String[] args) throws ClassNotFoundException {
+ System.loadLibrary(args[0]);
- // Run the initialization routine. This will enable hidden API checks in
- // the runtime, in case they are not enabled by default.
- init();
+ // Run the initialization routine. This will enable hidden API checks in
+ // the runtime, in case they are not enabled by default.
+ init();
- // Load the '-ex' APK and attach it to the boot class path.
- appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
+ // Load the '-ex' APK and attach it to the boot class path.
+ appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
- // Find the test class in boot class loader and verify that its members are hidden.
- Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
- assertFieldIsHidden(klass, "before redefinition");
- assertMethodIsHidden(klass, "before redefinition");
+ // Find the test class in boot class loader and verify that its members are hidden.
+ Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
+ assertFieldIsHidden(klass, "before redefinition");
+ assertMethodIsHidden(klass, "before redefinition");
- // Redefine the class using JVMTI. Use dex file without hiddenapi flags.
- art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
- art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES);
+ // Redefine the class using JVMTI. Use dex file without hiddenapi flags.
+ art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
+ art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES);
- // Verify that the class members are still hidden.
- assertFieldIsHidden(klass, "after first redefinition");
- assertMethodIsHidden(klass, "after first redefinition");
- }
-
- private static void assertMethodIsHidden(Class<?> klass, String msg) {
- try {
- klass.getDeclaredMethod("foo");
- // Unexpected. Should have thrown NoSuchMethodException.
- throw new RuntimeException("Method should not be accessible " + msg);
- } catch (NoSuchMethodException ex) {
+ // Verify that the class members are still hidden.
+ assertFieldIsHidden(klass, "after first redefinition");
+ assertMethodIsHidden(klass, "after first redefinition");
}
- }
- private static void assertFieldIsHidden(Class<?> klass, String msg) {
- try {
- klass.getDeclaredField("bar");
- // Unexpected. Should have thrown NoSuchFieldException.
- throw new RuntimeException("Field should not be accessible " + msg);
- } catch (NoSuchFieldException ex) {
+ private static void assertMethodIsHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredMethod("foo");
+ // Unexpected. Should have thrown NoSuchMethodException.
+ throw new RuntimeException("Method should not be accessible " + msg);
+ } catch (NoSuchMethodException ex) {
+ }
}
- }
- private static final String DEX_EXTRA =
- new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath();
+ private static void assertFieldIsHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredField("bar");
+ // Unexpected. Should have thrown NoSuchFieldException.
+ throw new RuntimeException("Field should not be accessible " + msg);
+ } catch (NoSuchFieldException ex) {
+ }
+ }
- private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+ private static final String DEX_EXTRA = new File(
+ System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath();
- // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
- private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
- private static native void init();
+ private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
- /**
- * base64 encoded class/dex file for
- *
- * public class Test999 {
- * public void foo() {
- * System.out.println("Goodbye");
- * }
- *
- * public int bar = 64;
- * }
- */
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" +
- "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" +
- "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" +
- "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" +
- "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" +
- "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" +
- "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
- "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQDlfmgFfKulToQpDF+P4dsgeOkgfzzH+5lgAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAQ" +
- "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAsAgAANAEAAIYB" +
- "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
- "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
- "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
- "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
- "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
- "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
- "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
- "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
- "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
- "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
- "AQHUAgAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADM" +
- "AAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIA" +
- "AAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAAAAEAAACwAgAAABAA" +
- "AAEAAAC0AgAA");
+ // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void init();
+
+ /**
+ * base64 encoded class/dex file for
+ *
+ * public class Test999 {
+ * public void foo() {
+ * System.out.println("Goodbye");
+ * }
+ *
+ * public int bar = 64;
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" +
+ "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" +
+ "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" +
+ "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" +
+ "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" +
+ "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" +
+ "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
+ "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDlfmgFfKulToQpDF+P4dsgeOkgfzzH+5lgAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAQ" +
+ "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAsAgAANAEAAIYB" +
+ "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
+ "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
+ "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
+ "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+ "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
+ "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
+ "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
+ "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
+ "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
+ "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
+ "AQHUAgAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADM" +
+ "AAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIA" +
+ "AAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAAAAEAAACwAgAAABAA" +
+ "AAEAAAC0AgAA");
}
diff --git a/test/Android.bp b/test/Android.bp
index 4289c18..c5322cb 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -58,7 +58,11 @@
cflags: ["-Wno-frame-larger-than="],
},
host: {
- cflags: ["-Wno-frame-larger-than="],
+ cflags: [
+ "-Wno-frame-larger-than=",
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
},
},
}
@@ -104,10 +108,10 @@
],
}
-// Variant of art_test_defaults that installs the library in a location which
-// will be added to the default namespace, and hence also the com_android_art
-// namespace. That allows the library to have shared_libs dependencies on all
-// ART internal libraries.
+// Variant of art_test_defaults for test libraries that installs them in a
+// location which will be added to the default namespace, and hence also the
+// com_android_art namespace. That allows them to have shared_libs
+// dependencies on all ART internal libraries.
//
// Currently this only works for run tests where run-test-jar sets
// LD_LIBRARY_PATH and NATIVELOADER_DEFAULT_NAMESPACE_LIBS.
@@ -170,6 +174,21 @@
// eventually.
host_supported: false,
test_config_template: ":art-gtests-target-standalone-template",
+
+ // Support multilib variants (using different suffix per sub-architecture),
+ // which is needed on build targets with secondary architectures, as the
+ // CTS/MTS/etc test suite packaging logic flattens all test artifacts into a
+ // single `testcases` directory. Also, there is CI testing that expects
+ // 64-bit multilib test suites to work for 32-bit devices (b/233550842).
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
}
// Properties common to `art_gtest_defaults` and `art_standalone_gtest_defaults`.
@@ -205,6 +224,10 @@
shared_libs: [
"libziparchive",
],
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
},
},
}
@@ -328,6 +351,12 @@
"libgtest_isolated",
],
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
android32: {
cflags: ["-DART_TARGET_NATIVETEST_DIR=/data/nativetest/art"],
},
@@ -399,10 +428,15 @@
],
shared_libs: [
"libbase",
- "libbacktrace",
"liblog",
],
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
darwin: {
enabled: false,
},
@@ -581,7 +615,6 @@
name: "libartagent-defaults",
defaults: ["art_test_internal_library_defaults"],
shared_libs: [
- "libbacktrace",
"libbase",
"liblog",
"libnativehelper",
@@ -671,6 +704,7 @@
"991-field-trace-2/field_trace.cc",
"992-source-data/source_file.cc",
"993-breakpoints/breakpoints.cc",
+ "993-breakpoints-non-debuggable/onload.cc",
"996-breakpoint-obsolete/obsolete_breakpoints.cc",
"1900-track-alloc/alloc.cc",
"1901-get-bytecodes/bytecodes.cc",
@@ -689,6 +723,7 @@
"1932-monitor-events-misc/monitor_misc.cc",
"1934-jvmti-signal-thread/signal_threads.cc",
"1939-proxy-frames/local_instance.cc",
+ "1940-ddms-ext/ddm_ext.cc",
"1941-dispose-stress/dispose_stress.cc",
"1942-suspend-raw-monitor-exit/native_suspend_monitor.cc",
"1943-suspend-raw-monitor-wait/native_suspend_monitor.cc",
@@ -736,7 +771,7 @@
"936-search-onload/search_onload.cc",
"980-redefine-object/redef_object.cc",
"983-source-transform-verify/source_transform_art.cc",
- "1940-ddms-ext/ddm_ext.cc",
+ "993-breakpoints-non-debuggable/native_attach_agent.cc",
// "1952-pop-frame-jit/pop_frame.cc",
"1959-redefine-object-instrument/fake_redef_object.cc",
"1960-obsolete-jit-multithread-native/native_say_hi.cc",
@@ -933,6 +968,7 @@
"800-smali/jni.cc",
"817-hiddenapi/test_native.cc",
"909-attach-agent/disallow_debugging.cc",
+ "993-breakpoints-non-debuggable/native_attach_agent.cc",
"1001-app-image-regions/app_image_regions.cc",
"1002-notify-startup/startup_interface.cc",
"1947-breakpoint-redefine-deopt/check_deopt.cc",
@@ -949,10 +985,10 @@
"common/stack_inspect.cc",
],
shared_libs: [
- "libbacktrace",
"libbase",
"liblog",
"libnativehelper",
+ "libunwindstack",
],
}
@@ -1037,6 +1073,7 @@
"992-source-data/src/art/Test992.java",
"992-source-data/src/art/Target2.java",
"993-breakpoints/src/art/Test993.java",
+ "993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java",
"994-breakpoint-line/src/art/Test994.java",
"995-breakpoints-throw/src/art/Test995.java",
"996-breakpoint-obsolete/src/art/Test996.java",
@@ -1075,6 +1112,7 @@
"1936-thread-end-events/src/art/Test1936.java",
"1937-transform-soft-fail/src/art/Test1937.java",
"1939-proxy-frames/src/art/Test1939.java",
+ "1940-ddms-ext/src-art/art/Test1940.java",
"1941-dispose-stress/src/art/Test1941.java",
"1942-suspend-raw-monitor-exit/src/art/Test1942.java",
"1943-suspend-raw-monitor-wait/src/art/Test1943.java",
@@ -1184,6 +1222,7 @@
"992-source-data/expected-stdout.txt",
// Need to avoid using hidden-apis
"993-breakpoints/expected_cts.txt",
+ "993-breakpoints-non-debuggable/expected_cts.txt",
"994-breakpoint-line/expected-stdout.txt",
"995-breakpoints-throw/expected-stdout.txt",
"996-breakpoint-obsolete/expected-stdout.txt",
@@ -1222,6 +1261,7 @@
"1936-thread-end-events/expected-stdout.txt",
"1937-transform-soft-fail/expected-stdout.txt",
"1939-proxy-frames/expected-stdout.txt",
+ "1940-ddms-ext/expected-stdout.txt",
"1941-dispose-stress/expected-stdout.txt",
"1942-suspend-raw-monitor-exit/expected-stdout.txt",
"1943-suspend-raw-monitor-wait/expected-stdout.txt",
@@ -1353,33 +1393,6 @@
test_config_template: "csuite-app-compile-launch.xml",
}
-// Install run-test data in the output directory.
-prebuilt_etc_host {
- name: "art-run-test-host-data",
- defaults: ["art_module_source_build_prebuilt_defaults"],
- src: ":art-run-test-host-data-merged",
- sub_dir: "art",
- filename: "art-run-test-host-data.zip",
-}
-
-// Install run-test data in the output directory.
-prebuilt_etc_host {
- name: "art-run-test-jvm-data",
- defaults: ["art_module_source_build_prebuilt_defaults"],
- src: ":art-run-test-jvm-data-merged",
- sub_dir: "art",
- filename: "art-run-test-jvm-data.zip",
-}
-
-// Install run-test data in the output directory.
-prebuilt_etc_host {
- name: "art-run-test-target-data",
- defaults: ["art_module_source_build_prebuilt_defaults"],
- src: ":art-run-test-target-data-merged",
- sub_dir: "art",
- filename: "art-run-test-target-data.zip",
-}
-
filegroup {
name: "art-gtest-jars",
srcs: [
@@ -1873,11 +1886,7 @@
"art_module_source_build_genrule_defaults",
],
tool_files: [
- "run-test-build.py",
- "buildfailures.json",
- "etc/default-build",
- "etc/default-run",
- "etc/default-check",
+ "run_test_build.py",
":art-run-test-bootclasspath",
],
tools: [
diff --git a/test/Android.run-test.bp b/test/Android.run-test.bp
index 27666f8..b5fed42 100644
--- a/test/Android.run-test.bp
+++ b/test/Android.run-test.bp
@@ -2,3031 +2,6301 @@
// It is not necessary to regenerate it when tests are added/removed/modified.
java_genrule {
- name: "art-run-test-host-data-shard00",
+ name: "art-run-test-host-data-shard00-tmp",
out: ["art-run-test-host-data-shard00.zip"],
- srcs: ["*00-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 00 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?00-*/**/*", "??00-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard00",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard00-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard00.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard01",
+ name: "art-run-test-host-data-shard01-tmp",
out: ["art-run-test-host-data-shard01.zip"],
- srcs: ["*01-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 01 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?01-*/**/*", "??01-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard01",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard01-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard01.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard02",
+ name: "art-run-test-host-data-shard02-tmp",
out: ["art-run-test-host-data-shard02.zip"],
- srcs: ["*02-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 02 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?02-*/**/*", "??02-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard02",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard02-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard02.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard03",
+ name: "art-run-test-host-data-shard03-tmp",
out: ["art-run-test-host-data-shard03.zip"],
- srcs: ["*03-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 03 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?03-*/**/*", "??03-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard03",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard03-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard03.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard04",
+ name: "art-run-test-host-data-shard04-tmp",
out: ["art-run-test-host-data-shard04.zip"],
- srcs: ["*04-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 04 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?04-*/**/*", "??04-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard04",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard04-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard04.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard05",
+ name: "art-run-test-host-data-shard05-tmp",
out: ["art-run-test-host-data-shard05.zip"],
- srcs: ["*05-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 05 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?05-*/**/*", "??05-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard05",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard05-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard05.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard06",
+ name: "art-run-test-host-data-shard06-tmp",
out: ["art-run-test-host-data-shard06.zip"],
- srcs: ["*06-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 06 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?06-*/**/*", "??06-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard06",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard06-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard06.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard07",
+ name: "art-run-test-host-data-shard07-tmp",
out: ["art-run-test-host-data-shard07.zip"],
- srcs: ["*07-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 07 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?07-*/**/*", "??07-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard07",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard07-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard07.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard08",
+ name: "art-run-test-host-data-shard08-tmp",
out: ["art-run-test-host-data-shard08.zip"],
- srcs: ["*08-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 08 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?08-*/**/*", "??08-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard08",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard08-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard08.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard09",
+ name: "art-run-test-host-data-shard09-tmp",
out: ["art-run-test-host-data-shard09.zip"],
- srcs: ["*09-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 09 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?09-*/**/*", "??09-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard09",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard09-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard09.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard10",
+ name: "art-run-test-host-data-shard10-tmp",
out: ["art-run-test-host-data-shard10.zip"],
- srcs: ["*10-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 10 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?10-*/**/*", "??10-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard10",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard10-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard10.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard11",
+ name: "art-run-test-host-data-shard11-tmp",
out: ["art-run-test-host-data-shard11.zip"],
- srcs: ["*11-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 11 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?11-*/**/*", "??11-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard11",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard11-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard11.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard12",
+ name: "art-run-test-host-data-shard12-tmp",
out: ["art-run-test-host-data-shard12.zip"],
- srcs: ["*12-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 12 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?12-*/**/*", "??12-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard12",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard12-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard12.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard13",
+ name: "art-run-test-host-data-shard13-tmp",
out: ["art-run-test-host-data-shard13.zip"],
- srcs: ["*13-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 13 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?13-*/**/*", "??13-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard13",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard13-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard13.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard14",
+ name: "art-run-test-host-data-shard14-tmp",
out: ["art-run-test-host-data-shard14.zip"],
- srcs: ["*14-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 14 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?14-*/**/*", "??14-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard14",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard14-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard14.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard15",
+ name: "art-run-test-host-data-shard15-tmp",
out: ["art-run-test-host-data-shard15.zip"],
- srcs: ["*15-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 15 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?15-*/**/*", "??15-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard15",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard15-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard15.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard16",
+ name: "art-run-test-host-data-shard16-tmp",
out: ["art-run-test-host-data-shard16.zip"],
- srcs: ["*16-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 16 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?16-*/**/*", "??16-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard16",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard16-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard16.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard17",
+ name: "art-run-test-host-data-shard17-tmp",
out: ["art-run-test-host-data-shard17.zip"],
- srcs: ["*17-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 17 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?17-*/**/*", "??17-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard17",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard17-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard17.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard18",
+ name: "art-run-test-host-data-shard18-tmp",
out: ["art-run-test-host-data-shard18.zip"],
- srcs: ["*18-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 18 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?18-*/**/*", "??18-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard18",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard18-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard18.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard19",
+ name: "art-run-test-host-data-shard19-tmp",
out: ["art-run-test-host-data-shard19.zip"],
- srcs: ["*19-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 19 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?19-*/**/*", "??19-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard19",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard19-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard19.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard20",
+ name: "art-run-test-host-data-shard20-tmp",
out: ["art-run-test-host-data-shard20.zip"],
- srcs: ["*20-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 20 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?20-*/**/*", "??20-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard20",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard20-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard20.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard21",
+ name: "art-run-test-host-data-shard21-tmp",
out: ["art-run-test-host-data-shard21.zip"],
- srcs: ["*21-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 21 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?21-*/**/*", "??21-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard21",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard21-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard21.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard22",
+ name: "art-run-test-host-data-shard22-tmp",
out: ["art-run-test-host-data-shard22.zip"],
- srcs: ["*22-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 22 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?22-*/**/*", "??22-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard22",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard22-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard22.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard23",
+ name: "art-run-test-host-data-shard23-tmp",
out: ["art-run-test-host-data-shard23.zip"],
- srcs: ["*23-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 23 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?23-*/**/*", "??23-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard23",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard23-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard23.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard24",
+ name: "art-run-test-host-data-shard24-tmp",
out: ["art-run-test-host-data-shard24.zip"],
- srcs: ["*24-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 24 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?24-*/**/*", "??24-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard24",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard24-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard24.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard25",
+ name: "art-run-test-host-data-shard25-tmp",
out: ["art-run-test-host-data-shard25.zip"],
- srcs: ["*25-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 25 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?25-*/**/*", "??25-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard25",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard25-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard25.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard26",
+ name: "art-run-test-host-data-shard26-tmp",
out: ["art-run-test-host-data-shard26.zip"],
- srcs: ["*26-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 26 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?26-*/**/*", "??26-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard26",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard26-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard26.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard27",
+ name: "art-run-test-host-data-shard27-tmp",
out: ["art-run-test-host-data-shard27.zip"],
- srcs: ["*27-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 27 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?27-*/**/*", "??27-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard27",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard27-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard27.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard28",
+ name: "art-run-test-host-data-shard28-tmp",
out: ["art-run-test-host-data-shard28.zip"],
- srcs: ["*28-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 28 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?28-*/**/*", "??28-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard28",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard28-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard28.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard29",
+ name: "art-run-test-host-data-shard29-tmp",
out: ["art-run-test-host-data-shard29.zip"],
- srcs: ["*29-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 29 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?29-*/**/*", "??29-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard29",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard29-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard29.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard30",
+ name: "art-run-test-host-data-shard30-tmp",
out: ["art-run-test-host-data-shard30.zip"],
- srcs: ["*30-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 30 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?30-*/**/*", "??30-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard30",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard30-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard30.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard31",
+ name: "art-run-test-host-data-shard31-tmp",
out: ["art-run-test-host-data-shard31.zip"],
- srcs: ["*31-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 31 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?31-*/**/*", "??31-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard31",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard31-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard31.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard32",
+ name: "art-run-test-host-data-shard32-tmp",
out: ["art-run-test-host-data-shard32.zip"],
- srcs: ["*32-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 32 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?32-*/**/*", "??32-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard32",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard32-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard32.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard33",
+ name: "art-run-test-host-data-shard33-tmp",
out: ["art-run-test-host-data-shard33.zip"],
- srcs: ["*33-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 33 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?33-*/**/*", "??33-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard33",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard33-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard33.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard34",
+ name: "art-run-test-host-data-shard34-tmp",
out: ["art-run-test-host-data-shard34.zip"],
- srcs: ["*34-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 34 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?34-*/**/*", "??34-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard34",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard34-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard34.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard35",
+ name: "art-run-test-host-data-shard35-tmp",
out: ["art-run-test-host-data-shard35.zip"],
- srcs: ["*35-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 35 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?35-*/**/*", "??35-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard35",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard35-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard35.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard36",
+ name: "art-run-test-host-data-shard36-tmp",
out: ["art-run-test-host-data-shard36.zip"],
- srcs: ["*36-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 36 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?36-*/**/*", "??36-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard36",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard36-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard36.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard37",
+ name: "art-run-test-host-data-shard37-tmp",
out: ["art-run-test-host-data-shard37.zip"],
- srcs: ["*37-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 37 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?37-*/**/*", "??37-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard37",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard37-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard37.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard38",
+ name: "art-run-test-host-data-shard38-tmp",
out: ["art-run-test-host-data-shard38.zip"],
- srcs: ["*38-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 38 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?38-*/**/*", "??38-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard38",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard38-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard38.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard39",
+ name: "art-run-test-host-data-shard39-tmp",
out: ["art-run-test-host-data-shard39.zip"],
- srcs: ["*39-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 39 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?39-*/**/*", "??39-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard39",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard39-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard39.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard40",
+ name: "art-run-test-host-data-shard40-tmp",
out: ["art-run-test-host-data-shard40.zip"],
- srcs: ["*40-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 40 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?40-*/**/*", "??40-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard40",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard40-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard40.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard41",
+ name: "art-run-test-host-data-shard41-tmp",
out: ["art-run-test-host-data-shard41.zip"],
- srcs: ["*41-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 41 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?41-*/**/*", "??41-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard41",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard41-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard41.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard42",
+ name: "art-run-test-host-data-shard42-tmp",
out: ["art-run-test-host-data-shard42.zip"],
- srcs: ["*42-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 42 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?42-*/**/*", "??42-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard42",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard42-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard42.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard43",
+ name: "art-run-test-host-data-shard43-tmp",
out: ["art-run-test-host-data-shard43.zip"],
- srcs: ["*43-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 43 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?43-*/**/*", "??43-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard43",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard43-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard43.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard44",
+ name: "art-run-test-host-data-shard44-tmp",
out: ["art-run-test-host-data-shard44.zip"],
- srcs: ["*44-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 44 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?44-*/**/*", "??44-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard44",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard44-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard44.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard45",
+ name: "art-run-test-host-data-shard45-tmp",
out: ["art-run-test-host-data-shard45.zip"],
- srcs: ["*45-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 45 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?45-*/**/*", "??45-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard45",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard45-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard45.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard46",
+ name: "art-run-test-host-data-shard46-tmp",
out: ["art-run-test-host-data-shard46.zip"],
- srcs: ["*46-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 46 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?46-*/**/*", "??46-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard46",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard46-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard46.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard47",
+ name: "art-run-test-host-data-shard47-tmp",
out: ["art-run-test-host-data-shard47.zip"],
- srcs: ["*47-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 47 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?47-*/**/*", "??47-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard47",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard47-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard47.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard48",
+ name: "art-run-test-host-data-shard48-tmp",
out: ["art-run-test-host-data-shard48.zip"],
- srcs: ["*48-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 48 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?48-*/**/*", "??48-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard48",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard48-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard48.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard49",
+ name: "art-run-test-host-data-shard49-tmp",
out: ["art-run-test-host-data-shard49.zip"],
- srcs: ["*49-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 49 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?49-*/**/*", "??49-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard49",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard49-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard49.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard50",
+ name: "art-run-test-host-data-shard50-tmp",
out: ["art-run-test-host-data-shard50.zip"],
- srcs: ["*50-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 50 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?50-*/**/*", "??50-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard50",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard50-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard50.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard51",
+ name: "art-run-test-host-data-shard51-tmp",
out: ["art-run-test-host-data-shard51.zip"],
- srcs: ["*51-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 51 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?51-*/**/*", "??51-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard51",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard51-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard51.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard52",
+ name: "art-run-test-host-data-shard52-tmp",
out: ["art-run-test-host-data-shard52.zip"],
- srcs: ["*52-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 52 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?52-*/**/*", "??52-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard52",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard52-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard52.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard53",
+ name: "art-run-test-host-data-shard53-tmp",
out: ["art-run-test-host-data-shard53.zip"],
- srcs: ["*53-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 53 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?53-*/**/*", "??53-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard53",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard53-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard53.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard54",
+ name: "art-run-test-host-data-shard54-tmp",
out: ["art-run-test-host-data-shard54.zip"],
- srcs: ["*54-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 54 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?54-*/**/*", "??54-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard54",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard54-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard54.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard55",
+ name: "art-run-test-host-data-shard55-tmp",
out: ["art-run-test-host-data-shard55.zip"],
- srcs: ["*55-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 55 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?55-*/**/*", "??55-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard55",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard55-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard55.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard56",
+ name: "art-run-test-host-data-shard56-tmp",
out: ["art-run-test-host-data-shard56.zip"],
- srcs: ["*56-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 56 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?56-*/**/*", "??56-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard56",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard56-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard56.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard57",
+ name: "art-run-test-host-data-shard57-tmp",
out: ["art-run-test-host-data-shard57.zip"],
- srcs: ["*57-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 57 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?57-*/**/*", "??57-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard57",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard57-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard57.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard58",
+ name: "art-run-test-host-data-shard58-tmp",
out: ["art-run-test-host-data-shard58.zip"],
- srcs: ["*58-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 58 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?58-*/**/*", "??58-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard58",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard58-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard58.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard59",
+ name: "art-run-test-host-data-shard59-tmp",
out: ["art-run-test-host-data-shard59.zip"],
- srcs: ["*59-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 59 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?59-*/**/*", "??59-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard59",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard59-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard59.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard60",
+ name: "art-run-test-host-data-shard60-tmp",
out: ["art-run-test-host-data-shard60.zip"],
- srcs: ["*60-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 60 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?60-*/**/*", "??60-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard60",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard60-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard60.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard61",
+ name: "art-run-test-host-data-shard61-tmp",
out: ["art-run-test-host-data-shard61.zip"],
- srcs: ["*61-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 61 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?61-*/**/*", "??61-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard61",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard61-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard61.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard62",
+ name: "art-run-test-host-data-shard62-tmp",
out: ["art-run-test-host-data-shard62.zip"],
- srcs: ["*62-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 62 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?62-*/**/*", "??62-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard62",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard62-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard62.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard63",
+ name: "art-run-test-host-data-shard63-tmp",
out: ["art-run-test-host-data-shard63.zip"],
- srcs: ["*63-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 63 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?63-*/**/*", "??63-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard63",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard63-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard63.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard64",
+ name: "art-run-test-host-data-shard64-tmp",
out: ["art-run-test-host-data-shard64.zip"],
- srcs: ["*64-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 64 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?64-*/**/*", "??64-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard64",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard64-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard64.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard65",
+ name: "art-run-test-host-data-shard65-tmp",
out: ["art-run-test-host-data-shard65.zip"],
- srcs: ["*65-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 65 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?65-*/**/*", "??65-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard65",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard65-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard65.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard66",
+ name: "art-run-test-host-data-shard66-tmp",
out: ["art-run-test-host-data-shard66.zip"],
- srcs: ["*66-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 66 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?66-*/**/*", "??66-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard66",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard66-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard66.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard67",
+ name: "art-run-test-host-data-shard67-tmp",
out: ["art-run-test-host-data-shard67.zip"],
- srcs: ["*67-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 67 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?67-*/**/*", "??67-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard67",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard67-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard67.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard68",
+ name: "art-run-test-host-data-shard68-tmp",
out: ["art-run-test-host-data-shard68.zip"],
- srcs: ["*68-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 68 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?68-*/**/*", "??68-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard68",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard68-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard68.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard69",
+ name: "art-run-test-host-data-shard69-tmp",
out: ["art-run-test-host-data-shard69.zip"],
- srcs: ["*69-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 69 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?69-*/**/*", "??69-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard69",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard69-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard69.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard70",
+ name: "art-run-test-host-data-shard70-tmp",
out: ["art-run-test-host-data-shard70.zip"],
- srcs: ["*70-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 70 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?70-*/**/*", "??70-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard70",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard70-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard70.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard71",
+ name: "art-run-test-host-data-shard71-tmp",
out: ["art-run-test-host-data-shard71.zip"],
- srcs: ["*71-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 71 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?71-*/**/*", "??71-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard71",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard71-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard71.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard72",
+ name: "art-run-test-host-data-shard72-tmp",
out: ["art-run-test-host-data-shard72.zip"],
- srcs: ["*72-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 72 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?72-*/**/*", "??72-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard72",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard72-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard72.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard73",
+ name: "art-run-test-host-data-shard73-tmp",
out: ["art-run-test-host-data-shard73.zip"],
- srcs: ["*73-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 73 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?73-*/**/*", "??73-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard73",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard73-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard73.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard74",
+ name: "art-run-test-host-data-shard74-tmp",
out: ["art-run-test-host-data-shard74.zip"],
- srcs: ["*74-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 74 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?74-*/**/*", "??74-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard74",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard74-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard74.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard75",
+ name: "art-run-test-host-data-shard75-tmp",
out: ["art-run-test-host-data-shard75.zip"],
- srcs: ["*75-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 75 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?75-*/**/*", "??75-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard75",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard75-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard75.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard76",
+ name: "art-run-test-host-data-shard76-tmp",
out: ["art-run-test-host-data-shard76.zip"],
- srcs: ["*76-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 76 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?76-*/**/*", "??76-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard76",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard76-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard76.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard77",
+ name: "art-run-test-host-data-shard77-tmp",
out: ["art-run-test-host-data-shard77.zip"],
- srcs: ["*77-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 77 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?77-*/**/*", "??77-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard77",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard77-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard77.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard78",
+ name: "art-run-test-host-data-shard78-tmp",
out: ["art-run-test-host-data-shard78.zip"],
- srcs: ["*78-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 78 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?78-*/**/*", "??78-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard78",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard78-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard78.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard79",
+ name: "art-run-test-host-data-shard79-tmp",
out: ["art-run-test-host-data-shard79.zip"],
- srcs: ["*79-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 79 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?79-*/**/*", "??79-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard79",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard79-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard79.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard80",
+ name: "art-run-test-host-data-shard80-tmp",
out: ["art-run-test-host-data-shard80.zip"],
- srcs: ["*80-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 80 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?80-*/**/*", "??80-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard80",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard80-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard80.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard81",
+ name: "art-run-test-host-data-shard81-tmp",
out: ["art-run-test-host-data-shard81.zip"],
- srcs: ["*81-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 81 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?81-*/**/*", "??81-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard81",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard81-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard81.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard82",
+ name: "art-run-test-host-data-shard82-tmp",
out: ["art-run-test-host-data-shard82.zip"],
- srcs: ["*82-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 82 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?82-*/**/*", "??82-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard82",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard82-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard82.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard83",
+ name: "art-run-test-host-data-shard83-tmp",
out: ["art-run-test-host-data-shard83.zip"],
- srcs: ["*83-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 83 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?83-*/**/*", "??83-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard83",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard83-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard83.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard84",
+ name: "art-run-test-host-data-shard84-tmp",
out: ["art-run-test-host-data-shard84.zip"],
- srcs: ["*84-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 84 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?84-*/**/*", "??84-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard84",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard84-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard84.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard85",
+ name: "art-run-test-host-data-shard85-tmp",
out: ["art-run-test-host-data-shard85.zip"],
- srcs: ["*85-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 85 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?85-*/**/*", "??85-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard85",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard85-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard85.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard86",
+ name: "art-run-test-host-data-shard86-tmp",
out: ["art-run-test-host-data-shard86.zip"],
- srcs: ["*86-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 86 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?86-*/**/*", "??86-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard86",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard86-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard86.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard87",
+ name: "art-run-test-host-data-shard87-tmp",
out: ["art-run-test-host-data-shard87.zip"],
- srcs: ["*87-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 87 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?87-*/**/*", "??87-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard87",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard87-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard87.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard88",
+ name: "art-run-test-host-data-shard88-tmp",
out: ["art-run-test-host-data-shard88.zip"],
- srcs: ["*88-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 88 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?88-*/**/*", "??88-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard88",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard88-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard88.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard89",
+ name: "art-run-test-host-data-shard89-tmp",
out: ["art-run-test-host-data-shard89.zip"],
- srcs: ["*89-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 89 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?89-*/**/*", "??89-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard89",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard89-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard89.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard90",
+ name: "art-run-test-host-data-shard90-tmp",
out: ["art-run-test-host-data-shard90.zip"],
- srcs: ["*90-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 90 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?90-*/**/*", "??90-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard90",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard90-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard90.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard91",
+ name: "art-run-test-host-data-shard91-tmp",
out: ["art-run-test-host-data-shard91.zip"],
- srcs: ["*91-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 91 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?91-*/**/*", "??91-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard91",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard91-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard91.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard92",
+ name: "art-run-test-host-data-shard92-tmp",
out: ["art-run-test-host-data-shard92.zip"],
- srcs: ["*92-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 92 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?92-*/**/*", "??92-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard92",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard92-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard92.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard93",
+ name: "art-run-test-host-data-shard93-tmp",
out: ["art-run-test-host-data-shard93.zip"],
- srcs: ["*93-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 93 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?93-*/**/*", "??93-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard93",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard93-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard93.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard94",
+ name: "art-run-test-host-data-shard94-tmp",
out: ["art-run-test-host-data-shard94.zip"],
- srcs: ["*94-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 94 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?94-*/**/*", "??94-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard94",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard94-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard94.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard95",
+ name: "art-run-test-host-data-shard95-tmp",
out: ["art-run-test-host-data-shard95.zip"],
- srcs: ["*95-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 95 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?95-*/**/*", "??95-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard95",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard95-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard95.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard96",
+ name: "art-run-test-host-data-shard96-tmp",
out: ["art-run-test-host-data-shard96.zip"],
- srcs: ["*96-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 96 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?96-*/**/*", "??96-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard96",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard96-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard96.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard97",
+ name: "art-run-test-host-data-shard97-tmp",
out: ["art-run-test-host-data-shard97.zip"],
- srcs: ["*97-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 97 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?97-*/**/*", "??97-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard97",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard97-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard97.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard98",
+ name: "art-run-test-host-data-shard98-tmp",
out: ["art-run-test-host-data-shard98.zip"],
- srcs: ["*98-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 98 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?98-*/**/*", "??98-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard98",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard98-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard98.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard99",
+ name: "art-run-test-host-data-shard99-tmp",
out: ["art-run-test-host-data-shard99.zip"],
- srcs: ["*99-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 99 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?99-*/**/*", "??99-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard99",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard99-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard99.zip",
}
java_genrule {
- name: "art-run-test-host-data-merged",
- defaults: ["art-run-test-data-defaults"],
+ name: "art-run-test-host-data-shardHiddenApi-tmp",
+ out: ["art-run-test-host-data-shardHiddenApi.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode host " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shardHiddenApi",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shardHiddenApi-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shardHiddenApi.zip",
+}
+
+genrule_defaults {
+ name: "art-run-test-host-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode host " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+java_genrule {
+ name: "art-run-test-host-data-merged-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
out: ["art-run-test-host-data-merged.zip"],
srcs: [
- ":art-run-test-host-data-shard00",
- ":art-run-test-host-data-shard01",
- ":art-run-test-host-data-shard02",
- ":art-run-test-host-data-shard03",
- ":art-run-test-host-data-shard04",
- ":art-run-test-host-data-shard05",
- ":art-run-test-host-data-shard06",
- ":art-run-test-host-data-shard07",
- ":art-run-test-host-data-shard08",
- ":art-run-test-host-data-shard09",
- ":art-run-test-host-data-shard10",
- ":art-run-test-host-data-shard11",
- ":art-run-test-host-data-shard12",
- ":art-run-test-host-data-shard13",
- ":art-run-test-host-data-shard14",
- ":art-run-test-host-data-shard15",
- ":art-run-test-host-data-shard16",
- ":art-run-test-host-data-shard17",
- ":art-run-test-host-data-shard18",
- ":art-run-test-host-data-shard19",
- ":art-run-test-host-data-shard20",
- ":art-run-test-host-data-shard21",
- ":art-run-test-host-data-shard22",
- ":art-run-test-host-data-shard23",
- ":art-run-test-host-data-shard24",
- ":art-run-test-host-data-shard25",
- ":art-run-test-host-data-shard26",
- ":art-run-test-host-data-shard27",
- ":art-run-test-host-data-shard28",
- ":art-run-test-host-data-shard29",
- ":art-run-test-host-data-shard30",
- ":art-run-test-host-data-shard31",
- ":art-run-test-host-data-shard32",
- ":art-run-test-host-data-shard33",
- ":art-run-test-host-data-shard34",
- ":art-run-test-host-data-shard35",
- ":art-run-test-host-data-shard36",
- ":art-run-test-host-data-shard37",
- ":art-run-test-host-data-shard38",
- ":art-run-test-host-data-shard39",
- ":art-run-test-host-data-shard40",
- ":art-run-test-host-data-shard41",
- ":art-run-test-host-data-shard42",
- ":art-run-test-host-data-shard43",
- ":art-run-test-host-data-shard44",
- ":art-run-test-host-data-shard45",
- ":art-run-test-host-data-shard46",
- ":art-run-test-host-data-shard47",
- ":art-run-test-host-data-shard48",
- ":art-run-test-host-data-shard49",
- ":art-run-test-host-data-shard50",
- ":art-run-test-host-data-shard51",
- ":art-run-test-host-data-shard52",
- ":art-run-test-host-data-shard53",
- ":art-run-test-host-data-shard54",
- ":art-run-test-host-data-shard55",
- ":art-run-test-host-data-shard56",
- ":art-run-test-host-data-shard57",
- ":art-run-test-host-data-shard58",
- ":art-run-test-host-data-shard59",
- ":art-run-test-host-data-shard60",
- ":art-run-test-host-data-shard61",
- ":art-run-test-host-data-shard62",
- ":art-run-test-host-data-shard63",
- ":art-run-test-host-data-shard64",
- ":art-run-test-host-data-shard65",
- ":art-run-test-host-data-shard66",
- ":art-run-test-host-data-shard67",
- ":art-run-test-host-data-shard68",
- ":art-run-test-host-data-shard69",
- ":art-run-test-host-data-shard70",
- ":art-run-test-host-data-shard71",
- ":art-run-test-host-data-shard72",
- ":art-run-test-host-data-shard73",
- ":art-run-test-host-data-shard74",
- ":art-run-test-host-data-shard75",
- ":art-run-test-host-data-shard76",
- ":art-run-test-host-data-shard77",
- ":art-run-test-host-data-shard78",
- ":art-run-test-host-data-shard79",
- ":art-run-test-host-data-shard80",
- ":art-run-test-host-data-shard81",
- ":art-run-test-host-data-shard82",
- ":art-run-test-host-data-shard83",
- ":art-run-test-host-data-shard84",
- ":art-run-test-host-data-shard85",
- ":art-run-test-host-data-shard86",
- ":art-run-test-host-data-shard87",
- ":art-run-test-host-data-shard88",
- ":art-run-test-host-data-shard89",
- ":art-run-test-host-data-shard90",
- ":art-run-test-host-data-shard91",
- ":art-run-test-host-data-shard92",
- ":art-run-test-host-data-shard93",
- ":art-run-test-host-data-shard94",
- ":art-run-test-host-data-shard95",
- ":art-run-test-host-data-shard96",
- ":art-run-test-host-data-shard97",
- ":art-run-test-host-data-shard98",
- ":art-run-test-host-data-shard99",
+ ":art-run-test-host-data-shard00-tmp",
+ ":art-run-test-host-data-shard01-tmp",
+ ":art-run-test-host-data-shard02-tmp",
+ ":art-run-test-host-data-shard03-tmp",
+ ":art-run-test-host-data-shard04-tmp",
+ ":art-run-test-host-data-shard05-tmp",
+ ":art-run-test-host-data-shard06-tmp",
+ ":art-run-test-host-data-shard07-tmp",
+ ":art-run-test-host-data-shard08-tmp",
+ ":art-run-test-host-data-shard09-tmp",
+ ":art-run-test-host-data-shard10-tmp",
+ ":art-run-test-host-data-shard11-tmp",
+ ":art-run-test-host-data-shard12-tmp",
+ ":art-run-test-host-data-shard13-tmp",
+ ":art-run-test-host-data-shard14-tmp",
+ ":art-run-test-host-data-shard15-tmp",
+ ":art-run-test-host-data-shard16-tmp",
+ ":art-run-test-host-data-shard17-tmp",
+ ":art-run-test-host-data-shard18-tmp",
+ ":art-run-test-host-data-shard19-tmp",
+ ":art-run-test-host-data-shard20-tmp",
+ ":art-run-test-host-data-shard21-tmp",
+ ":art-run-test-host-data-shard22-tmp",
+ ":art-run-test-host-data-shard23-tmp",
+ ":art-run-test-host-data-shard24-tmp",
+ ":art-run-test-host-data-shard25-tmp",
+ ":art-run-test-host-data-shard26-tmp",
+ ":art-run-test-host-data-shard27-tmp",
+ ":art-run-test-host-data-shard28-tmp",
+ ":art-run-test-host-data-shard29-tmp",
+ ":art-run-test-host-data-shard30-tmp",
+ ":art-run-test-host-data-shard31-tmp",
+ ":art-run-test-host-data-shard32-tmp",
+ ":art-run-test-host-data-shard33-tmp",
+ ":art-run-test-host-data-shard34-tmp",
+ ":art-run-test-host-data-shard35-tmp",
+ ":art-run-test-host-data-shard36-tmp",
+ ":art-run-test-host-data-shard37-tmp",
+ ":art-run-test-host-data-shard38-tmp",
+ ":art-run-test-host-data-shard39-tmp",
+ ":art-run-test-host-data-shard40-tmp",
+ ":art-run-test-host-data-shard41-tmp",
+ ":art-run-test-host-data-shard42-tmp",
+ ":art-run-test-host-data-shard43-tmp",
+ ":art-run-test-host-data-shard44-tmp",
+ ":art-run-test-host-data-shard45-tmp",
+ ":art-run-test-host-data-shard46-tmp",
+ ":art-run-test-host-data-shard47-tmp",
+ ":art-run-test-host-data-shard48-tmp",
+ ":art-run-test-host-data-shard49-tmp",
+ ":art-run-test-host-data-shard50-tmp",
+ ":art-run-test-host-data-shard51-tmp",
+ ":art-run-test-host-data-shard52-tmp",
+ ":art-run-test-host-data-shard53-tmp",
+ ":art-run-test-host-data-shard54-tmp",
+ ":art-run-test-host-data-shard55-tmp",
+ ":art-run-test-host-data-shard56-tmp",
+ ":art-run-test-host-data-shard57-tmp",
+ ":art-run-test-host-data-shard58-tmp",
+ ":art-run-test-host-data-shard59-tmp",
+ ":art-run-test-host-data-shard60-tmp",
+ ":art-run-test-host-data-shard61-tmp",
+ ":art-run-test-host-data-shard62-tmp",
+ ":art-run-test-host-data-shard63-tmp",
+ ":art-run-test-host-data-shard64-tmp",
+ ":art-run-test-host-data-shard65-tmp",
+ ":art-run-test-host-data-shard66-tmp",
+ ":art-run-test-host-data-shard67-tmp",
+ ":art-run-test-host-data-shard68-tmp",
+ ":art-run-test-host-data-shard69-tmp",
+ ":art-run-test-host-data-shard70-tmp",
+ ":art-run-test-host-data-shard71-tmp",
+ ":art-run-test-host-data-shard72-tmp",
+ ":art-run-test-host-data-shard73-tmp",
+ ":art-run-test-host-data-shard74-tmp",
+ ":art-run-test-host-data-shard75-tmp",
+ ":art-run-test-host-data-shard76-tmp",
+ ":art-run-test-host-data-shard77-tmp",
+ ":art-run-test-host-data-shard78-tmp",
+ ":art-run-test-host-data-shard79-tmp",
+ ":art-run-test-host-data-shard80-tmp",
+ ":art-run-test-host-data-shard81-tmp",
+ ":art-run-test-host-data-shard82-tmp",
+ ":art-run-test-host-data-shard83-tmp",
+ ":art-run-test-host-data-shard84-tmp",
+ ":art-run-test-host-data-shard85-tmp",
+ ":art-run-test-host-data-shard86-tmp",
+ ":art-run-test-host-data-shard87-tmp",
+ ":art-run-test-host-data-shard88-tmp",
+ ":art-run-test-host-data-shard89-tmp",
+ ":art-run-test-host-data-shard90-tmp",
+ ":art-run-test-host-data-shard91-tmp",
+ ":art-run-test-host-data-shard92-tmp",
+ ":art-run-test-host-data-shard93-tmp",
+ ":art-run-test-host-data-shard94-tmp",
+ ":art-run-test-host-data-shard95-tmp",
+ ":art-run-test-host-data-shard96-tmp",
+ ":art-run-test-host-data-shard97-tmp",
+ ":art-run-test-host-data-shard98-tmp",
+ ":art-run-test-host-data-shard99-tmp",
+ ":art-run-test-host-data-shardHiddenApi-tmp",
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-merged",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-merged-tmp",
+ required: [
+ "art-run-test-host-data-shard00",
+ "art-run-test-host-data-shard01",
+ "art-run-test-host-data-shard02",
+ "art-run-test-host-data-shard03",
+ "art-run-test-host-data-shard04",
+ "art-run-test-host-data-shard05",
+ "art-run-test-host-data-shard06",
+ "art-run-test-host-data-shard07",
+ "art-run-test-host-data-shard08",
+ "art-run-test-host-data-shard09",
+ "art-run-test-host-data-shard10",
+ "art-run-test-host-data-shard11",
+ "art-run-test-host-data-shard12",
+ "art-run-test-host-data-shard13",
+ "art-run-test-host-data-shard14",
+ "art-run-test-host-data-shard15",
+ "art-run-test-host-data-shard16",
+ "art-run-test-host-data-shard17",
+ "art-run-test-host-data-shard18",
+ "art-run-test-host-data-shard19",
+ "art-run-test-host-data-shard20",
+ "art-run-test-host-data-shard21",
+ "art-run-test-host-data-shard22",
+ "art-run-test-host-data-shard23",
+ "art-run-test-host-data-shard24",
+ "art-run-test-host-data-shard25",
+ "art-run-test-host-data-shard26",
+ "art-run-test-host-data-shard27",
+ "art-run-test-host-data-shard28",
+ "art-run-test-host-data-shard29",
+ "art-run-test-host-data-shard30",
+ "art-run-test-host-data-shard31",
+ "art-run-test-host-data-shard32",
+ "art-run-test-host-data-shard33",
+ "art-run-test-host-data-shard34",
+ "art-run-test-host-data-shard35",
+ "art-run-test-host-data-shard36",
+ "art-run-test-host-data-shard37",
+ "art-run-test-host-data-shard38",
+ "art-run-test-host-data-shard39",
+ "art-run-test-host-data-shard40",
+ "art-run-test-host-data-shard41",
+ "art-run-test-host-data-shard42",
+ "art-run-test-host-data-shard43",
+ "art-run-test-host-data-shard44",
+ "art-run-test-host-data-shard45",
+ "art-run-test-host-data-shard46",
+ "art-run-test-host-data-shard47",
+ "art-run-test-host-data-shard48",
+ "art-run-test-host-data-shard49",
+ "art-run-test-host-data-shard50",
+ "art-run-test-host-data-shard51",
+ "art-run-test-host-data-shard52",
+ "art-run-test-host-data-shard53",
+ "art-run-test-host-data-shard54",
+ "art-run-test-host-data-shard55",
+ "art-run-test-host-data-shard56",
+ "art-run-test-host-data-shard57",
+ "art-run-test-host-data-shard58",
+ "art-run-test-host-data-shard59",
+ "art-run-test-host-data-shard60",
+ "art-run-test-host-data-shard61",
+ "art-run-test-host-data-shard62",
+ "art-run-test-host-data-shard63",
+ "art-run-test-host-data-shard64",
+ "art-run-test-host-data-shard65",
+ "art-run-test-host-data-shard66",
+ "art-run-test-host-data-shard67",
+ "art-run-test-host-data-shard68",
+ "art-run-test-host-data-shard69",
+ "art-run-test-host-data-shard70",
+ "art-run-test-host-data-shard71",
+ "art-run-test-host-data-shard72",
+ "art-run-test-host-data-shard73",
+ "art-run-test-host-data-shard74",
+ "art-run-test-host-data-shard75",
+ "art-run-test-host-data-shard76",
+ "art-run-test-host-data-shard77",
+ "art-run-test-host-data-shard78",
+ "art-run-test-host-data-shard79",
+ "art-run-test-host-data-shard80",
+ "art-run-test-host-data-shard81",
+ "art-run-test-host-data-shard82",
+ "art-run-test-host-data-shard83",
+ "art-run-test-host-data-shard84",
+ "art-run-test-host-data-shard85",
+ "art-run-test-host-data-shard86",
+ "art-run-test-host-data-shard87",
+ "art-run-test-host-data-shard88",
+ "art-run-test-host-data-shard89",
+ "art-run-test-host-data-shard90",
+ "art-run-test-host-data-shard91",
+ "art-run-test-host-data-shard92",
+ "art-run-test-host-data-shard93",
+ "art-run-test-host-data-shard94",
+ "art-run-test-host-data-shard95",
+ "art-run-test-host-data-shard96",
+ "art-run-test-host-data-shard97",
+ "art-run-test-host-data-shard98",
+ "art-run-test-host-data-shard99",
+ "art-run-test-host-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-host-data-merged.zip",
+}
+
+// Phony target used to build all shards
java_genrule {
- name: "art-run-test-target-data-shard00",
+ name: "art-run-test-host-data-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["art-run-test-host-data.txt"],
+ srcs: [
+ ":art-run-test-host-data-shard00-tmp",
+ ":art-run-test-host-data-shard01-tmp",
+ ":art-run-test-host-data-shard02-tmp",
+ ":art-run-test-host-data-shard03-tmp",
+ ":art-run-test-host-data-shard04-tmp",
+ ":art-run-test-host-data-shard05-tmp",
+ ":art-run-test-host-data-shard06-tmp",
+ ":art-run-test-host-data-shard07-tmp",
+ ":art-run-test-host-data-shard08-tmp",
+ ":art-run-test-host-data-shard09-tmp",
+ ":art-run-test-host-data-shard10-tmp",
+ ":art-run-test-host-data-shard11-tmp",
+ ":art-run-test-host-data-shard12-tmp",
+ ":art-run-test-host-data-shard13-tmp",
+ ":art-run-test-host-data-shard14-tmp",
+ ":art-run-test-host-data-shard15-tmp",
+ ":art-run-test-host-data-shard16-tmp",
+ ":art-run-test-host-data-shard17-tmp",
+ ":art-run-test-host-data-shard18-tmp",
+ ":art-run-test-host-data-shard19-tmp",
+ ":art-run-test-host-data-shard20-tmp",
+ ":art-run-test-host-data-shard21-tmp",
+ ":art-run-test-host-data-shard22-tmp",
+ ":art-run-test-host-data-shard23-tmp",
+ ":art-run-test-host-data-shard24-tmp",
+ ":art-run-test-host-data-shard25-tmp",
+ ":art-run-test-host-data-shard26-tmp",
+ ":art-run-test-host-data-shard27-tmp",
+ ":art-run-test-host-data-shard28-tmp",
+ ":art-run-test-host-data-shard29-tmp",
+ ":art-run-test-host-data-shard30-tmp",
+ ":art-run-test-host-data-shard31-tmp",
+ ":art-run-test-host-data-shard32-tmp",
+ ":art-run-test-host-data-shard33-tmp",
+ ":art-run-test-host-data-shard34-tmp",
+ ":art-run-test-host-data-shard35-tmp",
+ ":art-run-test-host-data-shard36-tmp",
+ ":art-run-test-host-data-shard37-tmp",
+ ":art-run-test-host-data-shard38-tmp",
+ ":art-run-test-host-data-shard39-tmp",
+ ":art-run-test-host-data-shard40-tmp",
+ ":art-run-test-host-data-shard41-tmp",
+ ":art-run-test-host-data-shard42-tmp",
+ ":art-run-test-host-data-shard43-tmp",
+ ":art-run-test-host-data-shard44-tmp",
+ ":art-run-test-host-data-shard45-tmp",
+ ":art-run-test-host-data-shard46-tmp",
+ ":art-run-test-host-data-shard47-tmp",
+ ":art-run-test-host-data-shard48-tmp",
+ ":art-run-test-host-data-shard49-tmp",
+ ":art-run-test-host-data-shard50-tmp",
+ ":art-run-test-host-data-shard51-tmp",
+ ":art-run-test-host-data-shard52-tmp",
+ ":art-run-test-host-data-shard53-tmp",
+ ":art-run-test-host-data-shard54-tmp",
+ ":art-run-test-host-data-shard55-tmp",
+ ":art-run-test-host-data-shard56-tmp",
+ ":art-run-test-host-data-shard57-tmp",
+ ":art-run-test-host-data-shard58-tmp",
+ ":art-run-test-host-data-shard59-tmp",
+ ":art-run-test-host-data-shard60-tmp",
+ ":art-run-test-host-data-shard61-tmp",
+ ":art-run-test-host-data-shard62-tmp",
+ ":art-run-test-host-data-shard63-tmp",
+ ":art-run-test-host-data-shard64-tmp",
+ ":art-run-test-host-data-shard65-tmp",
+ ":art-run-test-host-data-shard66-tmp",
+ ":art-run-test-host-data-shard67-tmp",
+ ":art-run-test-host-data-shard68-tmp",
+ ":art-run-test-host-data-shard69-tmp",
+ ":art-run-test-host-data-shard70-tmp",
+ ":art-run-test-host-data-shard71-tmp",
+ ":art-run-test-host-data-shard72-tmp",
+ ":art-run-test-host-data-shard73-tmp",
+ ":art-run-test-host-data-shard74-tmp",
+ ":art-run-test-host-data-shard75-tmp",
+ ":art-run-test-host-data-shard76-tmp",
+ ":art-run-test-host-data-shard77-tmp",
+ ":art-run-test-host-data-shard78-tmp",
+ ":art-run-test-host-data-shard79-tmp",
+ ":art-run-test-host-data-shard80-tmp",
+ ":art-run-test-host-data-shard81-tmp",
+ ":art-run-test-host-data-shard82-tmp",
+ ":art-run-test-host-data-shard83-tmp",
+ ":art-run-test-host-data-shard84-tmp",
+ ":art-run-test-host-data-shard85-tmp",
+ ":art-run-test-host-data-shard86-tmp",
+ ":art-run-test-host-data-shard87-tmp",
+ ":art-run-test-host-data-shard88-tmp",
+ ":art-run-test-host-data-shard89-tmp",
+ ":art-run-test-host-data-shard90-tmp",
+ ":art-run-test-host-data-shard91-tmp",
+ ":art-run-test-host-data-shard92-tmp",
+ ":art-run-test-host-data-shard93-tmp",
+ ":art-run-test-host-data-shard94-tmp",
+ ":art-run-test-host-data-shard95-tmp",
+ ":art-run-test-host-data-shard96-tmp",
+ ":art-run-test-host-data-shard97-tmp",
+ ":art-run-test-host-data-shard98-tmp",
+ ":art-run-test-host-data-shard99-tmp",
+ ":art-run-test-host-data-shardHiddenApi-tmp",
+ ],
+ cmd: "echo $(in) > $(out)",
+}
+
+// Phony target used to install all shards
+prebuilt_etc_host {
+ name: "art-run-test-host-data",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-tmp",
+ required: [
+ "art-run-test-host-data-shard00",
+ "art-run-test-host-data-shard01",
+ "art-run-test-host-data-shard02",
+ "art-run-test-host-data-shard03",
+ "art-run-test-host-data-shard04",
+ "art-run-test-host-data-shard05",
+ "art-run-test-host-data-shard06",
+ "art-run-test-host-data-shard07",
+ "art-run-test-host-data-shard08",
+ "art-run-test-host-data-shard09",
+ "art-run-test-host-data-shard10",
+ "art-run-test-host-data-shard11",
+ "art-run-test-host-data-shard12",
+ "art-run-test-host-data-shard13",
+ "art-run-test-host-data-shard14",
+ "art-run-test-host-data-shard15",
+ "art-run-test-host-data-shard16",
+ "art-run-test-host-data-shard17",
+ "art-run-test-host-data-shard18",
+ "art-run-test-host-data-shard19",
+ "art-run-test-host-data-shard20",
+ "art-run-test-host-data-shard21",
+ "art-run-test-host-data-shard22",
+ "art-run-test-host-data-shard23",
+ "art-run-test-host-data-shard24",
+ "art-run-test-host-data-shard25",
+ "art-run-test-host-data-shard26",
+ "art-run-test-host-data-shard27",
+ "art-run-test-host-data-shard28",
+ "art-run-test-host-data-shard29",
+ "art-run-test-host-data-shard30",
+ "art-run-test-host-data-shard31",
+ "art-run-test-host-data-shard32",
+ "art-run-test-host-data-shard33",
+ "art-run-test-host-data-shard34",
+ "art-run-test-host-data-shard35",
+ "art-run-test-host-data-shard36",
+ "art-run-test-host-data-shard37",
+ "art-run-test-host-data-shard38",
+ "art-run-test-host-data-shard39",
+ "art-run-test-host-data-shard40",
+ "art-run-test-host-data-shard41",
+ "art-run-test-host-data-shard42",
+ "art-run-test-host-data-shard43",
+ "art-run-test-host-data-shard44",
+ "art-run-test-host-data-shard45",
+ "art-run-test-host-data-shard46",
+ "art-run-test-host-data-shard47",
+ "art-run-test-host-data-shard48",
+ "art-run-test-host-data-shard49",
+ "art-run-test-host-data-shard50",
+ "art-run-test-host-data-shard51",
+ "art-run-test-host-data-shard52",
+ "art-run-test-host-data-shard53",
+ "art-run-test-host-data-shard54",
+ "art-run-test-host-data-shard55",
+ "art-run-test-host-data-shard56",
+ "art-run-test-host-data-shard57",
+ "art-run-test-host-data-shard58",
+ "art-run-test-host-data-shard59",
+ "art-run-test-host-data-shard60",
+ "art-run-test-host-data-shard61",
+ "art-run-test-host-data-shard62",
+ "art-run-test-host-data-shard63",
+ "art-run-test-host-data-shard64",
+ "art-run-test-host-data-shard65",
+ "art-run-test-host-data-shard66",
+ "art-run-test-host-data-shard67",
+ "art-run-test-host-data-shard68",
+ "art-run-test-host-data-shard69",
+ "art-run-test-host-data-shard70",
+ "art-run-test-host-data-shard71",
+ "art-run-test-host-data-shard72",
+ "art-run-test-host-data-shard73",
+ "art-run-test-host-data-shard74",
+ "art-run-test-host-data-shard75",
+ "art-run-test-host-data-shard76",
+ "art-run-test-host-data-shard77",
+ "art-run-test-host-data-shard78",
+ "art-run-test-host-data-shard79",
+ "art-run-test-host-data-shard80",
+ "art-run-test-host-data-shard81",
+ "art-run-test-host-data-shard82",
+ "art-run-test-host-data-shard83",
+ "art-run-test-host-data-shard84",
+ "art-run-test-host-data-shard85",
+ "art-run-test-host-data-shard86",
+ "art-run-test-host-data-shard87",
+ "art-run-test-host-data-shard88",
+ "art-run-test-host-data-shard89",
+ "art-run-test-host-data-shard90",
+ "art-run-test-host-data-shard91",
+ "art-run-test-host-data-shard92",
+ "art-run-test-host-data-shard93",
+ "art-run-test-host-data-shard94",
+ "art-run-test-host-data-shard95",
+ "art-run-test-host-data-shard96",
+ "art-run-test-host-data-shard97",
+ "art-run-test-host-data-shard98",
+ "art-run-test-host-data-shard99",
+ "art-run-test-host-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-host-data.txt",
+}
+
+java_genrule {
+ name: "art-run-test-target-data-shard00-tmp",
out: ["art-run-test-target-data-shard00.zip"],
- srcs: ["*00-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 00 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?00-*/**/*", "??00-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard00",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard00-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard00.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard01",
+ name: "art-run-test-target-data-shard01-tmp",
out: ["art-run-test-target-data-shard01.zip"],
- srcs: ["*01-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 01 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?01-*/**/*", "??01-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard01",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard01-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard01.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard02",
+ name: "art-run-test-target-data-shard02-tmp",
out: ["art-run-test-target-data-shard02.zip"],
- srcs: ["*02-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 02 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?02-*/**/*", "??02-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard02",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard02-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard02.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard03",
+ name: "art-run-test-target-data-shard03-tmp",
out: ["art-run-test-target-data-shard03.zip"],
- srcs: ["*03-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 03 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?03-*/**/*", "??03-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard03",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard03-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard03.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard04",
+ name: "art-run-test-target-data-shard04-tmp",
out: ["art-run-test-target-data-shard04.zip"],
- srcs: ["*04-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 04 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?04-*/**/*", "??04-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard04",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard04-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard04.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard05",
+ name: "art-run-test-target-data-shard05-tmp",
out: ["art-run-test-target-data-shard05.zip"],
- srcs: ["*05-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 05 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?05-*/**/*", "??05-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard05",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard05-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard05.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard06",
+ name: "art-run-test-target-data-shard06-tmp",
out: ["art-run-test-target-data-shard06.zip"],
- srcs: ["*06-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 06 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?06-*/**/*", "??06-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard06",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard06-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard06.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard07",
+ name: "art-run-test-target-data-shard07-tmp",
out: ["art-run-test-target-data-shard07.zip"],
- srcs: ["*07-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 07 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?07-*/**/*", "??07-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard07",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard07-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard07.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard08",
+ name: "art-run-test-target-data-shard08-tmp",
out: ["art-run-test-target-data-shard08.zip"],
- srcs: ["*08-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 08 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?08-*/**/*", "??08-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard08",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard08-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard08.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard09",
+ name: "art-run-test-target-data-shard09-tmp",
out: ["art-run-test-target-data-shard09.zip"],
- srcs: ["*09-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 09 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?09-*/**/*", "??09-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard09",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard09-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard09.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard10",
+ name: "art-run-test-target-data-shard10-tmp",
out: ["art-run-test-target-data-shard10.zip"],
- srcs: ["*10-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 10 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?10-*/**/*", "??10-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard10",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard10-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard10.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard11",
+ name: "art-run-test-target-data-shard11-tmp",
out: ["art-run-test-target-data-shard11.zip"],
- srcs: ["*11-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 11 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?11-*/**/*", "??11-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard11",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard11-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard11.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard12",
+ name: "art-run-test-target-data-shard12-tmp",
out: ["art-run-test-target-data-shard12.zip"],
- srcs: ["*12-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 12 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?12-*/**/*", "??12-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard12",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard12-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard12.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard13",
+ name: "art-run-test-target-data-shard13-tmp",
out: ["art-run-test-target-data-shard13.zip"],
- srcs: ["*13-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 13 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?13-*/**/*", "??13-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard13",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard13-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard13.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard14",
+ name: "art-run-test-target-data-shard14-tmp",
out: ["art-run-test-target-data-shard14.zip"],
- srcs: ["*14-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 14 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?14-*/**/*", "??14-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard14",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard14-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard14.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard15",
+ name: "art-run-test-target-data-shard15-tmp",
out: ["art-run-test-target-data-shard15.zip"],
- srcs: ["*15-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 15 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?15-*/**/*", "??15-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard15",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard15-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard15.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard16",
+ name: "art-run-test-target-data-shard16-tmp",
out: ["art-run-test-target-data-shard16.zip"],
- srcs: ["*16-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 16 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?16-*/**/*", "??16-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard16",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard16-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard16.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard17",
+ name: "art-run-test-target-data-shard17-tmp",
out: ["art-run-test-target-data-shard17.zip"],
- srcs: ["*17-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 17 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?17-*/**/*", "??17-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard17",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard17-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard17.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard18",
+ name: "art-run-test-target-data-shard18-tmp",
out: ["art-run-test-target-data-shard18.zip"],
- srcs: ["*18-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 18 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?18-*/**/*", "??18-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard18",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard18-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard18.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard19",
+ name: "art-run-test-target-data-shard19-tmp",
out: ["art-run-test-target-data-shard19.zip"],
- srcs: ["*19-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 19 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?19-*/**/*", "??19-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard19",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard19-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard19.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard20",
+ name: "art-run-test-target-data-shard20-tmp",
out: ["art-run-test-target-data-shard20.zip"],
- srcs: ["*20-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 20 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?20-*/**/*", "??20-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard20",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard20-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard20.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard21",
+ name: "art-run-test-target-data-shard21-tmp",
out: ["art-run-test-target-data-shard21.zip"],
- srcs: ["*21-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 21 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?21-*/**/*", "??21-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard21",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard21-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard21.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard22",
+ name: "art-run-test-target-data-shard22-tmp",
out: ["art-run-test-target-data-shard22.zip"],
- srcs: ["*22-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 22 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?22-*/**/*", "??22-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard22",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard22-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard22.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard23",
+ name: "art-run-test-target-data-shard23-tmp",
out: ["art-run-test-target-data-shard23.zip"],
- srcs: ["*23-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 23 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?23-*/**/*", "??23-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard23",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard23-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard23.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard24",
+ name: "art-run-test-target-data-shard24-tmp",
out: ["art-run-test-target-data-shard24.zip"],
- srcs: ["*24-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 24 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?24-*/**/*", "??24-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard24",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard24-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard24.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard25",
+ name: "art-run-test-target-data-shard25-tmp",
out: ["art-run-test-target-data-shard25.zip"],
- srcs: ["*25-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 25 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?25-*/**/*", "??25-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard25",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard25-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard25.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard26",
+ name: "art-run-test-target-data-shard26-tmp",
out: ["art-run-test-target-data-shard26.zip"],
- srcs: ["*26-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 26 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?26-*/**/*", "??26-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard26",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard26-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard26.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard27",
+ name: "art-run-test-target-data-shard27-tmp",
out: ["art-run-test-target-data-shard27.zip"],
- srcs: ["*27-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 27 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?27-*/**/*", "??27-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard27",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard27-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard27.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard28",
+ name: "art-run-test-target-data-shard28-tmp",
out: ["art-run-test-target-data-shard28.zip"],
- srcs: ["*28-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 28 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?28-*/**/*", "??28-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard28",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard28-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard28.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard29",
+ name: "art-run-test-target-data-shard29-tmp",
out: ["art-run-test-target-data-shard29.zip"],
- srcs: ["*29-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 29 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?29-*/**/*", "??29-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard29",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard29-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard29.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard30",
+ name: "art-run-test-target-data-shard30-tmp",
out: ["art-run-test-target-data-shard30.zip"],
- srcs: ["*30-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 30 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?30-*/**/*", "??30-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard30",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard30-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard30.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard31",
+ name: "art-run-test-target-data-shard31-tmp",
out: ["art-run-test-target-data-shard31.zip"],
- srcs: ["*31-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 31 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?31-*/**/*", "??31-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard31",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard31-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard31.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard32",
+ name: "art-run-test-target-data-shard32-tmp",
out: ["art-run-test-target-data-shard32.zip"],
- srcs: ["*32-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 32 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?32-*/**/*", "??32-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard32",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard32-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard32.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard33",
+ name: "art-run-test-target-data-shard33-tmp",
out: ["art-run-test-target-data-shard33.zip"],
- srcs: ["*33-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 33 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?33-*/**/*", "??33-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard33",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard33-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard33.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard34",
+ name: "art-run-test-target-data-shard34-tmp",
out: ["art-run-test-target-data-shard34.zip"],
- srcs: ["*34-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 34 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?34-*/**/*", "??34-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard34",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard34-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard34.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard35",
+ name: "art-run-test-target-data-shard35-tmp",
out: ["art-run-test-target-data-shard35.zip"],
- srcs: ["*35-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 35 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?35-*/**/*", "??35-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard35",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard35-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard35.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard36",
+ name: "art-run-test-target-data-shard36-tmp",
out: ["art-run-test-target-data-shard36.zip"],
- srcs: ["*36-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 36 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?36-*/**/*", "??36-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard36",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard36-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard36.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard37",
+ name: "art-run-test-target-data-shard37-tmp",
out: ["art-run-test-target-data-shard37.zip"],
- srcs: ["*37-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 37 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?37-*/**/*", "??37-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard37",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard37-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard37.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard38",
+ name: "art-run-test-target-data-shard38-tmp",
out: ["art-run-test-target-data-shard38.zip"],
- srcs: ["*38-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 38 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?38-*/**/*", "??38-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard38",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard38-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard38.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard39",
+ name: "art-run-test-target-data-shard39-tmp",
out: ["art-run-test-target-data-shard39.zip"],
- srcs: ["*39-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 39 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?39-*/**/*", "??39-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard39",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard39-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard39.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard40",
+ name: "art-run-test-target-data-shard40-tmp",
out: ["art-run-test-target-data-shard40.zip"],
- srcs: ["*40-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 40 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?40-*/**/*", "??40-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard40",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard40-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard40.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard41",
+ name: "art-run-test-target-data-shard41-tmp",
out: ["art-run-test-target-data-shard41.zip"],
- srcs: ["*41-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 41 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?41-*/**/*", "??41-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard41",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard41-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard41.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard42",
+ name: "art-run-test-target-data-shard42-tmp",
out: ["art-run-test-target-data-shard42.zip"],
- srcs: ["*42-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 42 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?42-*/**/*", "??42-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard42",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard42-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard42.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard43",
+ name: "art-run-test-target-data-shard43-tmp",
out: ["art-run-test-target-data-shard43.zip"],
- srcs: ["*43-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 43 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?43-*/**/*", "??43-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard43",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard43-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard43.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard44",
+ name: "art-run-test-target-data-shard44-tmp",
out: ["art-run-test-target-data-shard44.zip"],
- srcs: ["*44-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 44 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?44-*/**/*", "??44-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard44",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard44-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard44.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard45",
+ name: "art-run-test-target-data-shard45-tmp",
out: ["art-run-test-target-data-shard45.zip"],
- srcs: ["*45-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 45 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?45-*/**/*", "??45-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard45",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard45-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard45.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard46",
+ name: "art-run-test-target-data-shard46-tmp",
out: ["art-run-test-target-data-shard46.zip"],
- srcs: ["*46-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 46 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?46-*/**/*", "??46-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard46",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard46-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard46.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard47",
+ name: "art-run-test-target-data-shard47-tmp",
out: ["art-run-test-target-data-shard47.zip"],
- srcs: ["*47-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 47 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?47-*/**/*", "??47-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard47",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard47-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard47.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard48",
+ name: "art-run-test-target-data-shard48-tmp",
out: ["art-run-test-target-data-shard48.zip"],
- srcs: ["*48-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 48 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?48-*/**/*", "??48-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard48",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard48-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard48.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard49",
+ name: "art-run-test-target-data-shard49-tmp",
out: ["art-run-test-target-data-shard49.zip"],
- srcs: ["*49-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 49 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?49-*/**/*", "??49-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard49",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard49-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard49.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard50",
+ name: "art-run-test-target-data-shard50-tmp",
out: ["art-run-test-target-data-shard50.zip"],
- srcs: ["*50-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 50 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?50-*/**/*", "??50-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard50",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard50-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard50.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard51",
+ name: "art-run-test-target-data-shard51-tmp",
out: ["art-run-test-target-data-shard51.zip"],
- srcs: ["*51-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 51 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?51-*/**/*", "??51-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard51",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard51-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard51.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard52",
+ name: "art-run-test-target-data-shard52-tmp",
out: ["art-run-test-target-data-shard52.zip"],
- srcs: ["*52-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 52 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?52-*/**/*", "??52-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard52",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard52-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard52.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard53",
+ name: "art-run-test-target-data-shard53-tmp",
out: ["art-run-test-target-data-shard53.zip"],
- srcs: ["*53-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 53 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?53-*/**/*", "??53-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard53",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard53-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard53.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard54",
+ name: "art-run-test-target-data-shard54-tmp",
out: ["art-run-test-target-data-shard54.zip"],
- srcs: ["*54-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 54 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?54-*/**/*", "??54-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard54",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard54-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard54.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard55",
+ name: "art-run-test-target-data-shard55-tmp",
out: ["art-run-test-target-data-shard55.zip"],
- srcs: ["*55-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 55 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?55-*/**/*", "??55-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard55",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard55-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard55.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard56",
+ name: "art-run-test-target-data-shard56-tmp",
out: ["art-run-test-target-data-shard56.zip"],
- srcs: ["*56-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 56 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?56-*/**/*", "??56-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard56",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard56-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard56.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard57",
+ name: "art-run-test-target-data-shard57-tmp",
out: ["art-run-test-target-data-shard57.zip"],
- srcs: ["*57-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 57 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?57-*/**/*", "??57-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard57",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard57-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard57.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard58",
+ name: "art-run-test-target-data-shard58-tmp",
out: ["art-run-test-target-data-shard58.zip"],
- srcs: ["*58-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 58 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?58-*/**/*", "??58-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard58",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard58-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard58.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard59",
+ name: "art-run-test-target-data-shard59-tmp",
out: ["art-run-test-target-data-shard59.zip"],
- srcs: ["*59-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 59 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?59-*/**/*", "??59-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard59",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard59-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard59.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard60",
+ name: "art-run-test-target-data-shard60-tmp",
out: ["art-run-test-target-data-shard60.zip"],
- srcs: ["*60-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 60 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?60-*/**/*", "??60-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard60",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard60-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard60.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard61",
+ name: "art-run-test-target-data-shard61-tmp",
out: ["art-run-test-target-data-shard61.zip"],
- srcs: ["*61-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 61 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?61-*/**/*", "??61-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard61",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard61-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard61.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard62",
+ name: "art-run-test-target-data-shard62-tmp",
out: ["art-run-test-target-data-shard62.zip"],
- srcs: ["*62-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 62 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?62-*/**/*", "??62-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard62",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard62-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard62.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard63",
+ name: "art-run-test-target-data-shard63-tmp",
out: ["art-run-test-target-data-shard63.zip"],
- srcs: ["*63-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 63 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?63-*/**/*", "??63-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard63",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard63-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard63.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard64",
+ name: "art-run-test-target-data-shard64-tmp",
out: ["art-run-test-target-data-shard64.zip"],
- srcs: ["*64-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 64 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?64-*/**/*", "??64-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard64",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard64-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard64.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard65",
+ name: "art-run-test-target-data-shard65-tmp",
out: ["art-run-test-target-data-shard65.zip"],
- srcs: ["*65-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 65 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?65-*/**/*", "??65-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard65",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard65-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard65.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard66",
+ name: "art-run-test-target-data-shard66-tmp",
out: ["art-run-test-target-data-shard66.zip"],
- srcs: ["*66-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 66 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?66-*/**/*", "??66-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard66",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard66-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard66.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard67",
+ name: "art-run-test-target-data-shard67-tmp",
out: ["art-run-test-target-data-shard67.zip"],
- srcs: ["*67-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 67 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?67-*/**/*", "??67-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard67",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard67-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard67.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard68",
+ name: "art-run-test-target-data-shard68-tmp",
out: ["art-run-test-target-data-shard68.zip"],
- srcs: ["*68-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 68 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?68-*/**/*", "??68-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard68",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard68-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard68.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard69",
+ name: "art-run-test-target-data-shard69-tmp",
out: ["art-run-test-target-data-shard69.zip"],
- srcs: ["*69-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 69 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?69-*/**/*", "??69-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard69",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard69-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard69.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard70",
+ name: "art-run-test-target-data-shard70-tmp",
out: ["art-run-test-target-data-shard70.zip"],
- srcs: ["*70-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 70 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?70-*/**/*", "??70-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard70",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard70-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard70.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard71",
+ name: "art-run-test-target-data-shard71-tmp",
out: ["art-run-test-target-data-shard71.zip"],
- srcs: ["*71-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 71 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?71-*/**/*", "??71-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard71",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard71-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard71.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard72",
+ name: "art-run-test-target-data-shard72-tmp",
out: ["art-run-test-target-data-shard72.zip"],
- srcs: ["*72-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 72 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?72-*/**/*", "??72-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard72",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard72-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard72.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard73",
+ name: "art-run-test-target-data-shard73-tmp",
out: ["art-run-test-target-data-shard73.zip"],
- srcs: ["*73-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 73 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?73-*/**/*", "??73-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard73",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard73-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard73.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard74",
+ name: "art-run-test-target-data-shard74-tmp",
out: ["art-run-test-target-data-shard74.zip"],
- srcs: ["*74-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 74 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?74-*/**/*", "??74-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard74",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard74-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard74.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard75",
+ name: "art-run-test-target-data-shard75-tmp",
out: ["art-run-test-target-data-shard75.zip"],
- srcs: ["*75-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 75 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?75-*/**/*", "??75-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard75",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard75-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard75.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard76",
+ name: "art-run-test-target-data-shard76-tmp",
out: ["art-run-test-target-data-shard76.zip"],
- srcs: ["*76-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 76 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?76-*/**/*", "??76-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard76",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard76-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard76.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard77",
+ name: "art-run-test-target-data-shard77-tmp",
out: ["art-run-test-target-data-shard77.zip"],
- srcs: ["*77-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 77 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?77-*/**/*", "??77-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard77",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard77-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard77.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard78",
+ name: "art-run-test-target-data-shard78-tmp",
out: ["art-run-test-target-data-shard78.zip"],
- srcs: ["*78-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 78 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?78-*/**/*", "??78-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard78",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard78-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard78.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard79",
+ name: "art-run-test-target-data-shard79-tmp",
out: ["art-run-test-target-data-shard79.zip"],
- srcs: ["*79-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 79 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?79-*/**/*", "??79-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard79",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard79-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard79.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard80",
+ name: "art-run-test-target-data-shard80-tmp",
out: ["art-run-test-target-data-shard80.zip"],
- srcs: ["*80-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 80 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?80-*/**/*", "??80-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard80",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard80-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard80.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard81",
+ name: "art-run-test-target-data-shard81-tmp",
out: ["art-run-test-target-data-shard81.zip"],
- srcs: ["*81-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 81 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?81-*/**/*", "??81-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard81",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard81-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard81.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard82",
+ name: "art-run-test-target-data-shard82-tmp",
out: ["art-run-test-target-data-shard82.zip"],
- srcs: ["*82-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 82 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?82-*/**/*", "??82-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard82",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard82-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard82.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard83",
+ name: "art-run-test-target-data-shard83-tmp",
out: ["art-run-test-target-data-shard83.zip"],
- srcs: ["*83-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 83 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?83-*/**/*", "??83-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard83",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard83-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard83.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard84",
+ name: "art-run-test-target-data-shard84-tmp",
out: ["art-run-test-target-data-shard84.zip"],
- srcs: ["*84-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 84 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?84-*/**/*", "??84-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard84",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard84-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard84.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard85",
+ name: "art-run-test-target-data-shard85-tmp",
out: ["art-run-test-target-data-shard85.zip"],
- srcs: ["*85-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 85 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?85-*/**/*", "??85-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard85",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard85-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard85.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard86",
+ name: "art-run-test-target-data-shard86-tmp",
out: ["art-run-test-target-data-shard86.zip"],
- srcs: ["*86-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 86 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?86-*/**/*", "??86-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard86",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard86-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard86.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard87",
+ name: "art-run-test-target-data-shard87-tmp",
out: ["art-run-test-target-data-shard87.zip"],
- srcs: ["*87-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 87 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?87-*/**/*", "??87-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard87",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard87-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard87.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard88",
+ name: "art-run-test-target-data-shard88-tmp",
out: ["art-run-test-target-data-shard88.zip"],
- srcs: ["*88-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 88 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?88-*/**/*", "??88-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard88",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard88-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard88.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard89",
+ name: "art-run-test-target-data-shard89-tmp",
out: ["art-run-test-target-data-shard89.zip"],
- srcs: ["*89-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 89 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?89-*/**/*", "??89-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard89",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard89-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard89.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard90",
+ name: "art-run-test-target-data-shard90-tmp",
out: ["art-run-test-target-data-shard90.zip"],
- srcs: ["*90-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 90 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?90-*/**/*", "??90-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard90",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard90-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard90.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard91",
+ name: "art-run-test-target-data-shard91-tmp",
out: ["art-run-test-target-data-shard91.zip"],
- srcs: ["*91-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 91 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?91-*/**/*", "??91-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard91",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard91-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard91.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard92",
+ name: "art-run-test-target-data-shard92-tmp",
out: ["art-run-test-target-data-shard92.zip"],
- srcs: ["*92-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 92 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?92-*/**/*", "??92-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard92",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard92-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard92.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard93",
+ name: "art-run-test-target-data-shard93-tmp",
out: ["art-run-test-target-data-shard93.zip"],
- srcs: ["*93-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 93 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?93-*/**/*", "??93-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard93",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard93-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard93.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard94",
+ name: "art-run-test-target-data-shard94-tmp",
out: ["art-run-test-target-data-shard94.zip"],
- srcs: ["*94-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 94 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?94-*/**/*", "??94-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard94",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard94-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard94.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard95",
+ name: "art-run-test-target-data-shard95-tmp",
out: ["art-run-test-target-data-shard95.zip"],
- srcs: ["*95-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 95 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?95-*/**/*", "??95-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard95",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard95-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard95.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard96",
+ name: "art-run-test-target-data-shard96-tmp",
out: ["art-run-test-target-data-shard96.zip"],
- srcs: ["*96-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 96 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?96-*/**/*", "??96-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard96",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard96-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard96.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard97",
+ name: "art-run-test-target-data-shard97-tmp",
out: ["art-run-test-target-data-shard97.zip"],
- srcs: ["*97-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 97 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?97-*/**/*", "??97-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard97",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard97-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard97.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard98",
+ name: "art-run-test-target-data-shard98-tmp",
out: ["art-run-test-target-data-shard98.zip"],
- srcs: ["*98-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 98 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?98-*/**/*", "??98-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard98",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard98-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard98.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard99",
+ name: "art-run-test-target-data-shard99-tmp",
out: ["art-run-test-target-data-shard99.zip"],
- srcs: ["*99-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 99 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?99-*/**/*", "??99-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard99",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard99-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard99.zip",
}
java_genrule {
- name: "art-run-test-target-data-merged",
- defaults: ["art-run-test-data-defaults"],
+ name: "art-run-test-target-data-shardHiddenApi-tmp",
+ out: ["art-run-test-target-data-shardHiddenApi.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode target " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shardHiddenApi",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shardHiddenApi-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shardHiddenApi.zip",
+}
+
+genrule_defaults {
+ name: "art-run-test-target-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode target " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+java_genrule {
+ name: "art-run-test-target-data-merged-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
out: ["art-run-test-target-data-merged.zip"],
srcs: [
- ":art-run-test-target-data-shard00",
- ":art-run-test-target-data-shard01",
- ":art-run-test-target-data-shard02",
- ":art-run-test-target-data-shard03",
- ":art-run-test-target-data-shard04",
- ":art-run-test-target-data-shard05",
- ":art-run-test-target-data-shard06",
- ":art-run-test-target-data-shard07",
- ":art-run-test-target-data-shard08",
- ":art-run-test-target-data-shard09",
- ":art-run-test-target-data-shard10",
- ":art-run-test-target-data-shard11",
- ":art-run-test-target-data-shard12",
- ":art-run-test-target-data-shard13",
- ":art-run-test-target-data-shard14",
- ":art-run-test-target-data-shard15",
- ":art-run-test-target-data-shard16",
- ":art-run-test-target-data-shard17",
- ":art-run-test-target-data-shard18",
- ":art-run-test-target-data-shard19",
- ":art-run-test-target-data-shard20",
- ":art-run-test-target-data-shard21",
- ":art-run-test-target-data-shard22",
- ":art-run-test-target-data-shard23",
- ":art-run-test-target-data-shard24",
- ":art-run-test-target-data-shard25",
- ":art-run-test-target-data-shard26",
- ":art-run-test-target-data-shard27",
- ":art-run-test-target-data-shard28",
- ":art-run-test-target-data-shard29",
- ":art-run-test-target-data-shard30",
- ":art-run-test-target-data-shard31",
- ":art-run-test-target-data-shard32",
- ":art-run-test-target-data-shard33",
- ":art-run-test-target-data-shard34",
- ":art-run-test-target-data-shard35",
- ":art-run-test-target-data-shard36",
- ":art-run-test-target-data-shard37",
- ":art-run-test-target-data-shard38",
- ":art-run-test-target-data-shard39",
- ":art-run-test-target-data-shard40",
- ":art-run-test-target-data-shard41",
- ":art-run-test-target-data-shard42",
- ":art-run-test-target-data-shard43",
- ":art-run-test-target-data-shard44",
- ":art-run-test-target-data-shard45",
- ":art-run-test-target-data-shard46",
- ":art-run-test-target-data-shard47",
- ":art-run-test-target-data-shard48",
- ":art-run-test-target-data-shard49",
- ":art-run-test-target-data-shard50",
- ":art-run-test-target-data-shard51",
- ":art-run-test-target-data-shard52",
- ":art-run-test-target-data-shard53",
- ":art-run-test-target-data-shard54",
- ":art-run-test-target-data-shard55",
- ":art-run-test-target-data-shard56",
- ":art-run-test-target-data-shard57",
- ":art-run-test-target-data-shard58",
- ":art-run-test-target-data-shard59",
- ":art-run-test-target-data-shard60",
- ":art-run-test-target-data-shard61",
- ":art-run-test-target-data-shard62",
- ":art-run-test-target-data-shard63",
- ":art-run-test-target-data-shard64",
- ":art-run-test-target-data-shard65",
- ":art-run-test-target-data-shard66",
- ":art-run-test-target-data-shard67",
- ":art-run-test-target-data-shard68",
- ":art-run-test-target-data-shard69",
- ":art-run-test-target-data-shard70",
- ":art-run-test-target-data-shard71",
- ":art-run-test-target-data-shard72",
- ":art-run-test-target-data-shard73",
- ":art-run-test-target-data-shard74",
- ":art-run-test-target-data-shard75",
- ":art-run-test-target-data-shard76",
- ":art-run-test-target-data-shard77",
- ":art-run-test-target-data-shard78",
- ":art-run-test-target-data-shard79",
- ":art-run-test-target-data-shard80",
- ":art-run-test-target-data-shard81",
- ":art-run-test-target-data-shard82",
- ":art-run-test-target-data-shard83",
- ":art-run-test-target-data-shard84",
- ":art-run-test-target-data-shard85",
- ":art-run-test-target-data-shard86",
- ":art-run-test-target-data-shard87",
- ":art-run-test-target-data-shard88",
- ":art-run-test-target-data-shard89",
- ":art-run-test-target-data-shard90",
- ":art-run-test-target-data-shard91",
- ":art-run-test-target-data-shard92",
- ":art-run-test-target-data-shard93",
- ":art-run-test-target-data-shard94",
- ":art-run-test-target-data-shard95",
- ":art-run-test-target-data-shard96",
- ":art-run-test-target-data-shard97",
- ":art-run-test-target-data-shard98",
- ":art-run-test-target-data-shard99",
+ ":art-run-test-target-data-shard00-tmp",
+ ":art-run-test-target-data-shard01-tmp",
+ ":art-run-test-target-data-shard02-tmp",
+ ":art-run-test-target-data-shard03-tmp",
+ ":art-run-test-target-data-shard04-tmp",
+ ":art-run-test-target-data-shard05-tmp",
+ ":art-run-test-target-data-shard06-tmp",
+ ":art-run-test-target-data-shard07-tmp",
+ ":art-run-test-target-data-shard08-tmp",
+ ":art-run-test-target-data-shard09-tmp",
+ ":art-run-test-target-data-shard10-tmp",
+ ":art-run-test-target-data-shard11-tmp",
+ ":art-run-test-target-data-shard12-tmp",
+ ":art-run-test-target-data-shard13-tmp",
+ ":art-run-test-target-data-shard14-tmp",
+ ":art-run-test-target-data-shard15-tmp",
+ ":art-run-test-target-data-shard16-tmp",
+ ":art-run-test-target-data-shard17-tmp",
+ ":art-run-test-target-data-shard18-tmp",
+ ":art-run-test-target-data-shard19-tmp",
+ ":art-run-test-target-data-shard20-tmp",
+ ":art-run-test-target-data-shard21-tmp",
+ ":art-run-test-target-data-shard22-tmp",
+ ":art-run-test-target-data-shard23-tmp",
+ ":art-run-test-target-data-shard24-tmp",
+ ":art-run-test-target-data-shard25-tmp",
+ ":art-run-test-target-data-shard26-tmp",
+ ":art-run-test-target-data-shard27-tmp",
+ ":art-run-test-target-data-shard28-tmp",
+ ":art-run-test-target-data-shard29-tmp",
+ ":art-run-test-target-data-shard30-tmp",
+ ":art-run-test-target-data-shard31-tmp",
+ ":art-run-test-target-data-shard32-tmp",
+ ":art-run-test-target-data-shard33-tmp",
+ ":art-run-test-target-data-shard34-tmp",
+ ":art-run-test-target-data-shard35-tmp",
+ ":art-run-test-target-data-shard36-tmp",
+ ":art-run-test-target-data-shard37-tmp",
+ ":art-run-test-target-data-shard38-tmp",
+ ":art-run-test-target-data-shard39-tmp",
+ ":art-run-test-target-data-shard40-tmp",
+ ":art-run-test-target-data-shard41-tmp",
+ ":art-run-test-target-data-shard42-tmp",
+ ":art-run-test-target-data-shard43-tmp",
+ ":art-run-test-target-data-shard44-tmp",
+ ":art-run-test-target-data-shard45-tmp",
+ ":art-run-test-target-data-shard46-tmp",
+ ":art-run-test-target-data-shard47-tmp",
+ ":art-run-test-target-data-shard48-tmp",
+ ":art-run-test-target-data-shard49-tmp",
+ ":art-run-test-target-data-shard50-tmp",
+ ":art-run-test-target-data-shard51-tmp",
+ ":art-run-test-target-data-shard52-tmp",
+ ":art-run-test-target-data-shard53-tmp",
+ ":art-run-test-target-data-shard54-tmp",
+ ":art-run-test-target-data-shard55-tmp",
+ ":art-run-test-target-data-shard56-tmp",
+ ":art-run-test-target-data-shard57-tmp",
+ ":art-run-test-target-data-shard58-tmp",
+ ":art-run-test-target-data-shard59-tmp",
+ ":art-run-test-target-data-shard60-tmp",
+ ":art-run-test-target-data-shard61-tmp",
+ ":art-run-test-target-data-shard62-tmp",
+ ":art-run-test-target-data-shard63-tmp",
+ ":art-run-test-target-data-shard64-tmp",
+ ":art-run-test-target-data-shard65-tmp",
+ ":art-run-test-target-data-shard66-tmp",
+ ":art-run-test-target-data-shard67-tmp",
+ ":art-run-test-target-data-shard68-tmp",
+ ":art-run-test-target-data-shard69-tmp",
+ ":art-run-test-target-data-shard70-tmp",
+ ":art-run-test-target-data-shard71-tmp",
+ ":art-run-test-target-data-shard72-tmp",
+ ":art-run-test-target-data-shard73-tmp",
+ ":art-run-test-target-data-shard74-tmp",
+ ":art-run-test-target-data-shard75-tmp",
+ ":art-run-test-target-data-shard76-tmp",
+ ":art-run-test-target-data-shard77-tmp",
+ ":art-run-test-target-data-shard78-tmp",
+ ":art-run-test-target-data-shard79-tmp",
+ ":art-run-test-target-data-shard80-tmp",
+ ":art-run-test-target-data-shard81-tmp",
+ ":art-run-test-target-data-shard82-tmp",
+ ":art-run-test-target-data-shard83-tmp",
+ ":art-run-test-target-data-shard84-tmp",
+ ":art-run-test-target-data-shard85-tmp",
+ ":art-run-test-target-data-shard86-tmp",
+ ":art-run-test-target-data-shard87-tmp",
+ ":art-run-test-target-data-shard88-tmp",
+ ":art-run-test-target-data-shard89-tmp",
+ ":art-run-test-target-data-shard90-tmp",
+ ":art-run-test-target-data-shard91-tmp",
+ ":art-run-test-target-data-shard92-tmp",
+ ":art-run-test-target-data-shard93-tmp",
+ ":art-run-test-target-data-shard94-tmp",
+ ":art-run-test-target-data-shard95-tmp",
+ ":art-run-test-target-data-shard96-tmp",
+ ":art-run-test-target-data-shard97-tmp",
+ ":art-run-test-target-data-shard98-tmp",
+ ":art-run-test-target-data-shard99-tmp",
+ ":art-run-test-target-data-shardHiddenApi-tmp",
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-merged",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-merged-tmp",
+ required: [
+ "art-run-test-target-data-shard00",
+ "art-run-test-target-data-shard01",
+ "art-run-test-target-data-shard02",
+ "art-run-test-target-data-shard03",
+ "art-run-test-target-data-shard04",
+ "art-run-test-target-data-shard05",
+ "art-run-test-target-data-shard06",
+ "art-run-test-target-data-shard07",
+ "art-run-test-target-data-shard08",
+ "art-run-test-target-data-shard09",
+ "art-run-test-target-data-shard10",
+ "art-run-test-target-data-shard11",
+ "art-run-test-target-data-shard12",
+ "art-run-test-target-data-shard13",
+ "art-run-test-target-data-shard14",
+ "art-run-test-target-data-shard15",
+ "art-run-test-target-data-shard16",
+ "art-run-test-target-data-shard17",
+ "art-run-test-target-data-shard18",
+ "art-run-test-target-data-shard19",
+ "art-run-test-target-data-shard20",
+ "art-run-test-target-data-shard21",
+ "art-run-test-target-data-shard22",
+ "art-run-test-target-data-shard23",
+ "art-run-test-target-data-shard24",
+ "art-run-test-target-data-shard25",
+ "art-run-test-target-data-shard26",
+ "art-run-test-target-data-shard27",
+ "art-run-test-target-data-shard28",
+ "art-run-test-target-data-shard29",
+ "art-run-test-target-data-shard30",
+ "art-run-test-target-data-shard31",
+ "art-run-test-target-data-shard32",
+ "art-run-test-target-data-shard33",
+ "art-run-test-target-data-shard34",
+ "art-run-test-target-data-shard35",
+ "art-run-test-target-data-shard36",
+ "art-run-test-target-data-shard37",
+ "art-run-test-target-data-shard38",
+ "art-run-test-target-data-shard39",
+ "art-run-test-target-data-shard40",
+ "art-run-test-target-data-shard41",
+ "art-run-test-target-data-shard42",
+ "art-run-test-target-data-shard43",
+ "art-run-test-target-data-shard44",
+ "art-run-test-target-data-shard45",
+ "art-run-test-target-data-shard46",
+ "art-run-test-target-data-shard47",
+ "art-run-test-target-data-shard48",
+ "art-run-test-target-data-shard49",
+ "art-run-test-target-data-shard50",
+ "art-run-test-target-data-shard51",
+ "art-run-test-target-data-shard52",
+ "art-run-test-target-data-shard53",
+ "art-run-test-target-data-shard54",
+ "art-run-test-target-data-shard55",
+ "art-run-test-target-data-shard56",
+ "art-run-test-target-data-shard57",
+ "art-run-test-target-data-shard58",
+ "art-run-test-target-data-shard59",
+ "art-run-test-target-data-shard60",
+ "art-run-test-target-data-shard61",
+ "art-run-test-target-data-shard62",
+ "art-run-test-target-data-shard63",
+ "art-run-test-target-data-shard64",
+ "art-run-test-target-data-shard65",
+ "art-run-test-target-data-shard66",
+ "art-run-test-target-data-shard67",
+ "art-run-test-target-data-shard68",
+ "art-run-test-target-data-shard69",
+ "art-run-test-target-data-shard70",
+ "art-run-test-target-data-shard71",
+ "art-run-test-target-data-shard72",
+ "art-run-test-target-data-shard73",
+ "art-run-test-target-data-shard74",
+ "art-run-test-target-data-shard75",
+ "art-run-test-target-data-shard76",
+ "art-run-test-target-data-shard77",
+ "art-run-test-target-data-shard78",
+ "art-run-test-target-data-shard79",
+ "art-run-test-target-data-shard80",
+ "art-run-test-target-data-shard81",
+ "art-run-test-target-data-shard82",
+ "art-run-test-target-data-shard83",
+ "art-run-test-target-data-shard84",
+ "art-run-test-target-data-shard85",
+ "art-run-test-target-data-shard86",
+ "art-run-test-target-data-shard87",
+ "art-run-test-target-data-shard88",
+ "art-run-test-target-data-shard89",
+ "art-run-test-target-data-shard90",
+ "art-run-test-target-data-shard91",
+ "art-run-test-target-data-shard92",
+ "art-run-test-target-data-shard93",
+ "art-run-test-target-data-shard94",
+ "art-run-test-target-data-shard95",
+ "art-run-test-target-data-shard96",
+ "art-run-test-target-data-shard97",
+ "art-run-test-target-data-shard98",
+ "art-run-test-target-data-shard99",
+ "art-run-test-target-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-target-data-merged.zip",
+}
+
+// Phony target used to build all shards
java_genrule {
- name: "art-run-test-jvm-data-shard00",
+ name: "art-run-test-target-data-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["art-run-test-target-data.txt"],
+ srcs: [
+ ":art-run-test-target-data-shard00-tmp",
+ ":art-run-test-target-data-shard01-tmp",
+ ":art-run-test-target-data-shard02-tmp",
+ ":art-run-test-target-data-shard03-tmp",
+ ":art-run-test-target-data-shard04-tmp",
+ ":art-run-test-target-data-shard05-tmp",
+ ":art-run-test-target-data-shard06-tmp",
+ ":art-run-test-target-data-shard07-tmp",
+ ":art-run-test-target-data-shard08-tmp",
+ ":art-run-test-target-data-shard09-tmp",
+ ":art-run-test-target-data-shard10-tmp",
+ ":art-run-test-target-data-shard11-tmp",
+ ":art-run-test-target-data-shard12-tmp",
+ ":art-run-test-target-data-shard13-tmp",
+ ":art-run-test-target-data-shard14-tmp",
+ ":art-run-test-target-data-shard15-tmp",
+ ":art-run-test-target-data-shard16-tmp",
+ ":art-run-test-target-data-shard17-tmp",
+ ":art-run-test-target-data-shard18-tmp",
+ ":art-run-test-target-data-shard19-tmp",
+ ":art-run-test-target-data-shard20-tmp",
+ ":art-run-test-target-data-shard21-tmp",
+ ":art-run-test-target-data-shard22-tmp",
+ ":art-run-test-target-data-shard23-tmp",
+ ":art-run-test-target-data-shard24-tmp",
+ ":art-run-test-target-data-shard25-tmp",
+ ":art-run-test-target-data-shard26-tmp",
+ ":art-run-test-target-data-shard27-tmp",
+ ":art-run-test-target-data-shard28-tmp",
+ ":art-run-test-target-data-shard29-tmp",
+ ":art-run-test-target-data-shard30-tmp",
+ ":art-run-test-target-data-shard31-tmp",
+ ":art-run-test-target-data-shard32-tmp",
+ ":art-run-test-target-data-shard33-tmp",
+ ":art-run-test-target-data-shard34-tmp",
+ ":art-run-test-target-data-shard35-tmp",
+ ":art-run-test-target-data-shard36-tmp",
+ ":art-run-test-target-data-shard37-tmp",
+ ":art-run-test-target-data-shard38-tmp",
+ ":art-run-test-target-data-shard39-tmp",
+ ":art-run-test-target-data-shard40-tmp",
+ ":art-run-test-target-data-shard41-tmp",
+ ":art-run-test-target-data-shard42-tmp",
+ ":art-run-test-target-data-shard43-tmp",
+ ":art-run-test-target-data-shard44-tmp",
+ ":art-run-test-target-data-shard45-tmp",
+ ":art-run-test-target-data-shard46-tmp",
+ ":art-run-test-target-data-shard47-tmp",
+ ":art-run-test-target-data-shard48-tmp",
+ ":art-run-test-target-data-shard49-tmp",
+ ":art-run-test-target-data-shard50-tmp",
+ ":art-run-test-target-data-shard51-tmp",
+ ":art-run-test-target-data-shard52-tmp",
+ ":art-run-test-target-data-shard53-tmp",
+ ":art-run-test-target-data-shard54-tmp",
+ ":art-run-test-target-data-shard55-tmp",
+ ":art-run-test-target-data-shard56-tmp",
+ ":art-run-test-target-data-shard57-tmp",
+ ":art-run-test-target-data-shard58-tmp",
+ ":art-run-test-target-data-shard59-tmp",
+ ":art-run-test-target-data-shard60-tmp",
+ ":art-run-test-target-data-shard61-tmp",
+ ":art-run-test-target-data-shard62-tmp",
+ ":art-run-test-target-data-shard63-tmp",
+ ":art-run-test-target-data-shard64-tmp",
+ ":art-run-test-target-data-shard65-tmp",
+ ":art-run-test-target-data-shard66-tmp",
+ ":art-run-test-target-data-shard67-tmp",
+ ":art-run-test-target-data-shard68-tmp",
+ ":art-run-test-target-data-shard69-tmp",
+ ":art-run-test-target-data-shard70-tmp",
+ ":art-run-test-target-data-shard71-tmp",
+ ":art-run-test-target-data-shard72-tmp",
+ ":art-run-test-target-data-shard73-tmp",
+ ":art-run-test-target-data-shard74-tmp",
+ ":art-run-test-target-data-shard75-tmp",
+ ":art-run-test-target-data-shard76-tmp",
+ ":art-run-test-target-data-shard77-tmp",
+ ":art-run-test-target-data-shard78-tmp",
+ ":art-run-test-target-data-shard79-tmp",
+ ":art-run-test-target-data-shard80-tmp",
+ ":art-run-test-target-data-shard81-tmp",
+ ":art-run-test-target-data-shard82-tmp",
+ ":art-run-test-target-data-shard83-tmp",
+ ":art-run-test-target-data-shard84-tmp",
+ ":art-run-test-target-data-shard85-tmp",
+ ":art-run-test-target-data-shard86-tmp",
+ ":art-run-test-target-data-shard87-tmp",
+ ":art-run-test-target-data-shard88-tmp",
+ ":art-run-test-target-data-shard89-tmp",
+ ":art-run-test-target-data-shard90-tmp",
+ ":art-run-test-target-data-shard91-tmp",
+ ":art-run-test-target-data-shard92-tmp",
+ ":art-run-test-target-data-shard93-tmp",
+ ":art-run-test-target-data-shard94-tmp",
+ ":art-run-test-target-data-shard95-tmp",
+ ":art-run-test-target-data-shard96-tmp",
+ ":art-run-test-target-data-shard97-tmp",
+ ":art-run-test-target-data-shard98-tmp",
+ ":art-run-test-target-data-shard99-tmp",
+ ":art-run-test-target-data-shardHiddenApi-tmp",
+ ],
+ cmd: "echo $(in) > $(out)",
+}
+
+// Phony target used to install all shards
+prebuilt_etc_host {
+ name: "art-run-test-target-data",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-tmp",
+ required: [
+ "art-run-test-target-data-shard00",
+ "art-run-test-target-data-shard01",
+ "art-run-test-target-data-shard02",
+ "art-run-test-target-data-shard03",
+ "art-run-test-target-data-shard04",
+ "art-run-test-target-data-shard05",
+ "art-run-test-target-data-shard06",
+ "art-run-test-target-data-shard07",
+ "art-run-test-target-data-shard08",
+ "art-run-test-target-data-shard09",
+ "art-run-test-target-data-shard10",
+ "art-run-test-target-data-shard11",
+ "art-run-test-target-data-shard12",
+ "art-run-test-target-data-shard13",
+ "art-run-test-target-data-shard14",
+ "art-run-test-target-data-shard15",
+ "art-run-test-target-data-shard16",
+ "art-run-test-target-data-shard17",
+ "art-run-test-target-data-shard18",
+ "art-run-test-target-data-shard19",
+ "art-run-test-target-data-shard20",
+ "art-run-test-target-data-shard21",
+ "art-run-test-target-data-shard22",
+ "art-run-test-target-data-shard23",
+ "art-run-test-target-data-shard24",
+ "art-run-test-target-data-shard25",
+ "art-run-test-target-data-shard26",
+ "art-run-test-target-data-shard27",
+ "art-run-test-target-data-shard28",
+ "art-run-test-target-data-shard29",
+ "art-run-test-target-data-shard30",
+ "art-run-test-target-data-shard31",
+ "art-run-test-target-data-shard32",
+ "art-run-test-target-data-shard33",
+ "art-run-test-target-data-shard34",
+ "art-run-test-target-data-shard35",
+ "art-run-test-target-data-shard36",
+ "art-run-test-target-data-shard37",
+ "art-run-test-target-data-shard38",
+ "art-run-test-target-data-shard39",
+ "art-run-test-target-data-shard40",
+ "art-run-test-target-data-shard41",
+ "art-run-test-target-data-shard42",
+ "art-run-test-target-data-shard43",
+ "art-run-test-target-data-shard44",
+ "art-run-test-target-data-shard45",
+ "art-run-test-target-data-shard46",
+ "art-run-test-target-data-shard47",
+ "art-run-test-target-data-shard48",
+ "art-run-test-target-data-shard49",
+ "art-run-test-target-data-shard50",
+ "art-run-test-target-data-shard51",
+ "art-run-test-target-data-shard52",
+ "art-run-test-target-data-shard53",
+ "art-run-test-target-data-shard54",
+ "art-run-test-target-data-shard55",
+ "art-run-test-target-data-shard56",
+ "art-run-test-target-data-shard57",
+ "art-run-test-target-data-shard58",
+ "art-run-test-target-data-shard59",
+ "art-run-test-target-data-shard60",
+ "art-run-test-target-data-shard61",
+ "art-run-test-target-data-shard62",
+ "art-run-test-target-data-shard63",
+ "art-run-test-target-data-shard64",
+ "art-run-test-target-data-shard65",
+ "art-run-test-target-data-shard66",
+ "art-run-test-target-data-shard67",
+ "art-run-test-target-data-shard68",
+ "art-run-test-target-data-shard69",
+ "art-run-test-target-data-shard70",
+ "art-run-test-target-data-shard71",
+ "art-run-test-target-data-shard72",
+ "art-run-test-target-data-shard73",
+ "art-run-test-target-data-shard74",
+ "art-run-test-target-data-shard75",
+ "art-run-test-target-data-shard76",
+ "art-run-test-target-data-shard77",
+ "art-run-test-target-data-shard78",
+ "art-run-test-target-data-shard79",
+ "art-run-test-target-data-shard80",
+ "art-run-test-target-data-shard81",
+ "art-run-test-target-data-shard82",
+ "art-run-test-target-data-shard83",
+ "art-run-test-target-data-shard84",
+ "art-run-test-target-data-shard85",
+ "art-run-test-target-data-shard86",
+ "art-run-test-target-data-shard87",
+ "art-run-test-target-data-shard88",
+ "art-run-test-target-data-shard89",
+ "art-run-test-target-data-shard90",
+ "art-run-test-target-data-shard91",
+ "art-run-test-target-data-shard92",
+ "art-run-test-target-data-shard93",
+ "art-run-test-target-data-shard94",
+ "art-run-test-target-data-shard95",
+ "art-run-test-target-data-shard96",
+ "art-run-test-target-data-shard97",
+ "art-run-test-target-data-shard98",
+ "art-run-test-target-data-shard99",
+ "art-run-test-target-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-target-data.txt",
+}
+
+java_genrule {
+ name: "art-run-test-jvm-data-shard00-tmp",
out: ["art-run-test-jvm-data-shard00.zip"],
- srcs: ["*00-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 00 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?00-*/**/*", "??00-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard00",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard00-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard00.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard01",
+ name: "art-run-test-jvm-data-shard01-tmp",
out: ["art-run-test-jvm-data-shard01.zip"],
- srcs: ["*01-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 01 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?01-*/**/*", "??01-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard01",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard01-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard01.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard02",
+ name: "art-run-test-jvm-data-shard02-tmp",
out: ["art-run-test-jvm-data-shard02.zip"],
- srcs: ["*02-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 02 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?02-*/**/*", "??02-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard02",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard02-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard02.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard03",
+ name: "art-run-test-jvm-data-shard03-tmp",
out: ["art-run-test-jvm-data-shard03.zip"],
- srcs: ["*03-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 03 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?03-*/**/*", "??03-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard03",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard03-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard03.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard04",
+ name: "art-run-test-jvm-data-shard04-tmp",
out: ["art-run-test-jvm-data-shard04.zip"],
- srcs: ["*04-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 04 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?04-*/**/*", "??04-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard04",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard04-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard04.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard05",
+ name: "art-run-test-jvm-data-shard05-tmp",
out: ["art-run-test-jvm-data-shard05.zip"],
- srcs: ["*05-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 05 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?05-*/**/*", "??05-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard05",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard05-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard05.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard06",
+ name: "art-run-test-jvm-data-shard06-tmp",
out: ["art-run-test-jvm-data-shard06.zip"],
- srcs: ["*06-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 06 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?06-*/**/*", "??06-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard06",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard06-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard06.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard07",
+ name: "art-run-test-jvm-data-shard07-tmp",
out: ["art-run-test-jvm-data-shard07.zip"],
- srcs: ["*07-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 07 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?07-*/**/*", "??07-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard07",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard07-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard07.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard08",
+ name: "art-run-test-jvm-data-shard08-tmp",
out: ["art-run-test-jvm-data-shard08.zip"],
- srcs: ["*08-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 08 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?08-*/**/*", "??08-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard08",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard08-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard08.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard09",
+ name: "art-run-test-jvm-data-shard09-tmp",
out: ["art-run-test-jvm-data-shard09.zip"],
- srcs: ["*09-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 09 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?09-*/**/*", "??09-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard09",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard09-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard09.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard10",
+ name: "art-run-test-jvm-data-shard10-tmp",
out: ["art-run-test-jvm-data-shard10.zip"],
- srcs: ["*10-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 10 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?10-*/**/*", "??10-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard10",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard10-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard10.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard11",
+ name: "art-run-test-jvm-data-shard11-tmp",
out: ["art-run-test-jvm-data-shard11.zip"],
- srcs: ["*11-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 11 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?11-*/**/*", "??11-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard11",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard11-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard11.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard12",
+ name: "art-run-test-jvm-data-shard12-tmp",
out: ["art-run-test-jvm-data-shard12.zip"],
- srcs: ["*12-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 12 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?12-*/**/*", "??12-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard12",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard12-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard12.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard13",
+ name: "art-run-test-jvm-data-shard13-tmp",
out: ["art-run-test-jvm-data-shard13.zip"],
- srcs: ["*13-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 13 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?13-*/**/*", "??13-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard13",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard13-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard13.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard14",
+ name: "art-run-test-jvm-data-shard14-tmp",
out: ["art-run-test-jvm-data-shard14.zip"],
- srcs: ["*14-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 14 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?14-*/**/*", "??14-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard14",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard14-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard14.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard15",
+ name: "art-run-test-jvm-data-shard15-tmp",
out: ["art-run-test-jvm-data-shard15.zip"],
- srcs: ["*15-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 15 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?15-*/**/*", "??15-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard15",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard15-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard15.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard16",
+ name: "art-run-test-jvm-data-shard16-tmp",
out: ["art-run-test-jvm-data-shard16.zip"],
- srcs: ["*16-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 16 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?16-*/**/*", "??16-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard16",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard16-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard16.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard17",
+ name: "art-run-test-jvm-data-shard17-tmp",
out: ["art-run-test-jvm-data-shard17.zip"],
- srcs: ["*17-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 17 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?17-*/**/*", "??17-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard17",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard17-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard17.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard18",
+ name: "art-run-test-jvm-data-shard18-tmp",
out: ["art-run-test-jvm-data-shard18.zip"],
- srcs: ["*18-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 18 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?18-*/**/*", "??18-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard18",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard18-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard18.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard19",
+ name: "art-run-test-jvm-data-shard19-tmp",
out: ["art-run-test-jvm-data-shard19.zip"],
- srcs: ["*19-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 19 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?19-*/**/*", "??19-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard19",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard19-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard19.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard20",
+ name: "art-run-test-jvm-data-shard20-tmp",
out: ["art-run-test-jvm-data-shard20.zip"],
- srcs: ["*20-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 20 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?20-*/**/*", "??20-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard20",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard20-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard20.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard21",
+ name: "art-run-test-jvm-data-shard21-tmp",
out: ["art-run-test-jvm-data-shard21.zip"],
- srcs: ["*21-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 21 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?21-*/**/*", "??21-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard21",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard21-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard21.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard22",
+ name: "art-run-test-jvm-data-shard22-tmp",
out: ["art-run-test-jvm-data-shard22.zip"],
- srcs: ["*22-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 22 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?22-*/**/*", "??22-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard22",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard22-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard22.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard23",
+ name: "art-run-test-jvm-data-shard23-tmp",
out: ["art-run-test-jvm-data-shard23.zip"],
- srcs: ["*23-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 23 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?23-*/**/*", "??23-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard23",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard23-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard23.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard24",
+ name: "art-run-test-jvm-data-shard24-tmp",
out: ["art-run-test-jvm-data-shard24.zip"],
- srcs: ["*24-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 24 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?24-*/**/*", "??24-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard24",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard24-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard24.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard25",
+ name: "art-run-test-jvm-data-shard25-tmp",
out: ["art-run-test-jvm-data-shard25.zip"],
- srcs: ["*25-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 25 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?25-*/**/*", "??25-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard25",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard25-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard25.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard26",
+ name: "art-run-test-jvm-data-shard26-tmp",
out: ["art-run-test-jvm-data-shard26.zip"],
- srcs: ["*26-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 26 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?26-*/**/*", "??26-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard26",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard26-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard26.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard27",
+ name: "art-run-test-jvm-data-shard27-tmp",
out: ["art-run-test-jvm-data-shard27.zip"],
- srcs: ["*27-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 27 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?27-*/**/*", "??27-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard27",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard27-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard27.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard28",
+ name: "art-run-test-jvm-data-shard28-tmp",
out: ["art-run-test-jvm-data-shard28.zip"],
- srcs: ["*28-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 28 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?28-*/**/*", "??28-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard28",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard28-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard28.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard29",
+ name: "art-run-test-jvm-data-shard29-tmp",
out: ["art-run-test-jvm-data-shard29.zip"],
- srcs: ["*29-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 29 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?29-*/**/*", "??29-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard29",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard29-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard29.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard30",
+ name: "art-run-test-jvm-data-shard30-tmp",
out: ["art-run-test-jvm-data-shard30.zip"],
- srcs: ["*30-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 30 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?30-*/**/*", "??30-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard30",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard30-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard30.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard31",
+ name: "art-run-test-jvm-data-shard31-tmp",
out: ["art-run-test-jvm-data-shard31.zip"],
- srcs: ["*31-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 31 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?31-*/**/*", "??31-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard31",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard31-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard31.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard32",
+ name: "art-run-test-jvm-data-shard32-tmp",
out: ["art-run-test-jvm-data-shard32.zip"],
- srcs: ["*32-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 32 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?32-*/**/*", "??32-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard32",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard32-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard32.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard33",
+ name: "art-run-test-jvm-data-shard33-tmp",
out: ["art-run-test-jvm-data-shard33.zip"],
- srcs: ["*33-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 33 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?33-*/**/*", "??33-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard33",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard33-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard33.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard34",
+ name: "art-run-test-jvm-data-shard34-tmp",
out: ["art-run-test-jvm-data-shard34.zip"],
- srcs: ["*34-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 34 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?34-*/**/*", "??34-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard34",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard34-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard34.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard35",
+ name: "art-run-test-jvm-data-shard35-tmp",
out: ["art-run-test-jvm-data-shard35.zip"],
- srcs: ["*35-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 35 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?35-*/**/*", "??35-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard35",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard35-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard35.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard36",
+ name: "art-run-test-jvm-data-shard36-tmp",
out: ["art-run-test-jvm-data-shard36.zip"],
- srcs: ["*36-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 36 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?36-*/**/*", "??36-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard36",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard36-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard36.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard37",
+ name: "art-run-test-jvm-data-shard37-tmp",
out: ["art-run-test-jvm-data-shard37.zip"],
- srcs: ["*37-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 37 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?37-*/**/*", "??37-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard37",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard37-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard37.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard38",
+ name: "art-run-test-jvm-data-shard38-tmp",
out: ["art-run-test-jvm-data-shard38.zip"],
- srcs: ["*38-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 38 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?38-*/**/*", "??38-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard38",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard38-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard38.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard39",
+ name: "art-run-test-jvm-data-shard39-tmp",
out: ["art-run-test-jvm-data-shard39.zip"],
- srcs: ["*39-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 39 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?39-*/**/*", "??39-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard39",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard39-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard39.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard40",
+ name: "art-run-test-jvm-data-shard40-tmp",
out: ["art-run-test-jvm-data-shard40.zip"],
- srcs: ["*40-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 40 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?40-*/**/*", "??40-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard40",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard40-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard40.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard41",
+ name: "art-run-test-jvm-data-shard41-tmp",
out: ["art-run-test-jvm-data-shard41.zip"],
- srcs: ["*41-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 41 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?41-*/**/*", "??41-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard41",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard41-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard41.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard42",
+ name: "art-run-test-jvm-data-shard42-tmp",
out: ["art-run-test-jvm-data-shard42.zip"],
- srcs: ["*42-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 42 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?42-*/**/*", "??42-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard42",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard42-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard42.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard43",
+ name: "art-run-test-jvm-data-shard43-tmp",
out: ["art-run-test-jvm-data-shard43.zip"],
- srcs: ["*43-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 43 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?43-*/**/*", "??43-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard43",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard43-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard43.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard44",
+ name: "art-run-test-jvm-data-shard44-tmp",
out: ["art-run-test-jvm-data-shard44.zip"],
- srcs: ["*44-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 44 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?44-*/**/*", "??44-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard44",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard44-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard44.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard45",
+ name: "art-run-test-jvm-data-shard45-tmp",
out: ["art-run-test-jvm-data-shard45.zip"],
- srcs: ["*45-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 45 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?45-*/**/*", "??45-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard45",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard45-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard45.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard46",
+ name: "art-run-test-jvm-data-shard46-tmp",
out: ["art-run-test-jvm-data-shard46.zip"],
- srcs: ["*46-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 46 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?46-*/**/*", "??46-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard46",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard46-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard46.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard47",
+ name: "art-run-test-jvm-data-shard47-tmp",
out: ["art-run-test-jvm-data-shard47.zip"],
- srcs: ["*47-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 47 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?47-*/**/*", "??47-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard47",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard47-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard47.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard48",
+ name: "art-run-test-jvm-data-shard48-tmp",
out: ["art-run-test-jvm-data-shard48.zip"],
- srcs: ["*48-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 48 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?48-*/**/*", "??48-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard48",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard48-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard48.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard49",
+ name: "art-run-test-jvm-data-shard49-tmp",
out: ["art-run-test-jvm-data-shard49.zip"],
- srcs: ["*49-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 49 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?49-*/**/*", "??49-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard49",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard49-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard49.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard50",
+ name: "art-run-test-jvm-data-shard50-tmp",
out: ["art-run-test-jvm-data-shard50.zip"],
- srcs: ["*50-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 50 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?50-*/**/*", "??50-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard50",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard50-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard50.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard51",
+ name: "art-run-test-jvm-data-shard51-tmp",
out: ["art-run-test-jvm-data-shard51.zip"],
- srcs: ["*51-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 51 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?51-*/**/*", "??51-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard51",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard51-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard51.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard52",
+ name: "art-run-test-jvm-data-shard52-tmp",
out: ["art-run-test-jvm-data-shard52.zip"],
- srcs: ["*52-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 52 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?52-*/**/*", "??52-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard52",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard52-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard52.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard53",
+ name: "art-run-test-jvm-data-shard53-tmp",
out: ["art-run-test-jvm-data-shard53.zip"],
- srcs: ["*53-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 53 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?53-*/**/*", "??53-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard53",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard53-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard53.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard54",
+ name: "art-run-test-jvm-data-shard54-tmp",
out: ["art-run-test-jvm-data-shard54.zip"],
- srcs: ["*54-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 54 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?54-*/**/*", "??54-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard54",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard54-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard54.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard55",
+ name: "art-run-test-jvm-data-shard55-tmp",
out: ["art-run-test-jvm-data-shard55.zip"],
- srcs: ["*55-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 55 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?55-*/**/*", "??55-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard55",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard55-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard55.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard56",
+ name: "art-run-test-jvm-data-shard56-tmp",
out: ["art-run-test-jvm-data-shard56.zip"],
- srcs: ["*56-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 56 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?56-*/**/*", "??56-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard56",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard56-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard56.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard57",
+ name: "art-run-test-jvm-data-shard57-tmp",
out: ["art-run-test-jvm-data-shard57.zip"],
- srcs: ["*57-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 57 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?57-*/**/*", "??57-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard57",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard57-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard57.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard58",
+ name: "art-run-test-jvm-data-shard58-tmp",
out: ["art-run-test-jvm-data-shard58.zip"],
- srcs: ["*58-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 58 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?58-*/**/*", "??58-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard58",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard58-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard58.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard59",
+ name: "art-run-test-jvm-data-shard59-tmp",
out: ["art-run-test-jvm-data-shard59.zip"],
- srcs: ["*59-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 59 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?59-*/**/*", "??59-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard59",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard59-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard59.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard60",
+ name: "art-run-test-jvm-data-shard60-tmp",
out: ["art-run-test-jvm-data-shard60.zip"],
- srcs: ["*60-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 60 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?60-*/**/*", "??60-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard60",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard60-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard60.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard61",
+ name: "art-run-test-jvm-data-shard61-tmp",
out: ["art-run-test-jvm-data-shard61.zip"],
- srcs: ["*61-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 61 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?61-*/**/*", "??61-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard61",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard61-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard61.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard62",
+ name: "art-run-test-jvm-data-shard62-tmp",
out: ["art-run-test-jvm-data-shard62.zip"],
- srcs: ["*62-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 62 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?62-*/**/*", "??62-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard62",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard62-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard62.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard63",
+ name: "art-run-test-jvm-data-shard63-tmp",
out: ["art-run-test-jvm-data-shard63.zip"],
- srcs: ["*63-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 63 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?63-*/**/*", "??63-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard63",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard63-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard63.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard64",
+ name: "art-run-test-jvm-data-shard64-tmp",
out: ["art-run-test-jvm-data-shard64.zip"],
- srcs: ["*64-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 64 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?64-*/**/*", "??64-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard64",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard64-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard64.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard65",
+ name: "art-run-test-jvm-data-shard65-tmp",
out: ["art-run-test-jvm-data-shard65.zip"],
- srcs: ["*65-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 65 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?65-*/**/*", "??65-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard65",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard65-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard65.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard66",
+ name: "art-run-test-jvm-data-shard66-tmp",
out: ["art-run-test-jvm-data-shard66.zip"],
- srcs: ["*66-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 66 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?66-*/**/*", "??66-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard66",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard66-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard66.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard67",
+ name: "art-run-test-jvm-data-shard67-tmp",
out: ["art-run-test-jvm-data-shard67.zip"],
- srcs: ["*67-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 67 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?67-*/**/*", "??67-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard67",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard67-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard67.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard68",
+ name: "art-run-test-jvm-data-shard68-tmp",
out: ["art-run-test-jvm-data-shard68.zip"],
- srcs: ["*68-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 68 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?68-*/**/*", "??68-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard68",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard68-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard68.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard69",
+ name: "art-run-test-jvm-data-shard69-tmp",
out: ["art-run-test-jvm-data-shard69.zip"],
- srcs: ["*69-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 69 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?69-*/**/*", "??69-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard69",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard69-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard69.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard70",
+ name: "art-run-test-jvm-data-shard70-tmp",
out: ["art-run-test-jvm-data-shard70.zip"],
- srcs: ["*70-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 70 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?70-*/**/*", "??70-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard70",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard70-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard70.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard71",
+ name: "art-run-test-jvm-data-shard71-tmp",
out: ["art-run-test-jvm-data-shard71.zip"],
- srcs: ["*71-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 71 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?71-*/**/*", "??71-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard71",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard71-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard71.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard72",
+ name: "art-run-test-jvm-data-shard72-tmp",
out: ["art-run-test-jvm-data-shard72.zip"],
- srcs: ["*72-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 72 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?72-*/**/*", "??72-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard72",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard72-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard72.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard73",
+ name: "art-run-test-jvm-data-shard73-tmp",
out: ["art-run-test-jvm-data-shard73.zip"],
- srcs: ["*73-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 73 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?73-*/**/*", "??73-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard73",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard73-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard73.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard74",
+ name: "art-run-test-jvm-data-shard74-tmp",
out: ["art-run-test-jvm-data-shard74.zip"],
- srcs: ["*74-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 74 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?74-*/**/*", "??74-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard74",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard74-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard74.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard75",
+ name: "art-run-test-jvm-data-shard75-tmp",
out: ["art-run-test-jvm-data-shard75.zip"],
- srcs: ["*75-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 75 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?75-*/**/*", "??75-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard75",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard75-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard75.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard76",
+ name: "art-run-test-jvm-data-shard76-tmp",
out: ["art-run-test-jvm-data-shard76.zip"],
- srcs: ["*76-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 76 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?76-*/**/*", "??76-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard76",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard76-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard76.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard77",
+ name: "art-run-test-jvm-data-shard77-tmp",
out: ["art-run-test-jvm-data-shard77.zip"],
- srcs: ["*77-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 77 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?77-*/**/*", "??77-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard77",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard77-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard77.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard78",
+ name: "art-run-test-jvm-data-shard78-tmp",
out: ["art-run-test-jvm-data-shard78.zip"],
- srcs: ["*78-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 78 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?78-*/**/*", "??78-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard78",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard78-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard78.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard79",
+ name: "art-run-test-jvm-data-shard79-tmp",
out: ["art-run-test-jvm-data-shard79.zip"],
- srcs: ["*79-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 79 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?79-*/**/*", "??79-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard79",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard79-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard79.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard80",
+ name: "art-run-test-jvm-data-shard80-tmp",
out: ["art-run-test-jvm-data-shard80.zip"],
- srcs: ["*80-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 80 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?80-*/**/*", "??80-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard80",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard80-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard80.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard81",
+ name: "art-run-test-jvm-data-shard81-tmp",
out: ["art-run-test-jvm-data-shard81.zip"],
- srcs: ["*81-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 81 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?81-*/**/*", "??81-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard81",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard81-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard81.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard82",
+ name: "art-run-test-jvm-data-shard82-tmp",
out: ["art-run-test-jvm-data-shard82.zip"],
- srcs: ["*82-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 82 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?82-*/**/*", "??82-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard82",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard82-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard82.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard83",
+ name: "art-run-test-jvm-data-shard83-tmp",
out: ["art-run-test-jvm-data-shard83.zip"],
- srcs: ["*83-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 83 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?83-*/**/*", "??83-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard83",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard83-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard83.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard84",
+ name: "art-run-test-jvm-data-shard84-tmp",
out: ["art-run-test-jvm-data-shard84.zip"],
- srcs: ["*84-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 84 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?84-*/**/*", "??84-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard84",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard84-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard84.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard85",
+ name: "art-run-test-jvm-data-shard85-tmp",
out: ["art-run-test-jvm-data-shard85.zip"],
- srcs: ["*85-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 85 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?85-*/**/*", "??85-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard85",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard85-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard85.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard86",
+ name: "art-run-test-jvm-data-shard86-tmp",
out: ["art-run-test-jvm-data-shard86.zip"],
- srcs: ["*86-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 86 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?86-*/**/*", "??86-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard86",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard86-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard86.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard87",
+ name: "art-run-test-jvm-data-shard87-tmp",
out: ["art-run-test-jvm-data-shard87.zip"],
- srcs: ["*87-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 87 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?87-*/**/*", "??87-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard87",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard87-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard87.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard88",
+ name: "art-run-test-jvm-data-shard88-tmp",
out: ["art-run-test-jvm-data-shard88.zip"],
- srcs: ["*88-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 88 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?88-*/**/*", "??88-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard88",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard88-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard88.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard89",
+ name: "art-run-test-jvm-data-shard89-tmp",
out: ["art-run-test-jvm-data-shard89.zip"],
- srcs: ["*89-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 89 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?89-*/**/*", "??89-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard89",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard89-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard89.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard90",
+ name: "art-run-test-jvm-data-shard90-tmp",
out: ["art-run-test-jvm-data-shard90.zip"],
- srcs: ["*90-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 90 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?90-*/**/*", "??90-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard90",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard90-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard90.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard91",
+ name: "art-run-test-jvm-data-shard91-tmp",
out: ["art-run-test-jvm-data-shard91.zip"],
- srcs: ["*91-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 91 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?91-*/**/*", "??91-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard91",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard91-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard91.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard92",
+ name: "art-run-test-jvm-data-shard92-tmp",
out: ["art-run-test-jvm-data-shard92.zip"],
- srcs: ["*92-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 92 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?92-*/**/*", "??92-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard92",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard92-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard92.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard93",
+ name: "art-run-test-jvm-data-shard93-tmp",
out: ["art-run-test-jvm-data-shard93.zip"],
- srcs: ["*93-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 93 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?93-*/**/*", "??93-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard93",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard93-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard93.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard94",
+ name: "art-run-test-jvm-data-shard94-tmp",
out: ["art-run-test-jvm-data-shard94.zip"],
- srcs: ["*94-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 94 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?94-*/**/*", "??94-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard94",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard94-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard94.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard95",
+ name: "art-run-test-jvm-data-shard95-tmp",
out: ["art-run-test-jvm-data-shard95.zip"],
- srcs: ["*95-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 95 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?95-*/**/*", "??95-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard95",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard95-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard95.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard96",
+ name: "art-run-test-jvm-data-shard96-tmp",
out: ["art-run-test-jvm-data-shard96.zip"],
- srcs: ["*96-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 96 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?96-*/**/*", "??96-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard96",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard96-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard96.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard97",
+ name: "art-run-test-jvm-data-shard97-tmp",
out: ["art-run-test-jvm-data-shard97.zip"],
- srcs: ["*97-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 97 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?97-*/**/*", "??97-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard97",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard97-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard97.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard98",
+ name: "art-run-test-jvm-data-shard98-tmp",
out: ["art-run-test-jvm-data-shard98.zip"],
- srcs: ["*98-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 98 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?98-*/**/*", "??98-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard98",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard98-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard98.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard99",
+ name: "art-run-test-jvm-data-shard99-tmp",
out: ["art-run-test-jvm-data-shard99.zip"],
- srcs: ["*99-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 99 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?99-*/**/*", "??99-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard99",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard99-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard99.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-merged",
- defaults: ["art-run-test-data-defaults"],
+ name: "art-run-test-jvm-data-shardHiddenApi-tmp",
+ out: ["art-run-test-jvm-data-shardHiddenApi.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode jvm " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shardHiddenApi",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shardHiddenApi-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shardHiddenApi.zip",
+}
+
+genrule_defaults {
+ name: "art-run-test-jvm-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode jvm " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+java_genrule {
+ name: "art-run-test-jvm-data-merged-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
out: ["art-run-test-jvm-data-merged.zip"],
srcs: [
- ":art-run-test-jvm-data-shard00",
- ":art-run-test-jvm-data-shard01",
- ":art-run-test-jvm-data-shard02",
- ":art-run-test-jvm-data-shard03",
- ":art-run-test-jvm-data-shard04",
- ":art-run-test-jvm-data-shard05",
- ":art-run-test-jvm-data-shard06",
- ":art-run-test-jvm-data-shard07",
- ":art-run-test-jvm-data-shard08",
- ":art-run-test-jvm-data-shard09",
- ":art-run-test-jvm-data-shard10",
- ":art-run-test-jvm-data-shard11",
- ":art-run-test-jvm-data-shard12",
- ":art-run-test-jvm-data-shard13",
- ":art-run-test-jvm-data-shard14",
- ":art-run-test-jvm-data-shard15",
- ":art-run-test-jvm-data-shard16",
- ":art-run-test-jvm-data-shard17",
- ":art-run-test-jvm-data-shard18",
- ":art-run-test-jvm-data-shard19",
- ":art-run-test-jvm-data-shard20",
- ":art-run-test-jvm-data-shard21",
- ":art-run-test-jvm-data-shard22",
- ":art-run-test-jvm-data-shard23",
- ":art-run-test-jvm-data-shard24",
- ":art-run-test-jvm-data-shard25",
- ":art-run-test-jvm-data-shard26",
- ":art-run-test-jvm-data-shard27",
- ":art-run-test-jvm-data-shard28",
- ":art-run-test-jvm-data-shard29",
- ":art-run-test-jvm-data-shard30",
- ":art-run-test-jvm-data-shard31",
- ":art-run-test-jvm-data-shard32",
- ":art-run-test-jvm-data-shard33",
- ":art-run-test-jvm-data-shard34",
- ":art-run-test-jvm-data-shard35",
- ":art-run-test-jvm-data-shard36",
- ":art-run-test-jvm-data-shard37",
- ":art-run-test-jvm-data-shard38",
- ":art-run-test-jvm-data-shard39",
- ":art-run-test-jvm-data-shard40",
- ":art-run-test-jvm-data-shard41",
- ":art-run-test-jvm-data-shard42",
- ":art-run-test-jvm-data-shard43",
- ":art-run-test-jvm-data-shard44",
- ":art-run-test-jvm-data-shard45",
- ":art-run-test-jvm-data-shard46",
- ":art-run-test-jvm-data-shard47",
- ":art-run-test-jvm-data-shard48",
- ":art-run-test-jvm-data-shard49",
- ":art-run-test-jvm-data-shard50",
- ":art-run-test-jvm-data-shard51",
- ":art-run-test-jvm-data-shard52",
- ":art-run-test-jvm-data-shard53",
- ":art-run-test-jvm-data-shard54",
- ":art-run-test-jvm-data-shard55",
- ":art-run-test-jvm-data-shard56",
- ":art-run-test-jvm-data-shard57",
- ":art-run-test-jvm-data-shard58",
- ":art-run-test-jvm-data-shard59",
- ":art-run-test-jvm-data-shard60",
- ":art-run-test-jvm-data-shard61",
- ":art-run-test-jvm-data-shard62",
- ":art-run-test-jvm-data-shard63",
- ":art-run-test-jvm-data-shard64",
- ":art-run-test-jvm-data-shard65",
- ":art-run-test-jvm-data-shard66",
- ":art-run-test-jvm-data-shard67",
- ":art-run-test-jvm-data-shard68",
- ":art-run-test-jvm-data-shard69",
- ":art-run-test-jvm-data-shard70",
- ":art-run-test-jvm-data-shard71",
- ":art-run-test-jvm-data-shard72",
- ":art-run-test-jvm-data-shard73",
- ":art-run-test-jvm-data-shard74",
- ":art-run-test-jvm-data-shard75",
- ":art-run-test-jvm-data-shard76",
- ":art-run-test-jvm-data-shard77",
- ":art-run-test-jvm-data-shard78",
- ":art-run-test-jvm-data-shard79",
- ":art-run-test-jvm-data-shard80",
- ":art-run-test-jvm-data-shard81",
- ":art-run-test-jvm-data-shard82",
- ":art-run-test-jvm-data-shard83",
- ":art-run-test-jvm-data-shard84",
- ":art-run-test-jvm-data-shard85",
- ":art-run-test-jvm-data-shard86",
- ":art-run-test-jvm-data-shard87",
- ":art-run-test-jvm-data-shard88",
- ":art-run-test-jvm-data-shard89",
- ":art-run-test-jvm-data-shard90",
- ":art-run-test-jvm-data-shard91",
- ":art-run-test-jvm-data-shard92",
- ":art-run-test-jvm-data-shard93",
- ":art-run-test-jvm-data-shard94",
- ":art-run-test-jvm-data-shard95",
- ":art-run-test-jvm-data-shard96",
- ":art-run-test-jvm-data-shard97",
- ":art-run-test-jvm-data-shard98",
- ":art-run-test-jvm-data-shard99",
+ ":art-run-test-jvm-data-shard00-tmp",
+ ":art-run-test-jvm-data-shard01-tmp",
+ ":art-run-test-jvm-data-shard02-tmp",
+ ":art-run-test-jvm-data-shard03-tmp",
+ ":art-run-test-jvm-data-shard04-tmp",
+ ":art-run-test-jvm-data-shard05-tmp",
+ ":art-run-test-jvm-data-shard06-tmp",
+ ":art-run-test-jvm-data-shard07-tmp",
+ ":art-run-test-jvm-data-shard08-tmp",
+ ":art-run-test-jvm-data-shard09-tmp",
+ ":art-run-test-jvm-data-shard10-tmp",
+ ":art-run-test-jvm-data-shard11-tmp",
+ ":art-run-test-jvm-data-shard12-tmp",
+ ":art-run-test-jvm-data-shard13-tmp",
+ ":art-run-test-jvm-data-shard14-tmp",
+ ":art-run-test-jvm-data-shard15-tmp",
+ ":art-run-test-jvm-data-shard16-tmp",
+ ":art-run-test-jvm-data-shard17-tmp",
+ ":art-run-test-jvm-data-shard18-tmp",
+ ":art-run-test-jvm-data-shard19-tmp",
+ ":art-run-test-jvm-data-shard20-tmp",
+ ":art-run-test-jvm-data-shard21-tmp",
+ ":art-run-test-jvm-data-shard22-tmp",
+ ":art-run-test-jvm-data-shard23-tmp",
+ ":art-run-test-jvm-data-shard24-tmp",
+ ":art-run-test-jvm-data-shard25-tmp",
+ ":art-run-test-jvm-data-shard26-tmp",
+ ":art-run-test-jvm-data-shard27-tmp",
+ ":art-run-test-jvm-data-shard28-tmp",
+ ":art-run-test-jvm-data-shard29-tmp",
+ ":art-run-test-jvm-data-shard30-tmp",
+ ":art-run-test-jvm-data-shard31-tmp",
+ ":art-run-test-jvm-data-shard32-tmp",
+ ":art-run-test-jvm-data-shard33-tmp",
+ ":art-run-test-jvm-data-shard34-tmp",
+ ":art-run-test-jvm-data-shard35-tmp",
+ ":art-run-test-jvm-data-shard36-tmp",
+ ":art-run-test-jvm-data-shard37-tmp",
+ ":art-run-test-jvm-data-shard38-tmp",
+ ":art-run-test-jvm-data-shard39-tmp",
+ ":art-run-test-jvm-data-shard40-tmp",
+ ":art-run-test-jvm-data-shard41-tmp",
+ ":art-run-test-jvm-data-shard42-tmp",
+ ":art-run-test-jvm-data-shard43-tmp",
+ ":art-run-test-jvm-data-shard44-tmp",
+ ":art-run-test-jvm-data-shard45-tmp",
+ ":art-run-test-jvm-data-shard46-tmp",
+ ":art-run-test-jvm-data-shard47-tmp",
+ ":art-run-test-jvm-data-shard48-tmp",
+ ":art-run-test-jvm-data-shard49-tmp",
+ ":art-run-test-jvm-data-shard50-tmp",
+ ":art-run-test-jvm-data-shard51-tmp",
+ ":art-run-test-jvm-data-shard52-tmp",
+ ":art-run-test-jvm-data-shard53-tmp",
+ ":art-run-test-jvm-data-shard54-tmp",
+ ":art-run-test-jvm-data-shard55-tmp",
+ ":art-run-test-jvm-data-shard56-tmp",
+ ":art-run-test-jvm-data-shard57-tmp",
+ ":art-run-test-jvm-data-shard58-tmp",
+ ":art-run-test-jvm-data-shard59-tmp",
+ ":art-run-test-jvm-data-shard60-tmp",
+ ":art-run-test-jvm-data-shard61-tmp",
+ ":art-run-test-jvm-data-shard62-tmp",
+ ":art-run-test-jvm-data-shard63-tmp",
+ ":art-run-test-jvm-data-shard64-tmp",
+ ":art-run-test-jvm-data-shard65-tmp",
+ ":art-run-test-jvm-data-shard66-tmp",
+ ":art-run-test-jvm-data-shard67-tmp",
+ ":art-run-test-jvm-data-shard68-tmp",
+ ":art-run-test-jvm-data-shard69-tmp",
+ ":art-run-test-jvm-data-shard70-tmp",
+ ":art-run-test-jvm-data-shard71-tmp",
+ ":art-run-test-jvm-data-shard72-tmp",
+ ":art-run-test-jvm-data-shard73-tmp",
+ ":art-run-test-jvm-data-shard74-tmp",
+ ":art-run-test-jvm-data-shard75-tmp",
+ ":art-run-test-jvm-data-shard76-tmp",
+ ":art-run-test-jvm-data-shard77-tmp",
+ ":art-run-test-jvm-data-shard78-tmp",
+ ":art-run-test-jvm-data-shard79-tmp",
+ ":art-run-test-jvm-data-shard80-tmp",
+ ":art-run-test-jvm-data-shard81-tmp",
+ ":art-run-test-jvm-data-shard82-tmp",
+ ":art-run-test-jvm-data-shard83-tmp",
+ ":art-run-test-jvm-data-shard84-tmp",
+ ":art-run-test-jvm-data-shard85-tmp",
+ ":art-run-test-jvm-data-shard86-tmp",
+ ":art-run-test-jvm-data-shard87-tmp",
+ ":art-run-test-jvm-data-shard88-tmp",
+ ":art-run-test-jvm-data-shard89-tmp",
+ ":art-run-test-jvm-data-shard90-tmp",
+ ":art-run-test-jvm-data-shard91-tmp",
+ ":art-run-test-jvm-data-shard92-tmp",
+ ":art-run-test-jvm-data-shard93-tmp",
+ ":art-run-test-jvm-data-shard94-tmp",
+ ":art-run-test-jvm-data-shard95-tmp",
+ ":art-run-test-jvm-data-shard96-tmp",
+ ":art-run-test-jvm-data-shard97-tmp",
+ ":art-run-test-jvm-data-shard98-tmp",
+ ":art-run-test-jvm-data-shard99-tmp",
+ ":art-run-test-jvm-data-shardHiddenApi-tmp",
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-merged",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-merged-tmp",
+ required: [
+ "art-run-test-jvm-data-shard00",
+ "art-run-test-jvm-data-shard01",
+ "art-run-test-jvm-data-shard02",
+ "art-run-test-jvm-data-shard03",
+ "art-run-test-jvm-data-shard04",
+ "art-run-test-jvm-data-shard05",
+ "art-run-test-jvm-data-shard06",
+ "art-run-test-jvm-data-shard07",
+ "art-run-test-jvm-data-shard08",
+ "art-run-test-jvm-data-shard09",
+ "art-run-test-jvm-data-shard10",
+ "art-run-test-jvm-data-shard11",
+ "art-run-test-jvm-data-shard12",
+ "art-run-test-jvm-data-shard13",
+ "art-run-test-jvm-data-shard14",
+ "art-run-test-jvm-data-shard15",
+ "art-run-test-jvm-data-shard16",
+ "art-run-test-jvm-data-shard17",
+ "art-run-test-jvm-data-shard18",
+ "art-run-test-jvm-data-shard19",
+ "art-run-test-jvm-data-shard20",
+ "art-run-test-jvm-data-shard21",
+ "art-run-test-jvm-data-shard22",
+ "art-run-test-jvm-data-shard23",
+ "art-run-test-jvm-data-shard24",
+ "art-run-test-jvm-data-shard25",
+ "art-run-test-jvm-data-shard26",
+ "art-run-test-jvm-data-shard27",
+ "art-run-test-jvm-data-shard28",
+ "art-run-test-jvm-data-shard29",
+ "art-run-test-jvm-data-shard30",
+ "art-run-test-jvm-data-shard31",
+ "art-run-test-jvm-data-shard32",
+ "art-run-test-jvm-data-shard33",
+ "art-run-test-jvm-data-shard34",
+ "art-run-test-jvm-data-shard35",
+ "art-run-test-jvm-data-shard36",
+ "art-run-test-jvm-data-shard37",
+ "art-run-test-jvm-data-shard38",
+ "art-run-test-jvm-data-shard39",
+ "art-run-test-jvm-data-shard40",
+ "art-run-test-jvm-data-shard41",
+ "art-run-test-jvm-data-shard42",
+ "art-run-test-jvm-data-shard43",
+ "art-run-test-jvm-data-shard44",
+ "art-run-test-jvm-data-shard45",
+ "art-run-test-jvm-data-shard46",
+ "art-run-test-jvm-data-shard47",
+ "art-run-test-jvm-data-shard48",
+ "art-run-test-jvm-data-shard49",
+ "art-run-test-jvm-data-shard50",
+ "art-run-test-jvm-data-shard51",
+ "art-run-test-jvm-data-shard52",
+ "art-run-test-jvm-data-shard53",
+ "art-run-test-jvm-data-shard54",
+ "art-run-test-jvm-data-shard55",
+ "art-run-test-jvm-data-shard56",
+ "art-run-test-jvm-data-shard57",
+ "art-run-test-jvm-data-shard58",
+ "art-run-test-jvm-data-shard59",
+ "art-run-test-jvm-data-shard60",
+ "art-run-test-jvm-data-shard61",
+ "art-run-test-jvm-data-shard62",
+ "art-run-test-jvm-data-shard63",
+ "art-run-test-jvm-data-shard64",
+ "art-run-test-jvm-data-shard65",
+ "art-run-test-jvm-data-shard66",
+ "art-run-test-jvm-data-shard67",
+ "art-run-test-jvm-data-shard68",
+ "art-run-test-jvm-data-shard69",
+ "art-run-test-jvm-data-shard70",
+ "art-run-test-jvm-data-shard71",
+ "art-run-test-jvm-data-shard72",
+ "art-run-test-jvm-data-shard73",
+ "art-run-test-jvm-data-shard74",
+ "art-run-test-jvm-data-shard75",
+ "art-run-test-jvm-data-shard76",
+ "art-run-test-jvm-data-shard77",
+ "art-run-test-jvm-data-shard78",
+ "art-run-test-jvm-data-shard79",
+ "art-run-test-jvm-data-shard80",
+ "art-run-test-jvm-data-shard81",
+ "art-run-test-jvm-data-shard82",
+ "art-run-test-jvm-data-shard83",
+ "art-run-test-jvm-data-shard84",
+ "art-run-test-jvm-data-shard85",
+ "art-run-test-jvm-data-shard86",
+ "art-run-test-jvm-data-shard87",
+ "art-run-test-jvm-data-shard88",
+ "art-run-test-jvm-data-shard89",
+ "art-run-test-jvm-data-shard90",
+ "art-run-test-jvm-data-shard91",
+ "art-run-test-jvm-data-shard92",
+ "art-run-test-jvm-data-shard93",
+ "art-run-test-jvm-data-shard94",
+ "art-run-test-jvm-data-shard95",
+ "art-run-test-jvm-data-shard96",
+ "art-run-test-jvm-data-shard97",
+ "art-run-test-jvm-data-shard98",
+ "art-run-test-jvm-data-shard99",
+ "art-run-test-jvm-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-merged.zip",
+}
+
+// Phony target used to build all shards
+java_genrule {
+ name: "art-run-test-jvm-data-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["art-run-test-jvm-data.txt"],
+ srcs: [
+ ":art-run-test-jvm-data-shard00-tmp",
+ ":art-run-test-jvm-data-shard01-tmp",
+ ":art-run-test-jvm-data-shard02-tmp",
+ ":art-run-test-jvm-data-shard03-tmp",
+ ":art-run-test-jvm-data-shard04-tmp",
+ ":art-run-test-jvm-data-shard05-tmp",
+ ":art-run-test-jvm-data-shard06-tmp",
+ ":art-run-test-jvm-data-shard07-tmp",
+ ":art-run-test-jvm-data-shard08-tmp",
+ ":art-run-test-jvm-data-shard09-tmp",
+ ":art-run-test-jvm-data-shard10-tmp",
+ ":art-run-test-jvm-data-shard11-tmp",
+ ":art-run-test-jvm-data-shard12-tmp",
+ ":art-run-test-jvm-data-shard13-tmp",
+ ":art-run-test-jvm-data-shard14-tmp",
+ ":art-run-test-jvm-data-shard15-tmp",
+ ":art-run-test-jvm-data-shard16-tmp",
+ ":art-run-test-jvm-data-shard17-tmp",
+ ":art-run-test-jvm-data-shard18-tmp",
+ ":art-run-test-jvm-data-shard19-tmp",
+ ":art-run-test-jvm-data-shard20-tmp",
+ ":art-run-test-jvm-data-shard21-tmp",
+ ":art-run-test-jvm-data-shard22-tmp",
+ ":art-run-test-jvm-data-shard23-tmp",
+ ":art-run-test-jvm-data-shard24-tmp",
+ ":art-run-test-jvm-data-shard25-tmp",
+ ":art-run-test-jvm-data-shard26-tmp",
+ ":art-run-test-jvm-data-shard27-tmp",
+ ":art-run-test-jvm-data-shard28-tmp",
+ ":art-run-test-jvm-data-shard29-tmp",
+ ":art-run-test-jvm-data-shard30-tmp",
+ ":art-run-test-jvm-data-shard31-tmp",
+ ":art-run-test-jvm-data-shard32-tmp",
+ ":art-run-test-jvm-data-shard33-tmp",
+ ":art-run-test-jvm-data-shard34-tmp",
+ ":art-run-test-jvm-data-shard35-tmp",
+ ":art-run-test-jvm-data-shard36-tmp",
+ ":art-run-test-jvm-data-shard37-tmp",
+ ":art-run-test-jvm-data-shard38-tmp",
+ ":art-run-test-jvm-data-shard39-tmp",
+ ":art-run-test-jvm-data-shard40-tmp",
+ ":art-run-test-jvm-data-shard41-tmp",
+ ":art-run-test-jvm-data-shard42-tmp",
+ ":art-run-test-jvm-data-shard43-tmp",
+ ":art-run-test-jvm-data-shard44-tmp",
+ ":art-run-test-jvm-data-shard45-tmp",
+ ":art-run-test-jvm-data-shard46-tmp",
+ ":art-run-test-jvm-data-shard47-tmp",
+ ":art-run-test-jvm-data-shard48-tmp",
+ ":art-run-test-jvm-data-shard49-tmp",
+ ":art-run-test-jvm-data-shard50-tmp",
+ ":art-run-test-jvm-data-shard51-tmp",
+ ":art-run-test-jvm-data-shard52-tmp",
+ ":art-run-test-jvm-data-shard53-tmp",
+ ":art-run-test-jvm-data-shard54-tmp",
+ ":art-run-test-jvm-data-shard55-tmp",
+ ":art-run-test-jvm-data-shard56-tmp",
+ ":art-run-test-jvm-data-shard57-tmp",
+ ":art-run-test-jvm-data-shard58-tmp",
+ ":art-run-test-jvm-data-shard59-tmp",
+ ":art-run-test-jvm-data-shard60-tmp",
+ ":art-run-test-jvm-data-shard61-tmp",
+ ":art-run-test-jvm-data-shard62-tmp",
+ ":art-run-test-jvm-data-shard63-tmp",
+ ":art-run-test-jvm-data-shard64-tmp",
+ ":art-run-test-jvm-data-shard65-tmp",
+ ":art-run-test-jvm-data-shard66-tmp",
+ ":art-run-test-jvm-data-shard67-tmp",
+ ":art-run-test-jvm-data-shard68-tmp",
+ ":art-run-test-jvm-data-shard69-tmp",
+ ":art-run-test-jvm-data-shard70-tmp",
+ ":art-run-test-jvm-data-shard71-tmp",
+ ":art-run-test-jvm-data-shard72-tmp",
+ ":art-run-test-jvm-data-shard73-tmp",
+ ":art-run-test-jvm-data-shard74-tmp",
+ ":art-run-test-jvm-data-shard75-tmp",
+ ":art-run-test-jvm-data-shard76-tmp",
+ ":art-run-test-jvm-data-shard77-tmp",
+ ":art-run-test-jvm-data-shard78-tmp",
+ ":art-run-test-jvm-data-shard79-tmp",
+ ":art-run-test-jvm-data-shard80-tmp",
+ ":art-run-test-jvm-data-shard81-tmp",
+ ":art-run-test-jvm-data-shard82-tmp",
+ ":art-run-test-jvm-data-shard83-tmp",
+ ":art-run-test-jvm-data-shard84-tmp",
+ ":art-run-test-jvm-data-shard85-tmp",
+ ":art-run-test-jvm-data-shard86-tmp",
+ ":art-run-test-jvm-data-shard87-tmp",
+ ":art-run-test-jvm-data-shard88-tmp",
+ ":art-run-test-jvm-data-shard89-tmp",
+ ":art-run-test-jvm-data-shard90-tmp",
+ ":art-run-test-jvm-data-shard91-tmp",
+ ":art-run-test-jvm-data-shard92-tmp",
+ ":art-run-test-jvm-data-shard93-tmp",
+ ":art-run-test-jvm-data-shard94-tmp",
+ ":art-run-test-jvm-data-shard95-tmp",
+ ":art-run-test-jvm-data-shard96-tmp",
+ ":art-run-test-jvm-data-shard97-tmp",
+ ":art-run-test-jvm-data-shard98-tmp",
+ ":art-run-test-jvm-data-shard99-tmp",
+ ":art-run-test-jvm-data-shardHiddenApi-tmp",
+ ],
+ cmd: "echo $(in) > $(out)",
+}
+
+// Phony target used to install all shards
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-tmp",
+ required: [
+ "art-run-test-jvm-data-shard00",
+ "art-run-test-jvm-data-shard01",
+ "art-run-test-jvm-data-shard02",
+ "art-run-test-jvm-data-shard03",
+ "art-run-test-jvm-data-shard04",
+ "art-run-test-jvm-data-shard05",
+ "art-run-test-jvm-data-shard06",
+ "art-run-test-jvm-data-shard07",
+ "art-run-test-jvm-data-shard08",
+ "art-run-test-jvm-data-shard09",
+ "art-run-test-jvm-data-shard10",
+ "art-run-test-jvm-data-shard11",
+ "art-run-test-jvm-data-shard12",
+ "art-run-test-jvm-data-shard13",
+ "art-run-test-jvm-data-shard14",
+ "art-run-test-jvm-data-shard15",
+ "art-run-test-jvm-data-shard16",
+ "art-run-test-jvm-data-shard17",
+ "art-run-test-jvm-data-shard18",
+ "art-run-test-jvm-data-shard19",
+ "art-run-test-jvm-data-shard20",
+ "art-run-test-jvm-data-shard21",
+ "art-run-test-jvm-data-shard22",
+ "art-run-test-jvm-data-shard23",
+ "art-run-test-jvm-data-shard24",
+ "art-run-test-jvm-data-shard25",
+ "art-run-test-jvm-data-shard26",
+ "art-run-test-jvm-data-shard27",
+ "art-run-test-jvm-data-shard28",
+ "art-run-test-jvm-data-shard29",
+ "art-run-test-jvm-data-shard30",
+ "art-run-test-jvm-data-shard31",
+ "art-run-test-jvm-data-shard32",
+ "art-run-test-jvm-data-shard33",
+ "art-run-test-jvm-data-shard34",
+ "art-run-test-jvm-data-shard35",
+ "art-run-test-jvm-data-shard36",
+ "art-run-test-jvm-data-shard37",
+ "art-run-test-jvm-data-shard38",
+ "art-run-test-jvm-data-shard39",
+ "art-run-test-jvm-data-shard40",
+ "art-run-test-jvm-data-shard41",
+ "art-run-test-jvm-data-shard42",
+ "art-run-test-jvm-data-shard43",
+ "art-run-test-jvm-data-shard44",
+ "art-run-test-jvm-data-shard45",
+ "art-run-test-jvm-data-shard46",
+ "art-run-test-jvm-data-shard47",
+ "art-run-test-jvm-data-shard48",
+ "art-run-test-jvm-data-shard49",
+ "art-run-test-jvm-data-shard50",
+ "art-run-test-jvm-data-shard51",
+ "art-run-test-jvm-data-shard52",
+ "art-run-test-jvm-data-shard53",
+ "art-run-test-jvm-data-shard54",
+ "art-run-test-jvm-data-shard55",
+ "art-run-test-jvm-data-shard56",
+ "art-run-test-jvm-data-shard57",
+ "art-run-test-jvm-data-shard58",
+ "art-run-test-jvm-data-shard59",
+ "art-run-test-jvm-data-shard60",
+ "art-run-test-jvm-data-shard61",
+ "art-run-test-jvm-data-shard62",
+ "art-run-test-jvm-data-shard63",
+ "art-run-test-jvm-data-shard64",
+ "art-run-test-jvm-data-shard65",
+ "art-run-test-jvm-data-shard66",
+ "art-run-test-jvm-data-shard67",
+ "art-run-test-jvm-data-shard68",
+ "art-run-test-jvm-data-shard69",
+ "art-run-test-jvm-data-shard70",
+ "art-run-test-jvm-data-shard71",
+ "art-run-test-jvm-data-shard72",
+ "art-run-test-jvm-data-shard73",
+ "art-run-test-jvm-data-shard74",
+ "art-run-test-jvm-data-shard75",
+ "art-run-test-jvm-data-shard76",
+ "art-run-test-jvm-data-shard77",
+ "art-run-test-jvm-data-shard78",
+ "art-run-test-jvm-data-shard79",
+ "art-run-test-jvm-data-shard80",
+ "art-run-test-jvm-data-shard81",
+ "art-run-test-jvm-data-shard82",
+ "art-run-test-jvm-data-shard83",
+ "art-run-test-jvm-data-shard84",
+ "art-run-test-jvm-data-shard85",
+ "art-run-test-jvm-data-shard86",
+ "art-run-test-jvm-data-shard87",
+ "art-run-test-jvm-data-shard88",
+ "art-run-test-jvm-data-shard89",
+ "art-run-test-jvm-data-shard90",
+ "art-run-test-jvm-data-shard91",
+ "art-run-test-jvm-data-shard92",
+ "art-run-test-jvm-data-shard93",
+ "art-run-test-jvm-data-shard94",
+ "art-run-test-jvm-data-shard95",
+ "art-run-test-jvm-data-shard96",
+ "art-run-test-jvm-data-shard97",
+ "art-run-test-jvm-data-shard98",
+ "art-run-test-jvm-data-shard99",
+ "art-run-test-jvm-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data.txt",
+}
diff --git a/test/Android.run-test.bp.py b/test/Android.run-test.bp.py
index a051a1d..d1e3027 100755
--- a/test/Android.run-test.bp.py
+++ b/test/Android.run-test.bp.py
@@ -35,27 +35,140 @@
names.append(name)
f.write(textwrap.dedent("""
java_genrule {{
- name: "{name}",
+ name: "{name}-tmp",
out: ["{name}.zip"],
- srcs: ["*{shard}-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode {mode} --shard {shard} " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?{shard}-*/**/*", "??{shard}-*/**/*"],
+ defaults: ["art-run-test-{mode}-data-defaults"],
+ }}
+
+ // Install in the output directory to make it accessible for tests.
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ sub_dir: "art",
+ filename: "{name}.zip",
}}
""".format(name=name, mode=mode, shard=shard)))
- srcs = ("\n"+" "*8).join('":{}",'.format(n) for n in names)
+
+ # Build all hiddenapi tests in their own shard.
+ # This removes the dependency on hiddenapi from all other shards,
+ # which in turn removes dependency on ART C++ source code.
+ name = "art-run-test-{mode}-data-shardHiddenApi".format(mode=mode)
+ names.append(name)
f.write(textwrap.dedent("""
java_genrule {{
- name: "art-run-test-{mode}-data-merged",
- defaults: ["art-run-test-data-defaults"],
- out: ["art-run-test-{mode}-data-merged.zip"],
+ name: "{name}-tmp",
+ out: ["{name}.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-{mode}-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode {mode} " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+ }}
+
+ // Install in the output directory to make it accessible for tests.
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ sub_dir: "art",
+ filename: "{name}.zip",
+ }}
+ """.format(name=name, mode=mode)))
+
+ f.write(textwrap.dedent("""
+ genrule_defaults {{
+ name: "art-run-test-{mode}-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode {mode} " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+ }}
+ """).format(mode=mode))
+
+ name = "art-run-test-{mode}-data-merged".format(mode=mode)
+ srcs = ("\n"+" "*8).join('":{}-tmp",'.format(n) for n in names)
+ deps = ("\n"+" "*8).join('"{}",'.format(n) for n in names)
+ f.write(textwrap.dedent("""
+ java_genrule {{
+ name: "{name}-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
+ out: ["{name}.zip"],
srcs: [
{srcs}
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}}
- """).format(mode=mode, srcs=srcs))
+
+ // Install in the output directory to make it accessible for tests.
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ required: [
+ {deps}
+ ],
+ sub_dir: "art",
+ filename: "{name}.zip",
+ }}
+ """).format(name=name, srcs=srcs, deps=deps))
+
+ name = "art-run-test-{mode}-data".format(mode=mode)
+ srcs = ("\n"+" "*8).join('":{}-tmp",'.format(n) for n in names)
+ deps = ("\n"+" "*8).join('"{}",'.format(n) for n in names)
+ f.write(textwrap.dedent("""
+ // Phony target used to build all shards
+ java_genrule {{
+ name: "{name}-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["{name}.txt"],
+ srcs: [
+ {srcs}
+ ],
+ cmd: "echo $(in) > $(out)",
+ }}
+
+ // Phony target used to install all shards
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ required: [
+ {deps}
+ ],
+ sub_dir: "art",
+ filename: "{name}.txt",
+ }}
+ """).format(name=name, srcs=srcs, deps=deps))
if __name__ == "__main__":
main()
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ea305ce..9dec0c5 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -48,14 +48,10 @@
# Also need signal_dumper.
ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += signal_dumper-target
-ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += art-run-test-target-data
-
# All tests require the host executables. The tests also depend on the core images, but on
# specific version depending on the compiler.
ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
- art-run-test-host-data \
- art-run-test-jvm-data \
$(HOST_OUT_EXECUTABLES)/hprof-conv \
$(HOST_OUT_EXECUTABLES)/signal_dumper \
$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \
@@ -104,6 +100,9 @@
.PHONY: test-art-host-run-test-dependencies
test-art-run-test-dependencies : test-art-host-run-test-dependencies
+test-art-jvm-run-test-dependencies : test-art-host-run-test-dependencies
+.PHONY: test-art-jvm-run-test-dependencies
+
test-art-target-run-test-dependencies :
.PHONY: test-art-target-run-test-dependencies
test-art-run-test-dependencies : test-art-target-run-test-dependencies
diff --git a/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java b/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java
index e4862bd..ef6b4ef 100644
--- a/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java
+++ b/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java
@@ -30,6 +30,7 @@
}
}
+@SuppressWarnings("LockOnBoxedPrimitive")
class AccessPublicMethodFromParent {
public void foo(Integer i) {
i.notify();
@@ -64,6 +65,7 @@
}
}
+@SuppressWarnings("LockOnBoxedPrimitive")
class AccessNonPublicMethodFromParent {
public void foo(Integer i) {
i.notifyAll();
diff --git a/test/README.arm_fvp.md b/test/README.arm_fvp.md
index 47ef715..5fc3c1b 100644
--- a/test/README.arm_fvp.md
+++ b/test/README.arm_fvp.md
@@ -1,165 +1,65 @@
-# Build and Run ART tests on ARM FVP
+# Testing ART on a model (QEMU or Arm FVP)
-This document describes how to build and run an Android system image targeting
-the ARM Fixed Virtual Platform and to use it as a target platform for running
-ART tests via ADB.
+This document describes how to test ART on a model - QEMU or the ARM Fixed Virtual Platform.
-This instruction was checked to be working for the AOSP master tree on
-2021-01-13; the up-to-date instruction on how to build the kernel and firmware
-could be found here: device/generic/goldfish/fvpbase/README.md.
+It covers steps on how to build and run an Android system image targeting a model
+and to use it as a target platform for running ART tests via ADB in chroot mode. The guide
+covers both QEMU and the ARM Fixed Virtual Platform; the setup is very similar.
-## Configuring and Building AOSP
+More information on QEMU and Arm FVP could be found in
+{AOSP}/device/generic/goldfish/fvpbase/README.md.
-First, an AOSP image should be configured and built, including the kernel and
-firmware.
+One would need two AOSP trees for this setup:
+ - a full stable (tagged) tree - to be used to build AOSP image for the model.
+ - android-13.0.0_r12 was tested successfully to run QEMU:
+ ```repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r12```
+ - a full or minimal tree - the one to be tested as part of ART test run.
-### Generating build system configs
+## Setting up the QEMU/Arm FVP
+
+Once a full AOSP tree is downloaded, please follow the instructions in
+${AOSP}/device/generic/goldfish/fvpbase/README.md; they should cover:
+ - fetching, configuring and building the model.
+ - building AOSP image for it.
+ - launching the model.
+
+Once the model is started and reachable via adb, ART tests could be run.
+
+Notes:
+ - fvp_mini lunch target should be used as we don't need graphics to run ART tests.
+ - 'Running the image in QEMU' mentions that a special commit should be checked out for QEMU
+ for GUI runs. Actually it is recommended to use it even for non-GUI runs (fvp_mini).
+
+### Running the Arm FVP with SVE enabled
+
+To test SVE on Arm FVP, one extra step is needed when following the instructions above;
+for QEMU run this is not needed. When launching the model some extra cmdline options should
+be provided for 'run_model':
```
-cd $AOSP
-
-. build/envsetup.sh
-# fvp_mini target is used as we don't need a GUI for ART tests.
-lunch fvp_mini-eng
-
-# This is expected to fail; it generates all the build rules files.
-m
-```
-
-### Building the kernel
-
-```
-cd $SOME_DIRECTORY_OUTSIDE_AOSP
-
-mkdir android-kernel-mainline
-cd android-kernel-mainline
-repo init -u https://android.googlesource.com/kernel/manifest -b common-android-mainline
-repo sync
-BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
-BUILD_CONFIG=common-modules/virtual-device/build.config.fvp build/build.sh
-```
-
-The resulting kernel image and DTB (Device Tree Binary) must then be copied into
-the product output directory:
-
-```
-cp out/android-mainline/dist/Image $ANDROID_PRODUCT_OUT/kernel
-cp out/android-mainline/dist/fvp-base-revc.dtb out/android-mainline/dist/initramfs.img $ANDROID_PRODUCT_OUT/
-```
-
-### Building the firmware (ARM Trusted Firmware and U-Boot)
-
-First, install ``dtc``, the device tree compiler. On Debian, this is in the
-``device-tree-compiler`` package.
-
-```
-sudo apt-get install device-tree-compiler
-```
-
-Then run:
-
-```
-mkdir platform
-cd platform
-repo init -u https://git.linaro.org/landing-teams/working/arm/manifest.git -m pinned-uboot.xml -b 20.01
-repo sync
-
-# The included copy of U-Boot is incompatible with this version of AOSP, switch to a recent upstream checkout.
-cd u-boot
-git fetch https://gitlab.denx.de/u-boot/u-boot.git/ master
-git checkout 18b9c98024ec89e00a57707f07ff6ada06089d26
-cd ..
-
-mkdir -p tools/gcc
-cd tools/gcc
-wget https://releases.linaro.org/components/toolchain/binaries/6.2-2016.11/aarch64-linux-gnu/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gnu.tar.xz
-tar -xJf gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gnu.tar.xz
-cd ../..
-
-build-scripts/build-test-uboot.sh -p fvp all
-```
-
-These components must then be copied into the product output directory:
-
-```
-cp output/fvp/fvp-uboot/uboot/{bl1,fip}.bin $ANDROID_PRODUCT_OUT/
-```
-
-## Setting up the FVP model
-
-### Obtaining the model
-
-The public Arm FVP could be obtained from https://developer.arm.com/; one would
-need to create an account there and accept EULA to download and install it.
-A link for the latest version:
-
-https://developer.arm.com/tools-and-software/simulation-models/fixed-virtual-platforms/arm-ecosystem-models: "Armv8-A Base RevC AEM FVP"
-
-The AEMv8-A Base Platform FVP is a free of charge Fixed Virtual Platform of the
-latest Arm v8-A architecture features and has been validated with compatible
-Open Source software, which can be found on the reference open source software
-stacks page along with instructions for running the software
-
-### Running the model
-
-From a lunched environment:
-
-```
-export MODEL_PATH=/path/to/model/dir
-export MODEL_BIN=${MODEL_PATH}/models/Linux64_GCC-6.4/FVP_Base_RevC-2xAEMv8A
-./device/generic/goldfish/fvpbase/run_model
-```
-
-If any extra parameters are needed for the model (e.g. specifying plugins) they
-should be specified as cmdline options for 'run_model'. E.g. to run a model
-which support SVE:
-
-```
-export SVE_PLUGIN=${MODEL_PATH}/plugins/Linux64_GCC-6.4/ScalableVectorExtension.so
+export SVE_PLUGIN=${MODEL_PATH}/plugins/<os_and_toolchain>/ScalableVectorExtension.so
$ ./device/generic/goldfish/fvpbase/run_model --plugin ${SVE_PLUGIN} -C SVE.ScalableVectorExtension.veclen=2
```
Note: SVE vector length is passed in units of 64-bit blocks. So "2" would stand
for 128-bit vector length.
-The model will start and will have fully booted to shell in around 20 minutes
-(you will see "sys.boot_completed=1" in the log). It can be accessed as a
-regular device with adb:
+## Running ART test
-```
-adb connect localhost:5555
-```
+QEMU/FVP behaves as a regular adb device so running ART tests is possible using
+the standard chroot method described in test/README.chroot.md with an additional step,
+described below. A separate AOSP tree (not the one used for the model itself), should
+be used - full or minimal.
-To terminate the model, press ``Ctrl-] Ctrl-D`` to terminate the telnet
-connection.
-
-## Running ART test on FVP
-
-The model behaves as a regular adb device so running ART tests could be done using
-the standard chroot method described in test/README.chroot.md; the steps are
-also described below. A separate AOSP tree (not the one used for the model
-itself), should be used - full or minimal.
-
-Then the regular ART testing routine could be performed; the regular "lunch"
+Then the regular ART testing routine should be performed; the regular "lunch"
target ("armv8" and other targets, not "fvp-eng").
-
```
-export ART_TEST_CHROOT=/data/local/art-test-chroot
-export OVERRIDE_TARGET_FLATTEN_APEX=true
-export SOONG_ALLOW_MISSING_DEPENDENCIES=true
-export TARGET_BUILD_UNBUNDLED=true
+# Config the test run for QEMU/FVP.
export ART_TEST_RUN_ON_ARM_FVP=true
-. ./build/envsetup.sh
-lunch armv8-userdebug
-art/tools/buildbot-build.sh --target
-
-art/tools/buildbot-teardown-device.sh
-art/tools/buildbot-cleanup-device.sh
-art/tools/buildbot-setup-device.sh
-art/tools/buildbot-sync.sh
-
-art/test/testrunner/testrunner.py --target --64 --optimizing -j1
-
+# Build, sync ART tests to the model and run, see test/README.chroot.md.
```
+
+Note: ART scripts only support one adb device at a time. If you have other adb devices
+connected, use `export ANDROID_SERIAL=localhost:5555` to run scripts on QEMU/FVP."
diff --git a/test/README.chroot.md b/test/README.chroot.md
index 49b76ba..ce4670a 100644
--- a/test/README.chroot.md
+++ b/test/README.chroot.md
@@ -50,19 +50,62 @@
```
2. Set lunch target and ADB:
* With a minimal `aosp/master-art` tree:
- ```bash
- export SOONG_ALLOW_MISSING_DEPENDENCIES=true
- . ./build/envsetup.sh
- lunch armv8-eng # or arm_krait-eng for 32-bit ARM
- export PATH="$(pwd)/prebuilts/runtime:$PATH"
- export ADB="$ANDROID_BUILD_TOP/prebuilts/runtime/adb"
- ```
+ 1. Initialize the environment:
+ ```bash
+ export SOONG_ALLOW_MISSING_DEPENDENCIES=true
+ export BUILD_BROKEN_DISABLE_BAZEL=true
+ . ./build/envsetup.sh
+ ```
+ 2. Select a lunch target corresponding to the architecture you want to
+ build and test:
+ * For (32-bit) Arm:
+ ```bash
+ lunch arm_krait-eng
+ ```
+ * For (64-bit only) Arm64:
+ ```bash
+ lunch armv8-eng
+ ```
+ * For (32- and 64-bit) Arm64:
+ ```bash
+ lunch arm_v7_v8-eng
+ ```
+ * For (32-bit) Intel x86:
+ ```bash
+ lunch silvermont-eng
+ ```
+ 3. Set up the environment to use a pre-built ADB:
+ ```bash
+ export PATH="$(pwd)/prebuilts/runtime:$PATH"
+ export ADB="$ANDROID_BUILD_TOP/prebuilts/runtime/adb"
+ ```
* With a full Android (AOSP) `aosp/master` tree:
- ```bash
- . ./build/envsetup.sh
- lunch aosp_arm64-eng # or aosp_arm-eng for 32-bit ARM
- m adb
- ```
+ 1. Initialize the environment:
+ ```bash
+ . ./build/envsetup.sh
+ ```
+ 2. Select a lunch target corresponding to the architecture you want to
+ build and test:
+ * For (32-bit) Arm:
+ ```bash
+ lunch aosp_arm-eng
+ ```
+ * For (32- and 64-bit) Arm64:
+ ```bash
+ lunch aosp_arm64-eng
+ ```
+ * For (32-bit) Intel x86:
+ ```bash
+ lunch aosp_x86-eng
+ ```
+ * For (32- and 64-bit) Intel x86-64:
+ ```bash
+ lunch aosp_x86_64-eng
+ ```
+ 3. Build ADB:
+ ```bash
+ m adb
+ ```
3. Build ART and required dependencies:
```bash
art/tools/buildbot-build.sh --target
diff --git a/test/README.chroot_vm.md b/test/README.chroot_vm.md
new file mode 100644
index 0000000..2f163de
--- /dev/null
+++ b/test/README.chroot_vm.md
@@ -0,0 +1,74 @@
+# ART chroot-based testing on a Linux VM
+
+This doc describes how to set up a Linux VM and how to run ART tests on it.
+
+## Set up the VM
+
+Use script art/build/buildbot-vm.sh. It has various commands (actions) described
+below. First, set up some environment variables used by the script (change as
+you see fit):
+```
+export ART_TEST_SSH_USER=ubuntu
+export ART_TEST_SSH_HOST=localhost
+export ART_TEST_SSH_PORT=10001
+```
+Create the VM (download it and do some initial setup):
+```
+art/tools/buildbot-vm.sh create
+```
+Boot the VM (login is `$ART_TEST_SSH_USER`, password is `ubuntu`):
+```
+art/tools/buildbot-vm.sh boot
+```
+Configure SSH (enter `yes` to add VM to `known_hosts` and then the password):
+```
+art/tools/buildbot-vm.sh setup-ssh
+```
+Now you have the shell (no need to enter password every time):
+```
+art/tools/buildbot-vm.sh connect
+```
+To power off the VM, do:
+```
+art/tools/buildbot-vm.sh quit
+```
+To speed up SSH access, set `UseDNS no` in /etc/ssh/sshd_config on the VM (and
+apply other tweaks described in https://jrs-s.net/2017/07/01/slow-ssh-logins).
+
+# Run ART tests
+```
+This is done in the same way as you would run tests in chroot on device (except
+for a few extra environment variables):
+
+export ANDROID_SERIAL=nonexistent
+export ART_TEST_SSH_USER=ubuntu
+export ART_TEST_SSH_HOST=localhost
+export ART_TEST_SSH_PORT=10001
+export ART_TEST_ON_VM=true
+
+. ./build/envsetup.sh
+lunch armv8-eng # or aosp_riscv64-userdebug, etc.
+art/tools/buildbot-build.sh --target # --installclean
+
+art/tools/buildbot-cleanup-device.sh
+
+# The following two steps can be skipped for faster iteration, but it doesn't
+# always track and update dependencies correctly (e.g. if only an assembly file
+# has been modified).
+art/tools/buildbot-setup-device.sh
+art/tools/buildbot-sync.sh
+
+art/test/run-test --chroot $ART_TEST_CHROOT --64 --interpreter -O 001-HelloWorld
+art/test.py --target -r --ndebug --no-image --64 --interpreter # specify tests
+art/tools/run-gtests.sh
+
+art/tools/buildbot-cleanup-device.sh
+```
+Both test.py and run-test scripts can be used. Tweak options as necessary.
+
+# Limitations
+
+Limitations are mostly related to the absence of system properties on the Linux.
+They are not really needed for ART tests, but they are used for test-related
+things, e.g. to find out if the tests should run in debug configuration (option
+`ro.debuggable`). Therefore debug configuration is currently broken.
diff --git a/test/README.md b/test/README.md
index da9a1f1..dc9e9b7 100644
--- a/test/README.md
+++ b/test/README.md
@@ -201,6 +201,17 @@
## Running one gtest on the build host
```sh
+$ m test-art-host-gtest-art_runtime_tests
+```
+
+Note: Although this is a build command, it actually builds the test with
+dependencies and runs the test.
+
+If you want to run the test with more options, use the following commands
+instead. Note that you need to run the test with the command above at least once
+before you run the commands below.
+
+```sh
$ find out/host/ -type f -name art_runtime_tests # Find the path of the test.
$ out/host/linux-x86/nativetest/art_runtime_tests/art_runtime_tests
```
diff --git a/test/art-gtests-target-chroot.xml b/test/art-gtests-target-chroot.xml
index 5fd76f8..88c45b7 100644
--- a/test/art-gtests-target-chroot.xml
+++ b/test/art-gtests-target-chroot.xml
@@ -37,7 +37,7 @@
<test class="com.android.tradefed.testtype.ArtGTest" >
<!-- TODO(b/147821328): These tests do not work since they need to write to /system -->
- <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantTest.SystemFrameworkDir" />
+ <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantBaseTest.SystemFrameworkDir" />
<option name="native-test-timeout" value="600000" />
<option name="native-test-device-path" value="/data/local/tmp/art-test-chroot/apex/com.android.art/bin/art" />
</test>
diff --git a/test/art-gtests-target-install-apex.xml b/test/art-gtests-target-install-apex.xml
index 240b441..b030f26 100644
--- a/test/art-gtests-target-install-apex.xml
+++ b/test/art-gtests-target-install-apex.xml
@@ -23,7 +23,7 @@
<test class="com.android.tradefed.testtype.GTest" >
<!-- TODO(b/147821328): These tests do not work since they need to write to /system -->
- <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantTest.SystemFrameworkDir" />
+ <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantBaseTest.SystemFrameworkDir" />
<option name="native-test-timeout" value="600000" /> <!-- 10 min -->
<option name="native-test-device-path" value="/apex/com.android.art/bin/art" />
</test>
diff --git a/test/art-gtests-target-standalone-template.xml b/test/art-gtests-target-standalone-template.xml
index cc2f76e..194c1e3 100644
--- a/test/art-gtests-target-standalone-template.xml
+++ b/test/art-gtests-target-standalone-template.xml
@@ -15,6 +15,8 @@
-->
<!-- Note: This test config file for {MODULE} is generated from a template. -->
<configuration description="Runs {MODULE}.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="{MODULE}->/data/local/tmp/{MODULE}/{MODULE}" />
diff --git a/test/buildfailures.json b/test/buildfailures.json
deleted file mode 100644
index c926298..0000000
--- a/test/buildfailures.json
+++ /dev/null
@@ -1,31 +0,0 @@
-[
- {
- "description": ["The following tests don't build on RI."],
- "tests": [
- "004-UnsafeTest",
- "030-bad-finalizer",
- "122-npe",
- "160-read-barrier-stress",
- "178-app-image-native-method",
- "612-jit-dex-cache",
- "613-inlining-dex-cache",
- "674-hiddenapi",
- "676-resolve-field-type",
- "689-zygote-jit-deopt",
- "692-vdex-inmem-loader",
- "692-vdex-secondary-loader",
- "693-vdex-inmem-loader-evict",
- "811-checker-invoke-super-secondary",
- "817-hiddenapi",
- "831-unverified-bcp",
- "1336-short-finalizer-timeout",
- "1339-dead-reference-safe",
- "2005-pause-all-redefine-multithreaded",
- "2040-huge-native-alloc",
- "2041-bad-cleaner",
- "2235-JdkUnsafeTest",
- "2239-varhandle-perf"
- ],
- "variant": "jvm"
- }
-]
diff --git a/test/common/gtest_main.cc b/test/common/gtest_main.cc
index 95dadcf..e491ffc 100644
--- a/test/common/gtest_main.cc
+++ b/test/common/gtest_main.cc
@@ -38,12 +38,6 @@
extern "C" __attribute__((visibility("default"))) __attribute__((weak)) void ArtTestGlobalInit();
int main(int argc, char** argv, char** envp) {
- // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
- // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
- // everything else. In case you want to see all messages, comment out the line.
- const char* log_tag_override = getenv("ART_GTEST_OVERRIDE_LOG_TAGS");
- setenv("ANDROID_LOG_TAGS", log_tag_override == nullptr ? "*:e" : log_tag_override, 1);
-
art::Locks::Init();
art::InitLogging(argv, art::Runtime::Abort);
art::MemMap::Init();
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 0b72612..46f3828 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <sys/resource.h>
#include "art_field.h"
#include "art_method-inl.h"
@@ -234,7 +235,7 @@
{
ScopedObjectAccess soa(self);
- if (Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
+ if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
std::string msg(method->PrettyMethod());
msg += ": is not safe to jit!";
ThrowIllegalStateException(msg.c_str());
@@ -276,7 +277,20 @@
// this before checking if we will execute JIT code in case the request
// is for an 'optimized' compilation.
jit->CompileMethod(method, self, kind, /*prejit=*/ false);
- } while (!code_cache->ContainsPc(method->GetEntryPointFromQuickCompiledCode()));
+ const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+ if (code_cache->ContainsPc(entry_point)) {
+ // If we're running baseline or not requesting optimized, we're good to go.
+ if (jit->GetJitCompiler()->IsBaselineCompiler() || kind != CompilationKind::kOptimized) {
+ break;
+ }
+ // If we're requesting optimized, check that we did get the method
+ // compiled optimized.
+ OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(entry_point);
+ if (!CodeInfo::IsBaseline(method_header->GetOptimizedCodeInfoPtr())) {
+ break;
+ }
+ }
+ } while (true);
}
extern "C" JNIEXPORT void JNICALL Java_Main_ensureMethodJitCompiled(JNIEnv*, jclass, jobject meth) {
@@ -438,4 +452,21 @@
return soa.Decode<mirror::Class>(c)->IsObsoleteObject();
}
+extern "C" JNIEXPORT void JNICALL Java_Main_forceInterpreterOnThread(JNIEnv* env,
+ jclass cls ATTRIBUTE_UNUSED) {
+ ScopedObjectAccess soa(env);
+ MutexLock thread_list_mu(soa.Self(), *Locks::thread_list_lock_);
+ soa.Self()->IncrementForceInterpreterCount();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setAsyncExceptionsThrown(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass cls ATTRIBUTE_UNUSED) {
+ Runtime::Current()->SetAsyncExceptionsThrown();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setRlimitNoFile(JNIEnv*, jclass, jint value) {
+ rlimit limit { static_cast<rlim_t>(value), static_cast<rlim_t>(value) };
+ setrlimit(RLIMIT_NOFILE, &limit);
+}
+
} // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index 79c7a36..33f31e2 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -195,7 +195,8 @@
if (stack_visitor->GetMethod() == nullptr ||
stack_visitor->GetMethod()->IsNative() ||
(stack_visitor->GetCurrentShadowFrame() == nullptr &&
- !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetCurrentQuickFramePc()))) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetOuterMethod(),
+ stack_visitor->GetCurrentQuickFramePc()))) {
return true;
}
result = soa.AddLocalReference<jobject>(stack_visitor->GetThisObject());
diff --git a/test/csuite-app-compile-launch.xml b/test/csuite-app-compile-launch.xml
index 890c242..4ff7098 100644
--- a/test/csuite-app-compile-launch.xml
+++ b/test/csuite-app-compile-launch.xml
@@ -20,6 +20,9 @@
</target_preparer>
<target_preparer class="com.android.compatibility.targetprep.CheckGmsPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- We will fail if any command in RunCommandTargetPreparer fails.
+ This is useful to know if we failed to compile a package. -->
+ <option name="throw-if-cmd-fail" value="true"/>
<option name="run-command" value="cmd package compile -m speed {package}"/>
<option name="run-command" value="input keyevent KEYCODE_WAKEUP"/>
<option name="run-command" value="input keyevent KEYCODE_MENU"/>
diff --git a/test/default_run.py b/test/default_run.py
new file mode 100755
index 0000000..e102311
--- /dev/null
+++ b/test/default_run.py
@@ -0,0 +1,1206 @@
+#
+# Copyright (C) 2022 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 sys, os, shutil, shlex, re, subprocess, glob
+from argparse import ArgumentParser, BooleanOptionalAction, Namespace
+from os import path
+from os.path import isfile, isdir, basename
+from subprocess import check_output, DEVNULL, PIPE, STDOUT
+from tempfile import NamedTemporaryFile
+from testrunner import env
+from typing import List
+
+COLOR = (os.environ.get("LUCI_CONTEXT") == None) # Disable colors on LUCI.
+COLOR_BLUE = '\033[94m' if COLOR else ''
+COLOR_GREEN = '\033[92m' if COLOR else ''
+COLOR_NORMAL = '\033[0m' if COLOR else ''
+COLOR_RED = '\033[91m' if COLOR else ''
+
+def parse_args(argv):
+ argp, opt_bool = ArgumentParser(), BooleanOptionalAction
+ argp.add_argument("--64", dest="is64", action="store_true")
+ argp.add_argument("--O", action="store_true")
+ argp.add_argument("--Xcompiler-option", default=[], action="append")
+ argp.add_argument("--add-libdir-argument", action="store_true")
+ argp.add_argument("--android-art-root", default="/apex/com.android.art")
+ argp.add_argument("--android-i18n-root", default="/apex/com.android.i18n")
+ argp.add_argument("--android-log-tags", default="*:i")
+ argp.add_argument("--android-root", default="/system")
+ argp.add_argument("--android-runtime-option", default=[], action="append")
+ argp.add_argument("--android-tzdata-root", default="/apex/com.android.tzdata")
+ argp.add_argument("--app-image", default=True, action=opt_bool)
+ argp.add_argument("--baseline", action="store_true")
+ argp.add_argument("--bionic", action="store_true")
+ argp.add_argument("--boot", default="")
+ argp.add_argument("--chroot", default="")
+ argp.add_argument("--compact-dex-level")
+ argp.add_argument("--compiler-only-option", default=[], action="append")
+ argp.add_argument("--create-runner", action="store_true")
+ argp.add_argument("--diff-min-log-tag", default="E")
+ argp.add_argument("--debug", action="store_true")
+ argp.add_argument("--debug-agent")
+ argp.add_argument("--debug-wrap-agent", action="store_true")
+ argp.add_argument("--dex2oat-dm", action="store_true")
+ argp.add_argument(
+ "--dex2oat-rt-timeout", type=int,
+ default=360) # The *hard* timeout. 6 min.
+ argp.add_argument(
+ "--dex2oat-timeout", type=int, default=300) # The "soft" timeout. 5 min.
+ argp.add_argument("--dry-run", action="store_true")
+ argp.add_argument("--experimental", default=[], action="append")
+ argp.add_argument("--external-log-tags", action="store_true")
+ argp.add_argument("--gc-stress", action="store_true")
+ argp.add_argument("--gdb", action="store_true")
+ argp.add_argument("--gdb-arg", default=[], action="append")
+ argp.add_argument("--gdb-dex2oat", action="store_true")
+ argp.add_argument("--gdb-dex2oat-args", default=[], action="append")
+ argp.add_argument("--gdbserver", action="store_true")
+ argp.add_argument("--gdbserver-bin")
+ argp.add_argument("--gdbserver-port", default=":5039")
+ argp.add_argument("--host", action="store_true")
+ argp.add_argument("--image", default=True, action=opt_bool)
+ argp.add_argument("--instruction-set-features", default="")
+ argp.add_argument("--interpreter", action="store_true")
+ argp.add_argument("--invoke-with", default=[], action="append")
+ argp.add_argument("--jit", action="store_true")
+ argp.add_argument("--jvm", action="store_true")
+ argp.add_argument("--jvmti", action="store_true")
+ argp.add_argument("--jvmti-field-stress", action="store_true")
+ argp.add_argument("--jvmti-redefine-stress", action="store_true")
+ argp.add_argument("--jvmti-step-stress", action="store_true")
+ argp.add_argument("--jvmti-trace-stress", action="store_true")
+ argp.add_argument("--lib", default="")
+ argp.add_argument("--optimize", default=True, action=opt_bool)
+ argp.add_argument("--prebuild", default=True, action=opt_bool)
+ argp.add_argument("--profile", action="store_true")
+ argp.add_argument("--random-profile", action="store_true")
+ argp.add_argument("--relocate", default=False, action=opt_bool)
+ argp.add_argument("--runtime-dm", action="store_true")
+ argp.add_argument("--runtime-extracted-zipapex", default="")
+ argp.add_argument("--runtime-option", default=[], action="append")
+ argp.add_argument("--runtime-zipapex", default="")
+ argp.add_argument("--secondary", action="store_true")
+ argp.add_argument("--secondary-app-image", default=True, action=opt_bool)
+ argp.add_argument("--secondary-class-loader-context", default="")
+ argp.add_argument("--secondary-compilation", default=True, action=opt_bool)
+ argp.add_argument("--simpleperf", action="store_true")
+ argp.add_argument("--sync", action="store_true")
+ argp.add_argument("--testlib", default=[], action="append")
+ argp.add_argument("--timeout", default=0, type=int)
+ argp.add_argument("--vdex", action="store_true")
+ argp.add_argument("--vdex-arg", default=[], action="append")
+ argp.add_argument("--vdex-filter", default="")
+ argp.add_argument("--verify", default=True, action=opt_bool)
+ argp.add_argument("--verify-soft-fail", action="store_true")
+ argp.add_argument("--with-agent", default=[], action="append")
+ argp.add_argument("--zygote", action="store_true")
+ argp.add_argument("--test_args", default=[], action="append")
+ argp.add_argument("--stdout_file", default="")
+ argp.add_argument("--stderr_file", default="")
+ argp.add_argument("--main", default="Main")
+ argp.add_argument("--expected_exit_code", default=0)
+
+ # Python parser requires the format --key=--value, since without the equals symbol
+ # it looks like the required value has been omitted and there is just another flag.
+ # For example, '--args --foo --host --64' will become '--arg=--foo --host --64'
+ # because otherwise the --args is missing its value and --foo is unknown argument.
+ for i, arg in reversed(list(enumerate(argv))):
+ if arg in [
+ "--args", "--runtime-option", "--android-runtime-option",
+ "-Xcompiler-option", "--compiler-only-option"
+ ]:
+ argv[i] += "=" + argv.pop(i + 1)
+
+ # Accept single-dash arguments as if they were double-dash arguments.
+ # For exmpample, '-Xcompiler-option' becomes '--Xcompiler-option'
+ # became single-dash can be used only with single-letter arguments.
+ for i, arg in list(enumerate(argv)):
+ if arg.startswith("-") and not arg.startswith("--"):
+ argv[i] = "-" + arg
+ if arg == "--":
+ break
+
+ return argp.parse_args(argv)
+
+def get_target_arch(is64: bool) -> str:
+ # We may build for two arches. Get the one with the expected bitness.
+ arches = [a for a in [env.TARGET_ARCH, env.TARGET_2ND_ARCH] if a]
+ assert len(arches) > 0, "TARGET_ARCH/TARGET_2ND_ARCH not set"
+ if is64:
+ arches = [a for a in arches if a.endswith("64")]
+ assert len(arches) == 1, f"Can not find (unique) 64-bit arch in {arches}"
+ else:
+ arches = [a for a in arches if not a.endswith("64")]
+ assert len(arches) == 1, f"Can not find (unique) 32-bit arch in {arches}"
+ return arches[0]
+
+# Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
+# because that's what we use for compiling the boot.art image.
+# It may contain additional modules from TEST_CORE_JARS.
+bpath_modules = ("core-oj core-libart okhttp bouncycastle apache-xml core-icu4j"
+ " conscrypt")
+
+
+# Helper function to construct paths for apex modules (for both -Xbootclasspath and
+# -Xbootclasspath-location).
+def get_apex_bootclasspath_impl(bpath_prefix: str):
+ bpath_separator = ""
+ bpath = ""
+ bpath_jar = ""
+ for bpath_module in bpath_modules.split(" "):
+ apex_module = "com.android.art"
+ if bpath_module == "conscrypt":
+ apex_module = "com.android.conscrypt"
+ if bpath_module == "core-icu4j":
+ apex_module = "com.android.i18n"
+ bpath_jar = f"/apex/{apex_module}/javalib/{bpath_module}.jar"
+ bpath += f"{bpath_separator}{bpath_prefix}{bpath_jar}"
+ bpath_separator = ":"
+ return bpath
+
+
+# Gets a -Xbootclasspath paths with the apex modules.
+def get_apex_bootclasspath(host: bool):
+ bpath_prefix = ""
+
+ if host:
+ bpath_prefix = os.environ["ANDROID_HOST_OUT"]
+
+ return get_apex_bootclasspath_impl(bpath_prefix)
+
+
+# Gets a -Xbootclasspath-location paths with the apex modules.
+def get_apex_bootclasspath_locations(host: bool):
+ bpath_location_prefix = ""
+
+ if host:
+ ANDROID_BUILD_TOP=os.environ["ANDROID_BUILD_TOP"]
+ ANDROID_HOST_OUT=os.environ["ANDROID_HOST_OUT"]
+ if ANDROID_HOST_OUT[0:len(ANDROID_BUILD_TOP)+1] == f"{ANDROID_BUILD_TOP}/":
+ bpath_location_prefix=ANDROID_HOST_OUT[len(ANDROID_BUILD_TOP)+1:]
+ else:
+ print(f"ANDROID_BUILD_TOP/ is not a prefix of ANDROID_HOST_OUT"\
+ "\nANDROID_BUILD_TOP={ANDROID_BUILD_TOP}"\
+ "\nANDROID_HOST_OUT={ANDROID_HOST_OUT}")
+ sys.exit(1)
+
+ return get_apex_bootclasspath_impl(bpath_location_prefix)
+
+
+def default_run(ctx, args, **kwargs):
+ # Clone the args so we can modify them without affecting args in the caller.
+ args = Namespace(**vars(args))
+
+ # Overwrite args based on the named parameters.
+ # E.g. the caller can do `default_run(args, jvmti=True)` to modify args.jvmti.
+ for name, new_value in kwargs.items():
+ old_value = getattr(args, name)
+ assert isinstance(new_value, old_value.__class__), name + " should have type " + str(old_value.__class__)
+ if isinstance(old_value, list):
+ setattr(args, name, old_value + new_value) # Lists get merged.
+ else:
+ setattr(args, name, new_value)
+
+ ON_VM = os.environ.get("ART_TEST_ON_VM")
+
+ # Store copy of stdout&stderr of command in files so that we can diff them later.
+ # This may run under 'adb shell' so we are limited only to 'sh' shell feature set.
+ def tee(cmd: str):
+ # 'tee' works on stdout only, so we need to temporarily swap stdout and stderr.
+ cmd = f"({cmd} | tee -a {DEX_LOCATION}/{basename(args.stdout_file)}) 3>&1 1>&2 2>&3"
+ cmd = f"({cmd} | tee -a {DEX_LOCATION}/{basename(args.stderr_file)}) 3>&1 1>&2 2>&3"
+ return f"set -o pipefail; {cmd}" # Use exit code of first failure in piped command.
+
+ local_path = os.path.dirname(__file__)
+
+ ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP")
+ ANDROID_DATA = os.environ.get("ANDROID_DATA")
+ ANDROID_HOST_OUT = os.environ["ANDROID_HOST_OUT"]
+ ANDROID_LOG_TAGS = os.environ.get("ANDROID_LOG_TAGS", "")
+ ART_TIME_OUT_MULTIPLIER = int(os.environ.get("ART_TIME_OUT_MULTIPLIER", 1))
+ DEX2OAT = os.environ.get("DEX2OAT", "")
+ DEX_LOCATION = os.environ["DEX_LOCATION"]
+ JAVA = os.environ.get("JAVA")
+ OUT_DIR = os.environ.get("OUT_DIR")
+ PATH = os.environ.get("PATH", "")
+ SANITIZE_HOST = os.environ.get("SANITIZE_HOST", "")
+ TEST_NAME = os.environ["TEST_NAME"]
+ USE_EXRACTED_ZIPAPEX = os.environ.get("USE_EXRACTED_ZIPAPEX", "")
+
+ assert ANDROID_BUILD_TOP, "Did you forget to run `lunch`?"
+
+ ANDROID_ROOT = args.android_root
+ ANDROID_ART_ROOT = args.android_art_root
+ ANDROID_I18N_ROOT = args.android_i18n_root
+ ANDROID_TZDATA_ROOT = args.android_tzdata_root
+ ARCHITECTURES_32 = "(arm|x86|none)"
+ ARCHITECTURES_64 = "(arm64|x86_64|riscv64|none)"
+ ARCHITECTURES_PATTERN = ARCHITECTURES_32
+ GET_DEVICE_ISA_BITNESS_FLAG = "--32"
+ BOOT_IMAGE = args.boot
+ CHROOT = args.chroot
+ COMPILE_FLAGS = ""
+ DALVIKVM = "dalvikvm32"
+ DEBUGGER = "n"
+ WITH_AGENT = args.with_agent
+ DEBUGGER_AGENT = args.debug_agent
+ WRAP_DEBUGGER_AGENT = args.debug_wrap_agent
+ DEX2OAT_NDEBUG_BINARY = "dex2oat32"
+ DEX2OAT_DEBUG_BINARY = "dex2oatd32"
+ EXPERIMENTAL = args.experimental
+ FALSE_BIN = "false"
+ FLAGS = ""
+ ANDROID_FLAGS = ""
+ GDB = ""
+ GDB_ARGS = ""
+ GDB_DEX2OAT = ""
+ GDB_DEX2OAT_ARGS = ""
+ GDBSERVER_DEVICE = "gdbserver"
+ GDBSERVER_HOST = "gdbserver"
+ HAVE_IMAGE = args.image
+ HOST = args.host
+ BIONIC = args.bionic
+ CREATE_ANDROID_ROOT = False
+ USE_ZIPAPEX = (args.runtime_zipapex != "")
+ ZIPAPEX_LOC = args.runtime_zipapex
+ USE_EXTRACTED_ZIPAPEX = (args.runtime_extracted_zipapex != "")
+ EXTRACTED_ZIPAPEX_LOC = args.runtime_extracted_zipapex
+ INTERPRETER = args.interpreter
+ JIT = args.jit
+ INVOKE_WITH = " ".join(args.invoke_with)
+ USE_JVMTI = args.jvmti
+ IS_JVMTI_TEST = False
+ ADD_LIBDIR_ARGUMENTS = args.add_libdir_argument
+ SUFFIX64 = ""
+ ISA = "x86"
+ LIBRARY_DIRECTORY = "lib"
+ TEST_DIRECTORY = "nativetest"
+ MAIN = args.main
+ OPTIMIZE = args.optimize
+ PREBUILD = args.prebuild
+ RELOCATE = args.relocate
+ SECONDARY_DEX = ""
+ TIME_OUT = "timeout" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
+ TIMEOUT_DUMPER = "signal_dumper"
+ # Values in seconds.
+ TIME_OUT_EXTRA = 0
+ TIME_OUT_VALUE = args.timeout
+ USE_GDB = args.gdb
+ USE_GDBSERVER = args.gdbserver
+ GDBSERVER_PORT = args.gdbserver_port
+ USE_GDB_DEX2OAT = args.gdb_dex2oat
+ USE_JVM = args.jvm
+ VERIFY = "y" if args.verify else "n" # y=yes,n=no,s=softfail
+ ZYGOTE = ""
+ DEX_VERIFY = ""
+ INSTRUCTION_SET_FEATURES = args.instruction_set_features
+ ARGS = ""
+ VDEX_ARGS = ""
+ DRY_RUN = args.dry_run
+ TEST_VDEX = args.vdex
+ TEST_DEX2OAT_DM = args.dex2oat_dm
+ TEST_RUNTIME_DM = args.runtime_dm
+ TEST_IS_NDEBUG = args.O
+ APP_IMAGE = args.app_image
+ SECONDARY_APP_IMAGE = args.secondary_app_image
+ SECONDARY_CLASS_LOADER_CONTEXT = args.secondary_class_loader_context
+ SECONDARY_COMPILATION = args.secondary_compilation
+ JVMTI_STRESS = False
+ JVMTI_REDEFINE_STRESS = args.jvmti_redefine_stress
+ JVMTI_STEP_STRESS = args.jvmti_step_stress
+ JVMTI_FIELD_STRESS = args.jvmti_field_stress
+ JVMTI_TRACE_STRESS = args.jvmti_trace_stress
+ PROFILE = args.profile
+ RANDOM_PROFILE = args.random_profile
+ DEX2OAT_TIMEOUT = args.dex2oat_timeout
+ DEX2OAT_RT_TIMEOUT = args.dex2oat_rt_timeout
+ CREATE_RUNNER = args.create_runner
+ INT_OPTS = ""
+ SIMPLEPERF = args.simpleperf
+ DEBUGGER_OPTS = ""
+ JVM_VERIFY_ARG = ""
+ LIB = args.lib
+
+ # if True, run 'sync' before dalvikvm to make sure all files from
+ # build step (e.g. dex2oat) were finished writing.
+ SYNC_BEFORE_RUN = args.sync
+
+ # When running a debug build, we want to run with all checks.
+ ANDROID_FLAGS += " -XX:SlowDebug=true"
+ # The same for dex2oatd, both prebuild and runtime-driven.
+ ANDROID_FLAGS += (" -Xcompiler-option --runtime-arg -Xcompiler-option "
+ "-XX:SlowDebug=true")
+ COMPILER_FLAGS = " --runtime-arg -XX:SlowDebug=true"
+
+ # Let the compiler and runtime know that we are running tests.
+ COMPILE_FLAGS += " --compile-art-test"
+ ANDROID_FLAGS += " -Xcompiler-option --compile-art-test"
+
+ if USE_JVMTI:
+ IS_JVMTI_TEST = True
+ # Secondary images block some tested behavior.
+ SECONDARY_APP_IMAGE = False
+ if args.gc_stress:
+ # Give an extra 20 mins if we are gc-stress.
+ TIME_OUT_EXTRA += 1200
+ for arg in args.testlib:
+ ARGS += f" {arg}"
+ for arg in args.test_args:
+ ARGS += f" {arg}"
+ for arg in args.compiler_only_option:
+ COMPILE_FLAGS += f" {arg}"
+ for arg in args.Xcompiler_option:
+ FLAGS += f" -Xcompiler-option {arg}"
+ COMPILE_FLAGS += f" {arg}"
+ if args.secondary:
+ SECONDARY_DEX = f":{DEX_LOCATION}/{TEST_NAME}-ex.jar"
+ # Enable cfg-append to make sure we get the dump for both dex files.
+ # (otherwise the runtime compilation of the secondary dex will overwrite
+ # the dump of the first one).
+ FLAGS += " -Xcompiler-option --dump-cfg-append"
+ COMPILE_FLAGS += " --dump-cfg-append"
+ for arg in args.android_runtime_option:
+ ANDROID_FLAGS += f" {arg}"
+ for arg in args.runtime_option:
+ FLAGS += f" {arg}"
+ if arg == "-Xmethod-trace":
+ # Method tracing can slow some tests down a lot.
+ TIME_OUT_EXTRA += 1200
+ if args.compact_dex_level:
+ arg = args.compact_dex_level
+ COMPILE_FLAGS += f" --compact-dex-level={arg}"
+ if JVMTI_REDEFINE_STRESS:
+ # APP_IMAGE doesn't really work with jvmti redefine stress
+ SECONDARY_APP_IMAGE = False
+ JVMTI_STRESS = True
+ if JVMTI_REDEFINE_STRESS or JVMTI_STEP_STRESS or JVMTI_FIELD_STRESS or JVMTI_TRACE_STRESS:
+ USE_JVMTI = True
+ JVMTI_STRESS = True
+ if HOST:
+ ANDROID_ROOT = ANDROID_HOST_OUT
+ ANDROID_ART_ROOT = f"{ANDROID_HOST_OUT}/com.android.art"
+ ANDROID_I18N_ROOT = f"{ANDROID_HOST_OUT}/com.android.i18n"
+ ANDROID_TZDATA_ROOT = f"{ANDROID_HOST_OUT}/com.android.tzdata"
+ # On host, we default to using the symlink, as the PREFER_32BIT
+ # configuration is the only configuration building a 32bit version of
+ # dex2oat.
+ DEX2OAT_DEBUG_BINARY = "dex2oatd"
+ DEX2OAT_NDEBUG_BINARY = "dex2oat"
+ if BIONIC:
+ # We need to create an ANDROID_ROOT because currently we cannot create
+ # the frameworks/libcore with linux_bionic so we need to use the normal
+ # host ones which are in a different location.
+ CREATE_ANDROID_ROOT = True
+ if USE_ZIPAPEX:
+ # TODO (b/119942078): Currently apex does not support
+ # symlink_preferred_arch so we will not have a dex2oatd to execute and
+ # need to manually provide
+ # dex2oatd64.
+ DEX2OAT_DEBUG_BINARY = "dex2oatd64"
+ if WITH_AGENT:
+ USE_JVMTI = True
+ if DEBUGGER_AGENT:
+ DEBUGGER = "agent"
+ USE_JVMTI = True
+ TIME_OUT = "n"
+ if args.debug:
+ USE_JVMTI = True
+ DEBUGGER = "y"
+ TIME_OUT = "n"
+ if args.gdbserver_bin:
+ arg = args.gdbserver_bin
+ GDBSERVER_HOST = arg
+ GDBSERVER_DEVICE = arg
+ if args.gdbserver or args.gdb or USE_GDB_DEX2OAT:
+ TIME_OUT = "n"
+ for arg in args.gdb_arg:
+ GDB_ARGS += f" {arg}"
+ if args.gdb_dex2oat_args:
+ for arg in arg.split(";"):
+ GDB_DEX2OAT_ARGS += f"{arg} "
+ if args.zygote:
+ ZYGOTE = "-Xzygote"
+ print("Spawning from zygote")
+ if args.baseline:
+ FLAGS += " -Xcompiler-option --baseline"
+ COMPILE_FLAGS += " --baseline"
+ if args.verify_soft_fail:
+ VERIFY = "s"
+ if args.is64:
+ SUFFIX64 = "64"
+ ISA = "x86_64"
+ GDBSERVER_DEVICE = "gdbserver64"
+ DALVIKVM = "dalvikvm64"
+ LIBRARY_DIRECTORY = "lib64"
+ TEST_DIRECTORY = "nativetest64"
+ ARCHITECTURES_PATTERN = ARCHITECTURES_64
+ GET_DEVICE_ISA_BITNESS_FLAG = "--64"
+ DEX2OAT_NDEBUG_BINARY = "dex2oat64"
+ DEX2OAT_DEBUG_BINARY = "dex2oatd64"
+ if args.vdex_filter:
+ option = args.vdex_filter
+ VDEX_ARGS += f" --compiler-filter={option}"
+ if args.vdex_arg:
+ arg = args.vdex_arg
+ VDEX_ARGS += f" {arg}"
+
+# HACK: Force the use of `signal_dumper` on host.
+ if HOST or ON_VM:
+ TIME_OUT = "timeout"
+
+# If you change this, update the timeout in testrunner.py as well.
+ if not TIME_OUT_VALUE:
+ # 10 minutes is the default.
+ TIME_OUT_VALUE = 600
+
+ # For sanitized builds use a larger base.
+ # TODO: Consider sanitized target builds?
+ if SANITIZE_HOST != "":
+ TIME_OUT_VALUE = 1500 # 25 minutes.
+
+ TIME_OUT_VALUE += TIME_OUT_EXTRA
+
+# Escape hatch for slow hosts or devices. Accept an environment variable as a timeout factor.
+ if ART_TIME_OUT_MULTIPLIER:
+ TIME_OUT_VALUE *= ART_TIME_OUT_MULTIPLIER
+
+# The DEX_LOCATION with the chroot prefix, if any.
+ CHROOT_DEX_LOCATION = f"{CHROOT}{DEX_LOCATION}"
+
+ # If running on device, determine the ISA of the device.
+ if not HOST and not USE_JVM:
+ ISA = get_target_arch(args.is64)
+
+ if not USE_JVM:
+ FLAGS += f" {ANDROID_FLAGS}"
+ # we don't want to be trying to get adbconnections since the plugin might
+ # not have been built.
+ FLAGS += " -XjdwpProvider:none"
+ for feature in EXPERIMENTAL:
+ FLAGS += f" -Xexperimental:{feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:{feature}"
+ COMPILE_FLAGS = f"{COMPILE_FLAGS} --runtime-arg -Xexperimental:{feature}"
+
+ if CREATE_ANDROID_ROOT:
+ ANDROID_ROOT = f"{DEX_LOCATION}/android-root"
+
+ if ZYGOTE == "":
+ if OPTIMIZE:
+ if VERIFY == "y":
+ DEX_OPTIMIZE = "-Xdexopt:verified"
+ else:
+ DEX_OPTIMIZE = "-Xdexopt:all"
+ else:
+ DEX_OPTIMIZE = "-Xdexopt:none"
+
+ if VERIFY == "y":
+ JVM_VERIFY_ARG = "-Xverify:all"
+ elif VERIFY == "s":
+ JVM_VERIFY_ARG = "Xverify:all"
+ DEX_VERIFY = "-Xverify:softfail"
+ else: # VERIFY == "n"
+ DEX_VERIFY = "-Xverify:none"
+ JVM_VERIFY_ARG = "-Xverify:none"
+
+ if DEBUGGER == "y":
+ # Use this instead for ddms and connect by running 'ddms':
+ # DEBUGGER_OPTS="-XjdwpOptions=server=y,suspend=y -XjdwpProvider:adbconnection"
+ # TODO: add a separate --ddms option?
+
+ PORT = 12345
+ print("Waiting for jdb to connect:")
+ if not HOST:
+ print(f" adb forward tcp:{PORT} tcp:{PORT}")
+ print(f" jdb -attach localhost:{PORT}")
+ if not USE_JVM:
+ # Use the default libjdwp agent. Use --debug-agent to use a custom one.
+ DEBUGGER_OPTS = f"-agentpath:libjdwp.so=transport=dt_socket,address={PORT},server=y,suspend=y -XjdwpProvider:internal"
+ else:
+ DEBUGGER_OPTS = f"-agentlib:jdwp=transport=dt_socket,address={PORT},server=y,suspend=y"
+ elif DEBUGGER == "agent":
+ PORT = 12345
+ # TODO Support ddms connection and support target.
+ assert HOST, "--debug-agent not supported yet for target!"
+ AGENTPATH = DEBUGGER_AGENT
+ if WRAP_DEBUGGER_AGENT:
+ WRAPPROPS = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}/libwrapagentpropertiesd.so"
+ if TEST_IS_NDEBUG:
+ WRAPPROPS = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}/libwrapagentproperties.so"
+ AGENTPATH = f"{WRAPPROPS}={ANDROID_BUILD_TOP}/art/tools/libjdwp-compat.props,{AGENTPATH}"
+ print(f"Connect to localhost:{PORT}")
+ DEBUGGER_OPTS = f"-agentpath:{AGENTPATH}=transport=dt_socket,address={PORT},server=y,suspend=y"
+
+ for agent in WITH_AGENT:
+ FLAGS += f" -agentpath:{agent}"
+
+ if USE_JVMTI:
+ if not USE_JVM:
+ plugin = "libopenjdkjvmtid.so"
+ if TEST_IS_NDEBUG:
+ plugin = "libopenjdkjvmti.so"
+ # We used to add flags here that made the runtime debuggable but that is not
+ # needed anymore since the plugin can do it for us now.
+ FLAGS += f" -Xplugin:{plugin}"
+
+ # For jvmti tests, set the threshold of compilation to 1, so we jit early to
+ # provide better test coverage for jvmti + jit. This means we won't run
+ # the default --jit configuration but it is not too important test scenario for
+ # jvmti tests. This is art specific flag, so don't use it with jvm.
+ FLAGS += " -Xjitthreshold:1"
+
+# Add the libdir to the argv passed to the main function.
+ if ADD_LIBDIR_ARGUMENTS:
+ if HOST:
+ ARGS += f" {ANDROID_HOST_OUT}/{TEST_DIRECTORY}/"
+ else:
+ ARGS += f" /data/{TEST_DIRECTORY}/art/{ISA}/"
+ if IS_JVMTI_TEST:
+ agent = "libtiagentd.so"
+ lib = "tiagentd"
+ if TEST_IS_NDEBUG:
+ agent = "libtiagent.so"
+ lib = "tiagent"
+
+ ARGS += f" {lib}"
+ if USE_JVM:
+ FLAGS += f" -agentpath:{ANDROID_HOST_OUT}/nativetest64/{agent}={TEST_NAME},jvm"
+ else:
+ FLAGS += f" -agentpath:{agent}={TEST_NAME},art"
+
+ if JVMTI_STRESS:
+ agent = "libtistressd.so"
+ if TEST_IS_NDEBUG:
+ agent = "libtistress.so"
+
+ # Just give it a default start so we can always add ',' to it.
+ agent_args = "jvmti-stress"
+ if JVMTI_REDEFINE_STRESS:
+ # We really cannot do this on RI so don't both passing it in that case.
+ if not USE_JVM:
+ agent_args = f"{agent_args},redefine"
+ if JVMTI_FIELD_STRESS:
+ agent_args = f"{agent_args},field"
+ if JVMTI_STEP_STRESS:
+ agent_args = f"{agent_args},step"
+ if JVMTI_TRACE_STRESS:
+ agent_args = f"{agent_args},trace"
+ # In the future add onto this;
+ if USE_JVM:
+ FLAGS += f" -agentpath:{ANDROID_HOST_OUT}/nativetest64/{agent}={agent_args}"
+ else:
+ FLAGS += f" -agentpath:{agent}={agent_args}"
+
+ if USE_JVM:
+ ctx.export(
+ ANDROID_I18N_ROOT = ANDROID_I18N_ROOT,
+ DEX_LOCATION = DEX_LOCATION,
+ JAVA_HOME = os.environ["JAVA_HOME"],
+ LANG = "en_US.UTF-8", # Needed to enable unicode and make the output is deterministic.
+ LD_LIBRARY_PATH = f"{ANDROID_HOST_OUT}/lib64",
+ )
+ # Some jvmti tests are flaky without -Xint on the RI.
+ if IS_JVMTI_TEST:
+ FLAGS += " -Xint"
+ # Xmx is necessary since we don't pass down the ART flags to JVM.
+ # We pass the classes2 path whether it's used (src-multidex) or not.
+ cmdline = f"{JAVA} {DEBUGGER_OPTS} {JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 {FLAGS} {MAIN} {ARGS}"
+ ctx.run(tee(cmdline), expected_exit_code=args.expected_exit_code)
+ return
+
+ b_path = get_apex_bootclasspath(HOST)
+ b_path_locations = get_apex_bootclasspath_locations(HOST)
+
+ BCPEX = ""
+ if isfile(f"{TEST_NAME}-bcpex.jar"):
+ BCPEX = f":{DEX_LOCATION}/{TEST_NAME}-bcpex.jar"
+
+ # Pass down the bootclasspath
+ FLAGS += f" -Xbootclasspath:{b_path}{BCPEX}"
+ FLAGS += f" -Xbootclasspath-locations:{b_path_locations}{BCPEX}"
+ COMPILE_FLAGS += f" --runtime-arg -Xbootclasspath:{b_path}"
+ COMPILE_FLAGS += f" --runtime-arg -Xbootclasspath-locations:{b_path_locations}"
+
+ if not HAVE_IMAGE:
+ # Disable image dex2oat - this will forbid the runtime to patch or compile an image.
+ FLAGS += " -Xnoimage-dex2oat"
+
+ # We'll abuse a second flag here to test different behavior. If --relocate, use the
+ # existing image - relocation will fail as patching is disallowed. If --no-relocate,
+ # pass a non-existent image - compilation will fail as dex2oat is disallowed.
+ if not RELOCATE:
+ BOOT_IMAGE = "/system/non-existent/boot.art"
+ # App images cannot be generated without a boot image.
+ APP_IMAGE = False
+ DALVIKVM_BOOT_OPT = f"-Ximage:{BOOT_IMAGE}"
+
+ if USE_GDB_DEX2OAT:
+ assert HOST, "The --gdb-dex2oat option is not yet implemented for target."
+
+ assert not USE_GDBSERVER, "Not supported"
+ if USE_GDB:
+ if not HOST:
+ # We might not have any hostname resolution if we are using a chroot.
+ GDB = f"{GDBSERVER_DEVICE} --no-startup-with-shell 127.0.0.1{GDBSERVER_PORT}"
+ else:
+ GDB = "gdb"
+ GDB_ARGS += f" --args {DALVIKVM}"
+
+ if INTERPRETER:
+ INT_OPTS += " -Xint"
+
+ if JIT:
+ INT_OPTS += " -Xusejit:true"
+ else:
+ INT_OPTS += " -Xusejit:false"
+
+ if INTERPRETER or JIT:
+ if VERIFY == "y":
+ INT_OPTS += " -Xcompiler-option --compiler-filter=verify"
+ COMPILE_FLAGS += " --compiler-filter=verify"
+ elif VERIFY == "s":
+ INT_OPTS += " -Xcompiler-option --compiler-filter=extract"
+ COMPILE_FLAGS += " --compiler-filter=extract"
+ DEX_VERIFY = f"{DEX_VERIFY} -Xverify:softfail"
+ else: # VERIFY == "n"
+ INT_OPTS += " -Xcompiler-option --compiler-filter=assume-verified"
+ COMPILE_FLAGS += " --compiler-filter=assume-verified"
+ DEX_VERIFY = f"{DEX_VERIFY} -Xverify:none"
+
+ JNI_OPTS = "-Xjnigreflimit:512 -Xcheck:jni"
+
+ COMPILE_FLAGS += " --runtime-arg -Xnorelocate"
+ if RELOCATE:
+ FLAGS += " -Xrelocate"
+ else:
+ FLAGS += " -Xnorelocate"
+
+ if BIONIC and not ON_VM:
+ # This is the location that soong drops linux_bionic builds. Despite being
+ # called linux_bionic-x86 the build is actually amd64 (x86_64) only.
+ assert path.exists(f"{OUT_DIR}/soong/host/linux_bionic-x86"), (
+ "linux_bionic-x86 target doesn't seem to have been built!")
+ # Set TIMEOUT_DUMPER manually so it works even with apex's
+ TIMEOUT_DUMPER = f"{OUT_DIR}/soong/host/linux_bionic-x86/bin/signal_dumper"
+
+ # Prevent test from silently falling back to interpreter in no-prebuild mode. This happens
+ # when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking
+ # full path to dex, stripping leading '/', appending '@classes.vdex' and changing every
+ # remaining '/' into '@'.
+ if HOST:
+ max_filename_size = int(check_output(f"getconf NAME_MAX {DEX_LOCATION}", shell=True))
+ else:
+ # There is no getconf on device, fallback to standard value.
+ # See NAME_MAX in kernel <linux/limits.h>
+ max_filename_size = 255
+ # Compute VDEX_NAME.
+ DEX_LOCATION_STRIPPED = DEX_LOCATION.lstrip("/")
+ VDEX_NAME = f"{DEX_LOCATION_STRIPPED}@{TEST_NAME}.jar@classes.vdex".replace(
+ "/", "@")
+ assert len(VDEX_NAME) <= max_filename_size, "Dex location path too long"
+
+ if HOST:
+ # On host, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the `bin`
+ # directory under the "Android Root" (usually `out/host/linux-x86`).
+ #
+ # TODO(b/130295968): Adjust this if/when ART host artifacts are installed
+ # under the ART root (usually `out/host/linux-x86/com.android.art`).
+ ANDROID_ART_BIN_DIR = f"{ANDROID_ROOT}/bin"
+ else:
+ # On target, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the ART
+ # APEX's `bin` directory. This means the linker will observe the ART APEX
+ # linker configuration file (`/apex/com.android.art/etc/ld.config.txt`) for
+ # these binaries.
+ ANDROID_ART_BIN_DIR = f"{ANDROID_ART_ROOT}/bin"
+
+ profman_cmdline = "true"
+ dex2oat_cmdline = "true"
+ vdex_cmdline = "true"
+ dm_cmdline = "true"
+ mkdir_locations = f"{DEX_LOCATION}/dalvik-cache/{ISA}"
+ strip_cmdline = "true"
+ sync_cmdline = "true"
+ linkroot_cmdline = "true"
+ linkroot_overlay_cmdline = "true"
+ setupapex_cmdline = "true"
+ installapex_cmdline = "true"
+ installapex_test_cmdline = "true"
+
+ def linkdirs(host_out: str, root: str):
+ dirs = list(filter(os.path.isdir, glob.glob(os.path.join(host_out, "*"))))
+ # Also create a link for the boot image.
+ dirs.append(f"{ANDROID_HOST_OUT}/apex/art_boot_images")
+ return " && ".join(f"ln -sf {dir} {root}" for dir in dirs)
+
+ if CREATE_ANDROID_ROOT:
+ mkdir_locations += f" {ANDROID_ROOT}"
+ linkroot_cmdline = linkdirs(ANDROID_HOST_OUT, ANDROID_ROOT)
+ if BIONIC:
+ # TODO Make this overlay more generic.
+ linkroot_overlay_cmdline = linkdirs(
+ f"{OUT_DIR}/soong/host/linux_bionic-x86", ANDROID_ROOT)
+ # Replace the boot image to a location expected by the runtime.
+ DALVIKVM_BOOT_OPT = f"-Ximage:{ANDROID_ROOT}/art_boot_images/javalib/boot.art"
+
+ if USE_ZIPAPEX:
+ # TODO Currently this only works for linux_bionic zipapexes because those are
+ # stripped and so small enough that the ulimit doesn't kill us.
+ mkdir_locations += f" {DEX_LOCATION}/zipapex"
+ setupapex_cmdline = f"unzip -o -u {ZIPAPEX_LOC} apex_payload.zip -d {DEX_LOCATION}"
+ installapex_cmdline = f"unzip -o -u {DEX_LOCATION}/apex_payload.zip -d {DEX_LOCATION}/zipapex"
+ ANDROID_ART_BIN_DIR = f"{DEX_LOCATION}/zipapex/bin"
+ elif USE_EXTRACTED_ZIPAPEX:
+ # Just symlink the zipapex binaries
+ ANDROID_ART_BIN_DIR = f"{DEX_LOCATION}/zipapex/bin"
+ # Force since some tests manually run this file twice.
+ # If the {RUN} is executed multiple times we don't need to recreate the link
+ installapex_cmdline = f"ln -sfTv {EXTRACTED_ZIPAPEX_LOC} {DEX_LOCATION}/zipapex"
+
+ # PROFILE takes precedence over RANDOM_PROFILE, since PROFILE tests require a
+ # specific profile to run properly.
+ if PROFILE or RANDOM_PROFILE:
+ profman_cmdline = f"{ANDROID_ART_BIN_DIR}/profman \
+ --apk={DEX_LOCATION}/{TEST_NAME}.jar \
+ --dex-location={DEX_LOCATION}/{TEST_NAME}.jar"
+
+ if isfile(f"{TEST_NAME}-ex.jar") and SECONDARY_COMPILATION:
+ profman_cmdline = f"{profman_cmdline} \
+ --apk={DEX_LOCATION}/{TEST_NAME}-ex.jar \
+ --dex-location={DEX_LOCATION}/{TEST_NAME}-ex.jar"
+
+ COMPILE_FLAGS = f"{COMPILE_FLAGS} --profile-file={DEX_LOCATION}/{TEST_NAME}.prof"
+ FLAGS = f"{FLAGS} -Xcompiler-option --profile-file={DEX_LOCATION}/{TEST_NAME}.prof"
+ if PROFILE:
+ profman_cmdline = f"{profman_cmdline} --create-profile-from={DEX_LOCATION}/profile \
+ --reference-profile-file={DEX_LOCATION}/{TEST_NAME}.prof"
+
+ else:
+ profman_cmdline = f"{profman_cmdline} --generate-test-profile={DEX_LOCATION}/{TEST_NAME}.prof \
+ --generate-test-profile-seed=0"
+
+ def get_prebuilt_lldb_path():
+ CLANG_BASE = "prebuilts/clang/host"
+ CLANG_VERSION = check_output(
+ f"{ANDROID_BUILD_TOP}/build/soong/scripts/get_clang_version.py"
+ ).strip()
+ uname = check_output("uname -s", shell=True).strip()
+ if uname == "Darwin":
+ PREBUILT_NAME = "darwin-x86"
+ elif uname == "Linux":
+ PREBUILT_NAME = "linux-x86"
+ else:
+ print(
+ "Unknown host $(uname -s). Unsupported for debugging dex2oat with LLDB.",
+ file=sys.stderr)
+ return
+ CLANG_PREBUILT_HOST_PATH = f"{ANDROID_BUILD_TOP}/{CLANG_BASE}/{PREBUILT_NAME}/{CLANG_VERSION}"
+ # If the clang prebuilt directory exists and the reported clang version
+ # string does not, then it is likely that the clang version reported by the
+ # get_clang_version.py script does not match the expected directory name.
+ if isdir(f"{ANDROID_BUILD_TOP}/{CLANG_BASE}/{PREBUILT_NAME}"):
+ assert isdir(CLANG_PREBUILT_HOST_PATH), (
+ "The prebuilt clang directory exists, but the specific "
+ "clang\nversion reported by get_clang_version.py does not exist in "
+ "that path.\nPlease make sure that the reported clang version "
+ "resides in the\nprebuilt clang directory!")
+
+ # The lldb-server binary is a dependency of lldb.
+ os.environ[
+ "LLDB_DEBUGSERVER_PATH"] = f"{CLANG_PREBUILT_HOST_PATH}/runtimes_ndk_cxx/x86_64/lldb-server"
+
+ # Set the current terminfo directory to TERMINFO so that LLDB can read the
+ # termcap database.
+ terminfo = re.search("/.*/terminfo/", check_output("infocmp"))
+ if terminfo:
+ os.environ["TERMINFO"] = terminfo[0]
+
+ return f"{CLANG_PREBUILT_HOST_PATH}/bin/lldb.sh"
+
+ def write_dex2oat_cmdlines(name: str):
+ nonlocal dex2oat_cmdline, dm_cmdline, vdex_cmdline
+
+ class_loader_context = ""
+ enable_app_image = False
+ if APP_IMAGE:
+ enable_app_image = True
+
+ # If the name ends in -ex then this is a secondary dex file
+ if name.endswith("-ex"):
+ # Lazily realize the default value in case DEX_LOCATION/TEST_NAME change
+ nonlocal SECONDARY_CLASS_LOADER_CONTEXT
+ if SECONDARY_CLASS_LOADER_CONTEXT == "":
+ if SECONDARY_DEX == "":
+ # Tests without `--secondary` load the "-ex" jar in a separate PathClassLoader
+ # that is a child of the main PathClassLoader. If the class loader is constructed
+ # in any other way, the test needs to specify the secondary CLC explicitly.
+ SECONDARY_CLASS_LOADER_CONTEXT = f"PCL[];PCL[{DEX_LOCATION}/{TEST_NAME}.jar]"
+ else:
+ # Tests with `--secondary` load the `-ex` jar a part of the main PathClassLoader.
+ SECONDARY_CLASS_LOADER_CONTEXT = f"PCL[{DEX_LOCATION}/{TEST_NAME}.jar]"
+ class_loader_context = f"'--class-loader-context={SECONDARY_CLASS_LOADER_CONTEXT}'"
+ enable_app_image = enable_app_image and SECONDARY_APP_IMAGE
+
+ app_image = ""
+ if enable_app_image:
+ app_image = f"--app-image-file={DEX_LOCATION}/oat/{ISA}/{name}.art --resolve-startup-const-strings=true"
+
+ nonlocal GDB_DEX2OAT, GDB_DEX2OAT_ARGS
+ if USE_GDB_DEX2OAT:
+ prebuilt_lldb_path = get_prebuilt_lldb_path()
+ GDB_DEX2OAT = f"{prebuilt_lldb_path} -f"
+ GDB_DEX2OAT_ARGS += " -- "
+
+ dex2oat_binary = DEX2OAT_DEBUG_BINARY
+ if TEST_IS_NDEBUG:
+ dex2oat_binary = DEX2OAT_NDEBUG_BINARY
+ dex2oat_cmdline = f"{INVOKE_WITH} {GDB_DEX2OAT} \
+ {ANDROID_ART_BIN_DIR}/{dex2oat_binary} \
+ {GDB_DEX2OAT_ARGS} \
+ {COMPILE_FLAGS} \
+ --boot-image={BOOT_IMAGE} \
+ --dex-file={DEX_LOCATION}/{name}.jar \
+ --oat-file={DEX_LOCATION}/oat/{ISA}/{name}.odex \
+ {app_image} \
+ --generate-mini-debug-info \
+ --instruction-set={ISA} \
+ {class_loader_context}"
+
+ if INSTRUCTION_SET_FEATURES != "":
+ dex2oat_cmdline += f" --instruction-set-features={INSTRUCTION_SET_FEATURES}"
+
+ # Add in a timeout. This is important for testing the compilation/verification time of
+ # pathological cases. We do not append a timeout when debugging dex2oat because we
+ # do not want it to exit while debugging.
+ # Note: as we don't know how decent targets are (e.g., emulator), only do this on the host for
+ # now. We should try to improve this.
+ # The current value is rather arbitrary. run-tests should compile quickly.
+ # Watchdog timeout is in milliseconds so add 3 '0's to the dex2oat timeout.
+ if HOST and not USE_GDB_DEX2OAT:
+ # Use SIGRTMIN+2 to try to dump threads.
+ # Use -k 1m to SIGKILL it a minute later if it hasn't ended.
+ dex2oat_cmdline = f"timeout -k {DEX2OAT_TIMEOUT}s -s SIGRTMIN+2 {DEX2OAT_RT_TIMEOUT}s {dex2oat_cmdline} --watchdog-timeout={DEX2OAT_TIMEOUT}000"
+ if PROFILE or RANDOM_PROFILE:
+ vdex_cmdline = f"{dex2oat_cmdline} {VDEX_ARGS} --input-vdex={DEX_LOCATION}/oat/{ISA}/{name}.vdex --output-vdex={DEX_LOCATION}/oat/{ISA}/{name}.vdex"
+ elif TEST_VDEX:
+ if VDEX_ARGS == "":
+ # If no arguments need to be passed, just delete the odex file so that the runtime only picks up the vdex file.
+ vdex_cmdline = f"rm {DEX_LOCATION}/oat/{ISA}/{name}.odex"
+ else:
+ vdex_cmdline = f"{dex2oat_cmdline} {VDEX_ARGS} --compact-dex-level=none --input-vdex={DEX_LOCATION}/oat/{ISA}/{name}.vdex"
+ elif TEST_DEX2OAT_DM:
+ vdex_cmdline = f"{dex2oat_cmdline} {VDEX_ARGS} --dump-timings --dm-file={DEX_LOCATION}/oat/{ISA}/{name}.dm"
+ dex2oat_cmdline = f"{dex2oat_cmdline} --copy-dex-files=false --output-vdex={DEX_LOCATION}/oat/{ISA}/primary.vdex"
+ dm_cmdline = f"zip -qj {DEX_LOCATION}/oat/{ISA}/{name}.dm {DEX_LOCATION}/oat/{ISA}/primary.vdex"
+ elif TEST_RUNTIME_DM:
+ dex2oat_cmdline = f"{dex2oat_cmdline} --copy-dex-files=false --output-vdex={DEX_LOCATION}/oat/{ISA}/primary.vdex"
+ dm_cmdline = f"zip -qj {DEX_LOCATION}/{name}.dm {DEX_LOCATION}/oat/{ISA}/primary.vdex"
+
+# Enable mini-debug-info for JIT (if JIT is used).
+
+ FLAGS += " -Xcompiler-option --generate-mini-debug-info"
+
+ if PREBUILD:
+ mkdir_locations += f" {DEX_LOCATION}/oat/{ISA}"
+
+ # "Primary".
+ write_dex2oat_cmdlines(TEST_NAME)
+ dex2oat_cmdline = re.sub(" +", " ", dex2oat_cmdline)
+ dm_cmdline = re.sub(" +", " ", dm_cmdline)
+ vdex_cmdline = re.sub(" +", " ", vdex_cmdline)
+
+ # Enable mini-debug-info for JIT (if JIT is used).
+ FLAGS += " -Xcompiler-option --generate-mini-debug-info"
+
+ if isfile(f"{TEST_NAME}-ex.jar") and SECONDARY_COMPILATION:
+ # "Secondary" for test coverage.
+
+ # Store primary values.
+ base_dex2oat_cmdline = dex2oat_cmdline
+ base_dm_cmdline = dm_cmdline
+ base_vdex_cmdline = vdex_cmdline
+
+ write_dex2oat_cmdlines(f"{TEST_NAME}-ex")
+ dex2oat_cmdline = re.sub(" +", " ", dex2oat_cmdline)
+ dm_cmdline = re.sub(" +", " ", dm_cmdline)
+ vdex_cmdline = re.sub(" +", " ", vdex_cmdline)
+
+ # Concatenate.
+ dex2oat_cmdline = f"{base_dex2oat_cmdline} && {dex2oat_cmdline}"
+ dm_cmdline = base_dm_cmdline # Only use primary dm.
+ vdex_cmdline = f"{base_vdex_cmdline} && {vdex_cmdline}"
+
+ if SYNC_BEFORE_RUN:
+ sync_cmdline = "sync"
+
+ DALVIKVM_ISA_FEATURES_ARGS = ""
+ if INSTRUCTION_SET_FEATURES != "":
+ DALVIKVM_ISA_FEATURES_ARGS = f"-Xcompiler-option --instruction-set-features={INSTRUCTION_SET_FEATURES}"
+
+# java.io.tmpdir can only be set at launch time.
+ TMP_DIR_OPTION = ""
+ if not HOST:
+ TMP_DIR_OPTION = "-Djava.io.tmpdir=/data/local/tmp"
+
+# The build servers have an ancient version of bash so we cannot use @Q.
+ QUOTED_DALVIKVM_BOOT_OPT = shlex.quote(DALVIKVM_BOOT_OPT)
+
+ DALVIKVM_CLASSPATH = f"{DEX_LOCATION}/{TEST_NAME}.jar"
+ if isfile(f"{TEST_NAME}-aotex.jar"):
+ DALVIKVM_CLASSPATH = f"{DALVIKVM_CLASSPATH}:{DEX_LOCATION}/{TEST_NAME}-aotex.jar"
+ DALVIKVM_CLASSPATH = f"{DALVIKVM_CLASSPATH}{SECONDARY_DEX}"
+
+ # We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind.
+ # b/27185632
+ # b/24664297
+
+ dalvikvm_logger = ""
+ if ON_VM:
+ dalvikvm_logger = "-Xuse-stderr-logger"
+
+ dalvikvm_cmdline = f"{INVOKE_WITH} {GDB} {ANDROID_ART_BIN_DIR}/{DALVIKVM} \
+ {GDB_ARGS} \
+ {FLAGS} \
+ {DEX_VERIFY} \
+ -XXlib:{LIB} \
+ {DEX2OAT} \
+ {DALVIKVM_ISA_FEATURES_ARGS} \
+ {ZYGOTE} \
+ {JNI_OPTS} \
+ {INT_OPTS} \
+ {DEBUGGER_OPTS} \
+ {QUOTED_DALVIKVM_BOOT_OPT} \
+ {TMP_DIR_OPTION} \
+ {dalvikvm_logger} \
+ -XX:DumpNativeStackOnSigQuit:false \
+ -cp {DALVIKVM_CLASSPATH} {MAIN} {ARGS}"
+
+ if SIMPLEPERF:
+ dalvikvm_cmdline = f"simpleperf record {dalvikvm_cmdline} && simpleperf report"
+
+ def sanitize_dex2oat_cmdline(cmdline: str) -> str:
+ args = []
+ for arg in cmdline.split(" "):
+ if arg == "--class-loader-context=&":
+ arg = "--class-loader-context=\&"
+ args.append(arg)
+ return " ".join(args)
+
+ # Remove whitespace.
+ dex2oat_cmdline = sanitize_dex2oat_cmdline(dex2oat_cmdline)
+ dalvikvm_cmdline = re.sub(" +", " ", dalvikvm_cmdline)
+ dm_cmdline = re.sub(" +", " ", dm_cmdline)
+ vdex_cmdline = sanitize_dex2oat_cmdline(vdex_cmdline)
+ profman_cmdline = re.sub(" +", " ", profman_cmdline)
+
+ # Use an empty ASAN_OPTIONS to enable defaults.
+ # Note: this is required as envsetup right now exports detect_leaks=0.
+ RUN_TEST_ASAN_OPTIONS = ""
+
+ # Multiple shutdown leaks. b/38341789
+ if RUN_TEST_ASAN_OPTIONS != "":
+ RUN_TEST_ASAN_OPTIONS = f"{RUN_TEST_ASAN_OPTIONS}:"
+ RUN_TEST_ASAN_OPTIONS = f"{RUN_TEST_ASAN_OPTIONS}detect_leaks=0"
+
+ assert not args.external_log_tags, "Deprecated: use --android-log-tags=*:v"
+
+ ANDROID_LOG_TAGS = args.android_log_tags
+
+ def filter_output():
+ # Remove unwanted log messages from stderr before diffing with the expected output.
+ # NB: The unwanted log line can be interleaved in the middle of wanted stderr printf.
+ # In particular, unhandled exception is printed using several unterminated printfs.
+ ALL_LOG_TAGS = ["V", "D", "I", "W", "E", "F", "S"]
+ skip_tag_set = "|".join(ALL_LOG_TAGS[:ALL_LOG_TAGS.index(args.diff_min_log_tag.upper())])
+ skip_reg_exp = fr'[[:alnum:]]+ ({skip_tag_set}) #-# #:#:# [^\n]*\n'.replace('#', '[0-9]+')
+ ctx.run(fr"sed -i -z -E 's/{skip_reg_exp}//g' '{args.stderr_file}'")
+ if not HAVE_IMAGE:
+ message = "(Unable to open file|Could not create image space)"
+ ctx.run(fr"sed -i -E '/^dalvikvm(|32|64) E .* {message}/d' '{args.stderr_file}'")
+ if ANDROID_LOG_TAGS != "*:i" and "D" in skip_tag_set:
+ ctx.run(fr"sed -i -E '/^(Time zone|I18n) APEX ICU file found/d' '{args.stderr_file}'")
+ if ON_VM:
+ messages = "|".join([
+ "failed to connect to tombstoned",
+ "Failed to write stack traces to tombstoned",
+ "Failed to setpriority to :0"])
+ ctx.run(fr"sed -i -E '/({messages})/d' '{args.stderr_file}'")
+
+ if not HOST:
+ # Populate LD_LIBRARY_PATH.
+ LD_LIBRARY_PATH = ""
+ if ANDROID_ROOT != "/system":
+ # Current default installation is dalvikvm 64bits and dex2oat 32bits,
+ # so we can only use LD_LIBRARY_PATH when testing on a local
+ # installation.
+ LD_LIBRARY_PATH = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}"
+
+ # This adds libarttest(d).so to the default linker namespace when dalvikvm
+ # is run from /apex/com.android.art/bin. Since that namespace is essentially
+ # an alias for the com_android_art namespace, that gives libarttest(d).so
+ # full access to the internal ART libraries.
+ LD_LIBRARY_PATH = f"/data/{TEST_DIRECTORY}/com.android.art/lib{SUFFIX64}:{LD_LIBRARY_PATH}"
+ dlib = ("" if TEST_IS_NDEBUG else "d")
+ art_test_internal_libraries = [
+ f"libartagent{dlib}.so",
+ f"libarttest{dlib}.so",
+ f"libtiagent{dlib}.so",
+ f"libtistress{dlib}.so",
+ ]
+ NATIVELOADER_DEFAULT_NAMESPACE_LIBS = ":".join(art_test_internal_libraries)
+ dlib = ""
+ art_test_internal_libraries = []
+
+ if not ON_VM:
+ # Needed to access the test's Odex files.
+ LD_LIBRARY_PATH = f"{DEX_LOCATION}/oat/{ISA}:{LD_LIBRARY_PATH}"
+ # Needed to access the test's native libraries (see e.g. 674-hiddenapi,
+ # which generates `libhiddenapitest_*.so` libraries in `{DEX_LOCATION}`).
+ LD_LIBRARY_PATH = f"{DEX_LOCATION}:{LD_LIBRARY_PATH}"
+
+ # Prepend directories to the path on device.
+ PREPEND_TARGET_PATH = ANDROID_ART_BIN_DIR
+ if ANDROID_ROOT != "/system":
+ PREPEND_TARGET_PATH = f"{PREPEND_TARGET_PATH}:{ANDROID_ROOT}/bin"
+
+ timeout_dumper_cmd = ""
+
+ if TIMEOUT_DUMPER:
+ # Use "-l" to dump to logcat. That is convenience for the build bot crash symbolization.
+ # Use exit code 124 for toybox timeout (b/141007616).
+ timeout_dumper_cmd = f"{TIMEOUT_DUMPER} -l -s 15 -e 124"
+
+ timeout_prefix = ""
+ if TIME_OUT == "timeout":
+ # Add timeout command if time out is desired.
+ #
+ # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
+ # will induce a full thread dump before killing the process. To ensure any issues in
+ # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
+ # child.
+ # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
+ if ON_VM:
+ timeout_prefix = f"timeout -k 120s {TIME_OUT_VALUE}s"
+ else:
+ timeout_prefix = f"timeout --foreground -k 120s {TIME_OUT_VALUE}s {timeout_dumper_cmd}"
+
+ ctx.export(
+ ASAN_OPTIONS = RUN_TEST_ASAN_OPTIONS,
+ ANDROID_DATA = DEX_LOCATION,
+ DEX_LOCATION = DEX_LOCATION,
+ ANDROID_ROOT = ANDROID_ROOT,
+ ANDROID_I18N_ROOT = ANDROID_I18N_ROOT,
+ ANDROID_ART_ROOT = ANDROID_ART_ROOT,
+ ANDROID_TZDATA_ROOT = ANDROID_TZDATA_ROOT,
+ ANDROID_LOG_TAGS = ANDROID_LOG_TAGS,
+ LD_LIBRARY_PATH = LD_LIBRARY_PATH,
+ NATIVELOADER_DEFAULT_NAMESPACE_LIBS = NATIVELOADER_DEFAULT_NAMESPACE_LIBS,
+ PATH = f"{PREPEND_TARGET_PATH}:$PATH",
+ )
+
+ if USE_GDB or USE_GDBSERVER:
+ print(f"Forward {GDBSERVER_PORT} to local port and connect GDB")
+
+ ctx.run(f"rm -rf {DEX_LOCATION}/{{oat,dalvik-cache}}/ && mkdir -p {mkdir_locations}")
+ ctx.run(f"{profman_cmdline}")
+ ctx.run(f"{dex2oat_cmdline}", desc="Dex2oat")
+ ctx.run(f"{dm_cmdline}")
+ ctx.run(f"{vdex_cmdline}")
+ ctx.run(f"{strip_cmdline}")
+ ctx.run(f"{sync_cmdline}")
+ ctx.run(tee(f"{timeout_prefix} {dalvikvm_cmdline}"),
+ expected_exit_code=args.expected_exit_code, desc="DalvikVM")
+
+ if ON_VM:
+ filter_output()
+
+ else:
+ # Host run.
+ if USE_ZIPAPEX or USE_EXRACTED_ZIPAPEX:
+ # Put the zipapex files in front of the ld-library-path
+ LD_LIBRARY_PATH = f"{ANDROID_DATA}/zipapex/{LIBRARY_DIRECTORY}:{ANDROID_ROOT}/{TEST_DIRECTORY}"
+ else:
+ LD_LIBRARY_PATH = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}:{ANDROID_ROOT}/{TEST_DIRECTORY}"
+
+ ctx.export(
+ ANDROID_PRINTF_LOG = "brief",
+ ASAN_OPTIONS = RUN_TEST_ASAN_OPTIONS,
+ ANDROID_DATA = DEX_LOCATION,
+ DEX_LOCATION = DEX_LOCATION,
+ ANDROID_ROOT = ANDROID_ROOT,
+ ANDROID_I18N_ROOT = ANDROID_I18N_ROOT,
+ ANDROID_ART_ROOT = ANDROID_ART_ROOT,
+ ANDROID_TZDATA_ROOT = ANDROID_TZDATA_ROOT,
+ ANDROID_LOG_TAGS = ANDROID_LOG_TAGS,
+ LD_LIBRARY_PATH = LD_LIBRARY_PATH,
+ PATH = f"{PATH}:{ANDROID_ART_BIN_DIR}",
+ # Temporarily disable address space layout randomization (ASLR).
+ # This is needed on the host so that the linker loads core.oat at the necessary address.
+ LD_USE_LOAD_BIAS = "1",
+ TERM = os.environ.get("TERM", ""), # Needed for GDB
+ )
+
+ cmdline = dalvikvm_cmdline
+
+ if TIME_OUT == "gdb":
+ if run("uname").stdout.strip() == "Darwin":
+ # Fall back to timeout on Mac.
+ TIME_OUT = "timeout"
+ elif ISA == "x86":
+ # prctl call may fail in 32-bit on an older (3.2) 64-bit Linux kernel. Fall back to timeout.
+ TIME_OUT = "timeout"
+ else:
+ # Check if gdb is available.
+ proc = run('gdb --eval-command="quit"', check=False, save_cmd=False)
+ if proc.returncode != 0:
+ # gdb isn't available. Fall back to timeout.
+ TIME_OUT = "timeout"
+
+ if TIME_OUT == "timeout":
+ # Add timeout command if time out is desired.
+ #
+ # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
+ # will induce a full thread dump before killing the process. To ensure any issues in
+ # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
+ # child.
+ # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
+ cmdline = f"timeout --foreground -k 120s {TIME_OUT_VALUE}s {TIMEOUT_DUMPER} -s 15 {cmdline}"
+
+ os.chdir(ANDROID_BUILD_TOP)
+
+ # Make sure we delete any existing compiler artifacts.
+ # This enables tests to call the RUN script multiple times in a row
+ # without worrying about interference.
+ ctx.run(f"rm -rf {DEX_LOCATION}/{{oat,dalvik-cache}}/")
+
+ ctx.run(f"mkdir -p {mkdir_locations}")
+ ctx.run(setupapex_cmdline)
+ if USE_EXTRACTED_ZIPAPEX:
+ ctx.run(installapex_cmdline)
+ ctx.run(linkroot_cmdline)
+ ctx.run(linkroot_overlay_cmdline)
+ ctx.run(profman_cmdline)
+ ctx.run(dex2oat_cmdline, desc="Dex2oat")
+ ctx.run(dm_cmdline)
+ ctx.run(vdex_cmdline)
+ ctx.run(strip_cmdline)
+ ctx.run(sync_cmdline)
+
+ if DRY_RUN:
+ return
+
+ if USE_GDB:
+ # When running under gdb, we cannot do piping and grepping...
+ ctx.run(cmdline)
+ else:
+ ctx.run(tee(cmdline), expected_exit_code=args.expected_exit_code, desc="DalvikVM")
+ filter_output()
diff --git a/test/dexpreopt/Android.bp b/test/dexpreopt/Android.bp
index b39565b..f5ffd89 100644
--- a/test/dexpreopt/Android.bp
+++ b/test/dexpreopt/Android.bp
@@ -36,5 +36,5 @@
"libgmock",
"libprocinfo",
],
- test_config_template: "//art/test:art-gtests-target-standalone-root-template",
+ test_config_template: "art_standalone_dexpreopt_tests.xml",
}
diff --git a/test/dexpreopt/art_standalone_dexpreopt_tests.xml b/test/dexpreopt/art_standalone_dexpreopt_tests.xml
new file mode 100644
index 0000000..873b6a2
--- /dev/null
+++ b/test/dexpreopt/art_standalone_dexpreopt_tests.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<!-- Note: This test config file for {MODULE} is generated from a template. -->
+<configuration description="Runs {MODULE} as root.">
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}/{MODULE}" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp/{MODULE}" />
+ <option name="module-name" value="{MODULE}" />
+ <option name="ld-library-path-32" value="/apex/com.android.art/lib" />
+ <option name="ld-library-path-64" value="/apex/com.android.art/lib64" />
+ </test>
+
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- ART Mainline Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.art" />
+ <!-- ART Mainline Module (external (AOSP) version). -->
+ <option name="mainline-module-package-name" value="com.android.art" />
+ </object>
+
+ <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
+</configuration>
diff --git a/test/etc/apex-bootclasspath-utils.sh b/test/etc/apex-bootclasspath-utils.sh
deleted file mode 100755
index 5a0873d..0000000
--- a/test/etc/apex-bootclasspath-utils.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-# This file contains utils for constructing -Xbootclasspath and -Xbootclasspath-location
-# for dex2oat and dalvikvm from apex modules list.
-#
-# Those utils could be used outside of art/test/ to run ART in chroot setup.
-
-# Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
-# because that's what we use for compiling the boot.art image.
-# It may contain additional modules from TEST_CORE_JARS.
-readonly bpath_modules="core-oj core-libart okhttp bouncycastle apache-xml core-icu4j conscrypt"
-
-# Helper function to construct paths for apex modules (for both -Xbootclasspath and
-# -Xbootclasspath-location).
-#
-# Arguments.
-# ${1}: path prefix.
-get_apex_bootclasspath_impl() {
- local -r bpath_prefix="$1"
- local bpath_separator=""
- local bpath=""
- local bpath_jar=""
- for bpath_module in ${bpath_modules}; do
- local apex_module="com.android.art"
- case "$bpath_module" in
- (conscrypt) apex_module="com.android.conscrypt";;
- (core-icu4j) apex_module="com.android.i18n";;
- (*) apex_module="com.android.art";;
- esac
- bpath_jar="/apex/${apex_module}/javalib/${bpath_module}.jar"
- bpath+="${bpath_separator}${bpath_prefix}${bpath_jar}"
- bpath_separator=":"
- done
- echo "${bpath}"
-}
-
-# Gets a -Xbootclasspath paths with the apex modules.
-#
-# Arguments.
-# ${1}: host (y|n).
-get_apex_bootclasspath() {
- local -r host="${1}"
- local bpath_prefix=""
-
- if [[ "${host}" == "y" ]]; then
- bpath_prefix="${ANDROID_HOST_OUT}"
- fi
-
- get_apex_bootclasspath_impl "${bpath_prefix}"
-}
-
-# Gets a -Xbootclasspath-location paths with the apex modules.
-#
-# Arguments.
-# ${1}: host (y|n).
-get_apex_bootclasspath_locations() {
- local -r host="${1}"
- local bpath_location_prefix=""
-
- if [[ "${host}" == "y" ]]; then
- if [[ "${ANDROID_HOST_OUT:0:${#ANDROID_BUILD_TOP}+1}" == "${ANDROID_BUILD_TOP}/" ]]; then
- bpath_location_prefix="${ANDROID_HOST_OUT:${#ANDROID_BUILD_TOP}+1}"
- else
- error_msg "ANDROID_BUILD_TOP/ is not a prefix of ANDROID_HOST_OUT"\
- "\nANDROID_BUILD_TOP=${ANDROID_BUILD_TOP}"\
- "\nANDROID_HOST_OUT=${ANDROID_HOST_OUT}"
- exit
- fi
- fi
-
- get_apex_bootclasspath_impl "${bpath_location_prefix}"
-}
diff --git a/test/etc/default-build b/test/etc/default-build
deleted file mode 100755
index 26820f2..0000000
--- a/test/etc/default-build
+++ /dev/null
@@ -1,430 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-
-"""This is the default build script for run-tests.
-
-It can be overwrite by specific run-tests if needed.
-It is used from soong build and not intended to be called directly.
-"""
-
-import argparse
-import functools
-import glob
-import os
-from os import path
-import shlex
-import shutil
-import subprocess
-import tempfile
-import zipfile
-
-if not os.sys.argv:
- print(
- 'Error: default-build should have the parameters from the "build" script forwarded to it'
- )
- print('Error: An example of how do it correctly is ./default-build "$@"')
- os.sys.exit(1)
-
-
-def parse_bool(text):
- return {"true": True, "false": False}[text.lower()]
-
-
-TEST_NAME = os.environ["TEST_NAME"]
-ART_TEST_RUN_TEST_BOOTCLASSPATH = os.environ["ART_TEST_RUN_TEST_BOOTCLASSPATH"]
-NEED_DEX = parse_bool(os.environ["NEED_DEX"])
-
-# Set default values for directories.
-HAS_SMALI = path.exists("smali")
-HAS_JASMIN = path.exists("jasmin")
-HAS_SRC = path.exists("src")
-HAS_SRC_ART = path.exists("src-art")
-HAS_SRC2 = path.exists("src2")
-HAS_SRC_MULTIDEX = path.exists("src-multidex")
-HAS_SMALI_MULTIDEX = path.exists("smali-multidex")
-HAS_JASMIN_MULTIDEX = path.exists("jasmin-multidex")
-HAS_SMALI_EX = path.exists("smali-ex")
-HAS_SRC_EX = path.exists("src-ex")
-HAS_SRC_EX2 = path.exists("src-ex2")
-HAS_SRC_AOTEX = path.exists("src-aotex")
-HAS_SRC_BCPEX = path.exists("src-bcpex")
-HAS_HIDDENAPI_SPEC = path.exists("hiddenapi-flags.csv")
-
-# USE_HIDDENAPI=false run-test... will disable hiddenapi.
-USE_HIDDENAPI = parse_bool(os.environ.get("USE_HIDDENAPI", "true"))
-
-# USE_DESUGAR=false run-test... will disable desugaring.
-USE_DESUGAR = parse_bool(os.environ.get("USE_DESUGAR", "true"))
-
-JAVAC_ARGS = shlex.split(os.environ.get("JAVAC_ARGS", ""))
-SMALI_ARGS = shlex.split(os.environ.get("SMALI_ARGS", ""))
-D8_FLAGS = shlex.split(os.environ.get("D8_FLAGS", ""))
-
-# Allow overriding ZIP_COMPRESSION_METHOD with e.g. 'store'
-ZIP_COMPRESSION_METHOD = "deflate"
-# Align every ZIP file made by calling $ZIPALIGN command?
-ZIP_ALIGN_BYTES = None
-
-DEV_MODE = False
-BUILD_MODE = "target"
-API_LEVEL = None
-DEFAULT_EXPERIMENT = "no-experiment"
-EXPERIMENTAL = DEFAULT_EXPERIMENT
-
-# Setup experimental API level mappings in a bash associative array.
-EXPERIMENTAL_API_LEVEL = {}
-EXPERIMENTAL_API_LEVEL[DEFAULT_EXPERIMENT] = "26"
-EXPERIMENTAL_API_LEVEL["default-methods"] = "24"
-EXPERIMENTAL_API_LEVEL["parameter-annotations"] = "25"
-EXPERIMENTAL_API_LEVEL["agents"] = "26"
-EXPERIMENTAL_API_LEVEL["method-handles"] = "26"
-EXPERIMENTAL_API_LEVEL["var-handles"] = "28"
-
-# Parse command line arguments.
-opt_bool = argparse.BooleanOptionalAction # Bool also accepts the --no- prefix.
-parser = argparse.ArgumentParser(description=__doc__)
-parser.add_argument("--src", dest="HAS_SRC", action=opt_bool)
-parser.add_argument("--src2", dest="HAS_SRC2", action=opt_bool)
-parser.add_argument("--src-multidex", dest="HAS_SRC_MULTIDEX", action=opt_bool)
-parser.add_argument(
- "--smali-multidex", dest="HAS_SMALI_MULTIDEX", action=opt_bool)
-parser.add_argument("--src-ex", dest="HAS_SRC_EX", action=opt_bool)
-parser.add_argument("--src-ex2", dest="HAS_SRC_EX2", action=opt_bool)
-parser.add_argument("--smali", dest="HAS_SMALI", action=opt_bool)
-parser.add_argument("--jasmin", dest="HAS_JASMIN", action=opt_bool)
-parser.add_argument("--api-level", dest="API_LEVEL", type=int)
-parser.add_argument(
- "--experimental", dest="EXPERIMENTAL", type=str)
-parser.add_argument(
- "--zip-compression-method", dest="ZIP_COMPRESSION_METHOD", type=str)
-parser.add_argument("--zip-align", dest="ZIP_ALIGN_BYTES", type=int)
-parser.add_argument(
- "--host", dest="BUILD_MODE", action="store_const", const="host")
-parser.add_argument(
- "--target", dest="BUILD_MODE", action="store_const", const="target")
-parser.add_argument(
- "--jvm", dest="BUILD_MODE", action="store_const", const="jvm")
-parser.add_argument("--dev", dest="DEV_MODE", action=opt_bool)
-# Update variables with command line arguments that were set.
-globals().update(
- {k: v for k, v in parser.parse_args().__dict__.items() if v is not None})
-
-if BUILD_MODE == "jvm":
- # No desugaring on jvm because it supports the latest functionality.
- USE_DESUGAR = False
- # Do not attempt to build src-art directories on jvm,
- # since it would fail without libcore.
- HAS_SRC_ART = False
-
-# Set API level for smali and d8.
-if not API_LEVEL:
- API_LEVEL = EXPERIMENTAL_API_LEVEL[EXPERIMENTAL]
-
-# Add API level arguments to smali and dx
-SMALI_ARGS.extend(["--api", str(API_LEVEL)])
-D8_FLAGS.extend(["--min-api", str(API_LEVEL)])
-
-
-def run(executable, args):
- cmd = shlex.split(executable) + args
- if executable.endswith(".sh"):
- cmd = ["/bin/bash"] + cmd
- if DEV_MODE:
- print("Run:", " ".join(cmd))
- p = subprocess.run(cmd, check=True)
- if p.returncode != 0:
- raise Exception("Failed command: " + " ".join(cmd))
-
-
-# Helper functions to execute tools.
-soong_zip = functools.partial(run, os.environ["SOONG_ZIP"])
-zipalign = functools.partial(run, os.environ["ZIPALIGN"])
-javac = functools.partial(run, os.environ["JAVAC"])
-jasmin = functools.partial(run, os.environ["JASMIN"])
-smali = functools.partial(run, os.environ["SMALI"])
-d8 = functools.partial(run, os.environ["D8"])
-hiddenapi = functools.partial(run, os.environ["HIDDENAPI"])
-
-# If wrapper script exists, use it instead of the default javac.
-if os.path.exists("javac_wrapper.sh"):
- javac = functools.partial(run, "javac_wrapper.sh")
-
-def find(root, name):
- return sorted(glob.glob(path.join(root, "**", name), recursive=True))
-
-
-def zip(zip_target, *files):
- zip_args = ["-o", zip_target]
- if ZIP_COMPRESSION_METHOD == "store":
- zip_args.extend(["-L", "0"])
- for f in files:
- zip_args.extend(["-f", f])
- soong_zip(zip_args)
-
- if ZIP_ALIGN_BYTES:
- # zipalign does not operate in-place, so write results to a temp file.
- with tempfile.TemporaryDirectory(dir=".") as tmp_dir:
- tmp_file = path.join(tmp_dir, "aligned.zip")
- zipalign(["-f", str(ZIP_ALIGN_BYTES), zip_target, tmp_file])
- # replace original zip target with our temp file.
- os.rename(tmp_file, zip_target)
-
-
-def make_jasmin(out_directory, jasmin_sources):
- os.makedirs(out_directory, exist_ok=True)
- jasmin(["-d", out_directory] + sorted(jasmin_sources))
-
-
-# Like regular javac but may include libcore on the bootclasspath.
-def javac_with_bootclasspath(args):
- flags = JAVAC_ARGS + ["-encoding", "utf8"]
- if BUILD_MODE != "jvm":
- flags.extend(["-bootclasspath", ART_TEST_RUN_TEST_BOOTCLASSPATH])
- javac(flags + args)
-
-
-# Make a "dex" file given a directory of classes. This will be
-# packaged in a jar file.
-def make_dex(name):
- d8_inputs = find(name, "*.class")
- d8_output = name + ".jar"
- dex_output = name + ".dex"
- if USE_DESUGAR:
- flags = ["--lib", ART_TEST_RUN_TEST_BOOTCLASSPATH]
- else:
- flags = ["--no-desugaring"]
- assert d8_inputs
- d8(D8_FLAGS + flags + ["--output", d8_output] + d8_inputs)
-
- # D8 outputs to JAR files today rather than DEX files as DX used
- # to. To compensate, we extract the DEX from d8's output to meet the
- # expectations of make_dex callers.
- with tempfile.TemporaryDirectory(dir=".") as tmp_dir:
- zipfile.ZipFile(d8_output, "r").extractall(tmp_dir)
- os.rename(path.join(tmp_dir, "classes.dex"), dex_output)
-
-
-# Merge all the dex files.
-# Skip non-existing files, but at least 1 file must exist.
-def make_dexmerge(*dex_files_to_merge):
- # Dex file that acts as the destination.
- dst_file = dex_files_to_merge[0]
-
- # Skip any non-existing files.
- dex_files_to_merge = list(filter(path.exists, dex_files_to_merge))
-
- # NB: We merge even if there is just single input.
- # It is useful to normalize non-deterministic smali output.
-
- with tempfile.TemporaryDirectory(dir=".") as tmp_dir:
- d8(["--min-api", API_LEVEL, "--output", tmp_dir] + dex_files_to_merge)
- assert not path.exists(path.join(tmp_dir, "classes2.dex"))
- for input_dex in dex_files_to_merge:
- os.remove(input_dex)
- os.rename(path.join(tmp_dir, "classes.dex"), dst_file)
-
-
-def make_hiddenapi(*dex_files):
- args = ["encode"]
- for dex_file in dex_files:
- args.extend(["--input-dex=" + dex_file, "--output-dex=" + dex_file])
- args.append("--api-flags=hiddenapi-flags.csv")
- args.append("--no-force-assign-all")
- hiddenapi(args)
-
-
-if path.exists("classes.dex"):
- zip(TEST_NAME + ".jar", "classes.dex")
- os.sys.exit(0)
-
-
-def has_multidex():
- return HAS_SRC_MULTIDEX or HAS_JASMIN_MULTIDEX or HAS_SMALI_MULTIDEX
-
-
-def add_to_cp_args(old_cp_args, path):
- if len(old_cp_args) == 0:
- return ["-cp", path]
- else:
- return ["-cp", old_cp_args[1] + ":" + path]
-
-
-src_tmp_all = []
-
-if HAS_JASMIN:
- make_jasmin("jasmin_classes", find("jasmin", "*.j"))
- src_tmp_all = add_to_cp_args(src_tmp_all, "jasmin_classes")
-
-if HAS_JASMIN_MULTIDEX:
- make_jasmin("jasmin_classes2", find("jasmin-multidex", "*.j"))
- src_tmp_all = add_to_cp_args(src_tmp_all, "jasmin_classes2")
-
-if HAS_SRC and (HAS_SRC_MULTIDEX or HAS_SRC_AOTEX or HAS_SRC_BCPEX or
- HAS_SRC_EX or HAS_SRC_ART or HAS_SRC2 or HAS_SRC_EX2):
- # To allow circular references, compile src/, src-multidex/, src-aotex/,
- # src-bcpex/, src-ex/ together and pass the output as class path argument.
- # Replacement sources in src-art/, src2/ and src-ex2/ can replace symbols
- # used by the other src-* sources we compile here but everything needed to
- # compile the other src-* sources should be present in src/ (and jasmin*/).
- os.makedirs("classes-tmp-all")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-tmp-all"] +
- find("src", "*.java") +
- find("src-multidex", "*.java") +
- find("src-aotex", "*.java") +
- find("src-bcpex", "*.java") +
- find("src-ex", "*.java"))
- src_tmp_all = add_to_cp_args(src_tmp_all, "classes-tmp-all")
-
-if HAS_SRC_AOTEX:
- os.makedirs("classes-aotex")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-aotex"] +
- find("src-aotex", "*.java"))
- if NEED_DEX:
- make_dex("classes-aotex")
- # rename it so it shows up as "classes.dex" in the zip file.
- os.rename("classes-aotex.dex", "classes.dex")
- zip(TEST_NAME + "-aotex.jar", "classes.dex")
-
-if HAS_SRC_BCPEX:
- os.makedirs("classes-bcpex")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-bcpex"] +
- find("src-bcpex", "*.java"))
- if NEED_DEX:
- make_dex("classes-bcpex")
- # rename it so it shows up as "classes.dex" in the zip file.
- os.rename("classes-bcpex.dex", "classes.dex")
- zip(TEST_NAME + "-bcpex.jar", "classes.dex")
-
-if HAS_SRC:
- os.makedirs("classes", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes"] + find("src", "*.java"))
-
-if HAS_SRC_ART:
- os.makedirs("classes", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes"] + find("src-art", "*.java"))
-
-if HAS_SRC_MULTIDEX:
- os.makedirs("classes2")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes2"] +
- find("src-multidex", "*.java"))
- if NEED_DEX:
- make_dex("classes2")
-
-if HAS_SRC2:
- os.makedirs("classes", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes"] +
- find("src2", "*.java"))
-
-# If the classes directory is not-empty, package classes in a DEX file.
-# NB: some tests provide classes rather than java files.
-if find("classes", "*"):
- if NEED_DEX:
- make_dex("classes")
-
-if HAS_JASMIN:
- # Compile Jasmin classes as if they were part of the classes.dex file.
- if NEED_DEX:
- make_dex("jasmin_classes")
- make_dexmerge("classes.dex", "jasmin_classes.dex")
- else:
- # Move jasmin classes into classes directory so that they are picked up
- # with -cp classes.
- os.makedirs("classes", exist_ok=True)
- shutil.copytree("jasmin_classes", "classes", dirs_exist_ok=True)
-
-if HAS_SMALI and NEED_DEX:
- # Compile Smali classes
- smali(["-JXmx512m", "assemble"] + SMALI_ARGS +
- ["--output", "smali_classes.dex"] + find("smali", "*.smali"))
- assert path.exists("smali_classes.dex")
- # Merge smali files into classes.dex,
- # this takes priority over any jasmin files.
- make_dexmerge("classes.dex", "smali_classes.dex")
-
-# Compile Jasmin classes in jasmin-multidex as if they were part of
-# the classes2.jar
-if HAS_JASMIN_MULTIDEX:
- if NEED_DEX:
- make_dex("jasmin_classes2")
- make_dexmerge("classes2.dex", "jasmin_classes2.dex")
- else:
- # Move jasmin classes into classes2 directory so that
- # they are picked up with -cp classes2.
- os.makedirs("classes2", exist_ok=True)
- shutil.copytree("jasmin_classes2", "classes2", dirs_exist_ok=True)
- shutil.rmtree("jasmin_classes2")
-
-if HAS_SMALI_MULTIDEX and NEED_DEX:
- # Compile Smali classes
- smali(["-JXmx512m", "assemble"] + SMALI_ARGS +
- ["--output", "smali_classes2.dex"] + find("smali-multidex", "*.smali"))
-
- # Merge smali_classes2.dex into classes2.dex
- make_dexmerge("classes2.dex", "smali_classes2.dex")
-
-if HAS_SRC_EX:
- os.makedirs("classes-ex", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-ex"] + find("src-ex", "*.java"))
-
-if HAS_SRC_EX2:
- os.makedirs("classes-ex", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-ex"] + find("src-ex2", "*.java"))
-
-if path.exists("classes-ex") and NEED_DEX:
- make_dex("classes-ex")
-
-if HAS_SMALI_EX and NEED_DEX:
- # Compile Smali classes
- smali(["-JXmx512m", "assemble"] + SMALI_ARGS +
- ["--output", "smali_classes-ex.dex"] + find("smali-ex", "*.smali"))
- assert path.exists("smali_classes-ex.dex")
- # Merge smali files into classes-ex.dex.
- make_dexmerge("classes-ex.dex", "smali_classes-ex.dex")
-
-if path.exists("classes-ex.dex"):
- # Apply hiddenapi on the dex files if the test has API list file(s).
- if USE_HIDDENAPI and HAS_HIDDENAPI_SPEC:
- make_hiddenapi("classes-ex.dex")
-
- # quick shuffle so that the stored name is "classes.dex"
- os.rename("classes.dex", "classes-1.dex")
- os.rename("classes-ex.dex", "classes.dex")
- zip(TEST_NAME + "-ex.jar", "classes.dex")
- os.rename("classes.dex", "classes-ex.dex")
- os.rename("classes-1.dex", "classes.dex")
-
-# Apply hiddenapi on the dex files if the test has API list file(s).
-if NEED_DEX and USE_HIDDENAPI and HAS_HIDDENAPI_SPEC:
- if has_multidex():
- make_hiddenapi("classes.dex", "classes2.dex")
- else:
- make_hiddenapi("classes.dex")
-
-# Create a single dex jar with two dex files for multidex.
-if NEED_DEX:
- if path.exists("classes2.dex"):
- zip(TEST_NAME + ".jar", "classes.dex", "classes2.dex")
- else:
- zip(TEST_NAME + ".jar", "classes.dex")
diff --git a/test/etc/default-check b/test/etc/default-check
deleted file mode 100755
index f6f7bf4..0000000
--- a/test/etc/default-check
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/etc/default-run b/test/etc/default-run
deleted file mode 100755
index ecbbbc7..0000000
--- a/test/etc/default-run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-exec ${RUN} "$@"
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
deleted file mode 100755
index ef168ca..0000000
--- a/test/etc/run-test-jar
+++ /dev/null
@@ -1,1546 +0,0 @@
-#!/bin/bash
-#
-# Runner for an individual run-test.
-
-readonly local_path=$(dirname "$0")
-source "${local_path}/apex-bootclasspath-utils.sh"
-
-# Check how many colors the terminal can display.
-ncolors=$(tput colors 2>/dev/null)
-
-# Check that stdout is connected to a terminal and that we have at least 1 color.
-# This ensures that if the stdout is not connected to a terminal and instead
-# the stdout will be used for a log, it will not append the color characters.
-if [[ -t 1 && ${ncolors} && ${ncolors} -ge 1 ]]; then
- bold_red="$(tput bold)$(tput setaf 1)"
-fi
-
-readonly bold_red
-
-error_msg() {
- echo -e "${bold_red}ERROR: $@" 1>&2
-}
-
-if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- error_msg 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
-fi
-
-msg() {
- if [ "$QUIET" = "n" ]; then
- echo "$@"
- fi
-}
-
-ANDROID_ROOT="/system"
-ANDROID_ART_ROOT="/apex/com.android.art"
-ANDROID_I18N_ROOT="/apex/com.android.i18n"
-ANDROID_TZDATA_ROOT="/apex/com.android.tzdata"
-ARCHITECTURES_32="(arm|x86|none)"
-ARCHITECTURES_64="(arm64|x86_64|none)"
-ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
-GET_DEVICE_ISA_BITNESS_FLAG="--32"
-BOOT_IMAGE=""
-CHROOT=
-COMPILE_FLAGS=""
-DALVIKVM="dalvikvm32"
-DEBUGGER="n"
-WITH_AGENT=()
-DEBUGGER_AGENT=""
-WRAP_DEBUGGER_AGENT="n"
-DEV_MODE="n"
-DEX2OAT_NDEBUG_BINARY="dex2oat32"
-DEX2OAT_DEBUG_BINARY="dex2oatd32"
-EXPERIMENTAL=""
-FALSE_BIN="false"
-FLAGS=""
-ANDROID_FLAGS=""
-GDB=""
-GDB_ARGS=""
-GDB_DEX2OAT=""
-GDB_DEX2OAT_ARGS=""
-GDBSERVER_DEVICE="gdbserver"
-GDBSERVER_HOST="gdbserver"
-HAVE_IMAGE="y"
-HOST="n"
-BIONIC="n"
-CREATE_ANDROID_ROOT="n"
-USE_ZIPAPEX="n"
-ZIPAPEX_LOC=""
-USE_EXTRACTED_ZIPAPEX="n"
-EXTRACTED_ZIPAPEX_LOC=""
-INTERPRETER="n"
-JIT="n"
-INVOKE_WITH=""
-IS_JVMTI_TEST="n"
-ADD_LIBDIR_ARGUMENTS="n"
-SUFFIX64=""
-ISA=x86
-LIBRARY_DIRECTORY="lib"
-TEST_DIRECTORY="nativetest"
-MAIN=""
-OPTIMIZE="y"
-PREBUILD="y"
-QUIET="n"
-RELOCATE="n"
-SECONDARY_DEX=""
-TIME_OUT="n" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
-TIMEOUT_DUMPER=signal_dumper
-# Values in seconds.
-TIME_OUT_EXTRA=0
-TIME_OUT_VALUE=
-USE_GDB="n"
-USE_GDBSERVER="n"
-GDBSERVER_PORT=":5039"
-USE_GDB_DEX2OAT="n"
-USE_JVM="n"
-USE_JVMTI="n"
-VERIFY="y" # y=yes,n=no,s=softfail
-ZYGOTE=""
-DEX_VERIFY=""
-INSTRUCTION_SET_FEATURES=""
-ARGS=""
-VDEX_ARGS=""
-EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
-DRY_RUN="n" # if y prepare to run the test but don't run it.
-TEST_VDEX="n"
-TEST_DEX2OAT_DM="n"
-TEST_RUNTIME_DM="n"
-TEST_IS_NDEBUG="n"
-APP_IMAGE="y"
-SECONDARY_APP_IMAGE="y"
-SECONDARY_CLASS_LOADER_CONTEXT=""
-SECONDARY_COMPILATION="y"
-JVMTI_STRESS="n"
-JVMTI_STEP_STRESS="n"
-JVMTI_FIELD_STRESS="n"
-JVMTI_TRACE_STRESS="n"
-JVMTI_REDEFINE_STRESS="n"
-PROFILE="n"
-RANDOM_PROFILE="n"
-# The normal dex2oat timeout.
-DEX2OAT_TIMEOUT="300" # 5 mins
-# The *hard* timeout where we really start trying to kill the dex2oat.
-DEX2OAT_RT_TIMEOUT="360" # 6 mins
-CREATE_RUNNER="n"
-
-# if "y", run 'sync' before dalvikvm to make sure all files from
-# build step (e.g. dex2oat) were finished writing.
-SYNC_BEFORE_RUN="n"
-
-# When running a debug build, we want to run with all checks.
-ANDROID_FLAGS="${ANDROID_FLAGS} -XX:SlowDebug=true"
-# The same for dex2oatd, both prebuild and runtime-driven.
-ANDROID_FLAGS="${ANDROID_FLAGS} -Xcompiler-option --runtime-arg -Xcompiler-option -XX:SlowDebug=true"
-COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -XX:SlowDebug=true"
-
-# Let the compiler and runtime know that we are running tests.
-COMPILE_FLAGS="${COMPILE_FLAGS} --compile-art-test"
-ANDROID_FLAGS="${ANDROID_FLAGS} -Xcompiler-option --compile-art-test"
-
-while true; do
- if [ "x$1" = "x--quiet" ]; then
- QUIET="y"
- shift
- elif [ "x$1" = "x--dex2oat-rt-timeout" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --dex2oat-rt-timeout"
- exit 1
- fi
- DEX2OAT_RT_TIMEOUT="$1"
- shift
- elif [ "x$1" = "x--dex2oat-timeout" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --dex2oat-timeout"
- exit 1
- fi
- DEX2OAT_TIMEOUT="$1"
- shift
- elif [ "x$1" = "x--jvmti" ]; then
- USE_JVMTI="y"
- IS_JVMTI_TEST="y"
- # Secondary images block some tested behavior.
- SECONDARY_APP_IMAGE="n"
- shift
- elif [ "x$1" = "x--add-libdir-argument" ]; then
- ADD_LIBDIR_ARGUMENTS="y"
- shift
- elif [ "x$1" = "x-O" ]; then
- TEST_IS_NDEBUG="y"
- shift
- elif [ "x$1" = "x--lib" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --lib"
- exit 1
- fi
- LIB="$1"
- shift
- elif [ "x$1" = "x--gc-stress" ]; then
- # Give an extra 20 mins if we are gc-stress.
- TIME_OUT_EXTRA=$((${TIME_OUT_EXTRA} + 1200))
- shift
- elif [ "x$1" = "x--testlib" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --testlib"
- exit 1
- fi
- ARGS="${ARGS} $1"
- shift
- elif [ "x$1" = "x--args" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --args"
- exit 1
- fi
- ARGS="${ARGS} $1"
- shift
- elif [ "x$1" = "x--compiler-only-option" ]; then
- shift
- option="$1"
- COMPILE_FLAGS="${COMPILE_FLAGS} $option"
- shift
- elif [ "x$1" = "x-Xcompiler-option" ]; then
- shift
- option="$1"
- FLAGS="${FLAGS} -Xcompiler-option $option"
- COMPILE_FLAGS="${COMPILE_FLAGS} $option"
- shift
- elif [ "x$1" = "x--create-runner" ]; then
- CREATE_RUNNER="y"
- shift
- elif [ "x$1" = "x--android-runtime-option" ]; then
- shift
- option="$1"
- ANDROID_FLAGS="${ANDROID_FLAGS} $option"
- shift
- elif [ "x$1" = "x--runtime-option" ]; then
- shift
- option="$1"
- FLAGS="${FLAGS} $option"
- if [ "x$option" = "x-Xmethod-trace" ]; then
- # Method tracing can slow some tests down a lot.
- TIME_OUT_EXTRA=$((${TIME_OUT_EXTRA} + 1200))
- fi
- shift
- elif [ "x$1" = "x--boot" ]; then
- shift
- BOOT_IMAGE="$1"
- shift
- elif [ "x$1" = "x--relocate" ]; then
- RELOCATE="y"
- shift
- elif [ "x$1" = "x--no-relocate" ]; then
- RELOCATE="n"
- shift
- elif [ "x$1" = "x--prebuild" ]; then
- PREBUILD="y"
- shift
- elif [ "x$1" = "x--compact-dex-level" ]; then
- shift
- COMPILE_FLAGS="${COMPILE_FLAGS} --compact-dex-level=$1"
- shift
- elif [ "x$1" = "x--jvmti-redefine-stress" ]; then
- # APP_IMAGE doesn't really work with jvmti redefine stress
- USE_JVMTI="y"
- APP_IMAGE="n"
- SECONDARY_APP_IMAGE="n"
- JVMTI_STRESS="y"
- JVMTI_REDEFINE_STRESS="y"
- shift
- elif [ "x$1" = "x--jvmti-step-stress" ]; then
- USE_JVMTI="y"
- JVMTI_STRESS="y"
- JVMTI_STEP_STRESS="y"
- shift
- elif [ "x$1" = "x--jvmti-field-stress" ]; then
- USE_JVMTI="y"
- JVMTI_STRESS="y"
- JVMTI_FIELD_STRESS="y"
- shift
- elif [ "x$1" = "x--jvmti-trace-stress" ]; then
- USE_JVMTI="y"
- JVMTI_STRESS="y"
- JVMTI_TRACE_STRESS="y"
- shift
- elif [ "x$1" = "x--no-app-image" ]; then
- APP_IMAGE="n"
- shift
- elif [ "x$1" = "x--no-secondary-app-image" ]; then
- SECONDARY_APP_IMAGE="n"
- shift
- elif [ "x$1" = "x--secondary-class-loader-context" ]; then
- shift
- SECONDARY_CLASS_LOADER_CONTEXT="$1"
- shift
- elif [ "x$1" = "x--no-secondary-compilation" ]; then
- SECONDARY_COMPILATION="n"
- shift
- elif [ "x$1" = "x--host" ]; then
- HOST="y"
- ANDROID_ROOT="${ANDROID_HOST_OUT}"
- ANDROID_ART_ROOT="${ANDROID_HOST_OUT}/com.android.art"
- ANDROID_I18N_ROOT="${ANDROID_HOST_OUT}/com.android.i18n"
- ANDROID_TZDATA_ROOT="${ANDROID_HOST_OUT}/com.android.tzdata"
- # On host, we default to using the symlink, as the PREFER_32BIT
- # configuration is the only configuration building a 32bit version of
- # dex2oat.
- DEX2OAT_DEBUG_BINARY="dex2oatd"
- DEX2OAT_NDEBUG_BINARY="dex2oat"
- shift
- elif [ "x$1" = "x--bionic" ]; then
- BIONIC="y"
- # We need to create an ANDROID_ROOT because currently we cannot create
- # the frameworks/libcore with linux_bionic so we need to use the normal
- # host ones which are in a different location.
- CREATE_ANDROID_ROOT="y"
- shift
- elif [ "x$1" = "x--runtime-extracted-zipapex" ]; then
- shift
- USE_EXTRACTED_ZIPAPEX="y"
- EXTRACTED_ZIPAPEX_LOC="$1"
- shift
- elif [ "x$1" = "x--runtime-zipapex" ]; then
- shift
- USE_ZIPAPEX="y"
- ZIPAPEX_LOC="$1"
- # TODO (b/119942078): Currently apex does not support
- # symlink_preferred_arch so we will not have a dex2oatd to execute and
- # need to manually provide
- # dex2oatd64.
- DEX2OAT_DEBUG_BINARY="dex2oatd64"
- shift
- elif [ "x$1" = "x--no-prebuild" ]; then
- PREBUILD="n"
- shift
- elif [ "x$1" = "x--no-image" ]; then
- HAVE_IMAGE="n"
- shift
- elif [ "x$1" = "x--secondary" ]; then
- SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
- # Enable cfg-append to make sure we get the dump for both dex files.
- # (otherwise the runtime compilation of the secondary dex will overwrite
- # the dump of the first one).
- FLAGS="${FLAGS} -Xcompiler-option --dump-cfg-append"
- COMPILE_FLAGS="${COMPILE_FLAGS} --dump-cfg-append"
- shift
- elif [ "x$1" = "x--with-agent" ]; then
- shift
- USE_JVMTI="y"
- WITH_AGENT+=("$1")
- shift
- elif [ "x$1" = "x--debug-wrap-agent" ]; then
- WRAP_DEBUGGER_AGENT="y"
- shift
- elif [ "x$1" = "x--debug-agent" ]; then
- shift
- DEBUGGER="agent"
- USE_JVMTI="y"
- DEBUGGER_AGENT="$1"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--debug" ]; then
- USE_JVMTI="y"
- DEBUGGER="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdbserver-port" ]; then
- shift
- GDBSERVER_PORT=$1
- shift
- elif [ "x$1" = "x--gdbserver-bin" ]; then
- shift
- GDBSERVER_HOST=$1
- GDBSERVER_DEVICE=$1
- shift
- elif [ "x$1" = "x--gdbserver" ]; then
- USE_GDBSERVER="y"
- DEV_MODE="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdb" ]; then
- USE_GDB="y"
- DEV_MODE="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdb-arg" ]; then
- shift
- gdb_arg="$1"
- GDB_ARGS="${GDB_ARGS} $gdb_arg"
- shift
- elif [ "x$1" = "x--gdb-dex2oat" ]; then
- USE_GDB_DEX2OAT="y"
- DEV_MODE="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdb-dex2oat-args" ]; then
- shift
- for arg in $(echo $1 | tr ";" " "); do
- GDB_DEX2OAT_ARGS+="$arg "
- done
- shift
- elif [ "x$1" = "x--zygote" ]; then
- ZYGOTE="-Xzygote"
- msg "Spawning from zygote"
- shift
- elif [ "x$1" = "x--dev" ]; then
- DEV_MODE="y"
- shift
- elif [ "x$1" = "x--interpreter" ]; then
- INTERPRETER="y"
- shift
- elif [ "x$1" = "x--jit" ]; then
- JIT="y"
- shift
- elif [ "x$1" = "x--baseline" ]; then
- FLAGS="${FLAGS} -Xcompiler-option --baseline"
- COMPILE_FLAGS="${COMPILE_FLAGS} --baseline"
- shift
- elif [ "x$1" = "x--jvm" ]; then
- USE_JVM="y"
- shift
- elif [ "x$1" = "x--invoke-with" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --invoke-with"
- exit 1
- fi
- if [ "x$INVOKE_WITH" = "x" ]; then
- INVOKE_WITH="$1"
- else
- INVOKE_WITH="$INVOKE_WITH $1"
- fi
- shift
- elif [ "x$1" = "x--no-verify" ]; then
- VERIFY="n"
- shift
- elif [ "x$1" = "x--verify-soft-fail" ]; then
- VERIFY="s"
- shift
- elif [ "x$1" = "x--no-optimize" ]; then
- OPTIMIZE="n"
- shift
- elif [ "x$1" = "x--chroot" ]; then
- shift
- CHROOT="$1"
- shift
- elif [ "x$1" = "x--simpleperf" ]; then
- SIMPLEPERF="yes"
- shift
- elif [ "x$1" = "x--android-root" ]; then
- shift
- ANDROID_ROOT="$1"
- shift
- elif [ "x$1" = "x--android-i18n-root" ]; then
- shift
- ANDROID_I18N_ROOT="$1"
- shift
- elif [ "x$1" = "x--android-art-root" ]; then
- shift
- ANDROID_ART_ROOT="$1"
- shift
- elif [ "x$1" = "x--android-tzdata-root" ]; then
- shift
- ANDROID_TZDATA_ROOT="$1"
- shift
- elif [ "x$1" = "x--instruction-set-features" ]; then
- shift
- INSTRUCTION_SET_FEATURES="$1"
- shift
- elif [ "x$1" = "x--timeout" ]; then
- shift
- TIME_OUT_VALUE="$1"
- shift
- elif [ "x$1" = "x--" ]; then
- shift
- break
- elif [ "x$1" = "x--64" ]; then
- SUFFIX64="64"
- ISA="x86_64"
- GDBSERVER_DEVICE="gdbserver64"
- DALVIKVM="dalvikvm64"
- LIBRARY_DIRECTORY="lib64"
- TEST_DIRECTORY="nativetest64"
- ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
- GET_DEVICE_ISA_BITNESS_FLAG="--64"
- DEX2OAT_NDEBUG_BINARY="dex2oat64"
- DEX2OAT_DEBUG_BINARY="dex2oatd64"
- shift
- elif [ "x$1" = "x--experimental" ]; then
- if [ "$#" -lt 2 ]; then
- error_msg "missing --experimental option"
- exit 1
- fi
- EXPERIMENTAL="$EXPERIMENTAL $2"
- shift 2
- elif [ "x$1" = "x--external-log-tags" ]; then
- EXTERNAL_LOG_TAGS="y"
- shift
- elif [ "x$1" = "x--dry-run" ]; then
- DRY_RUN="y"
- shift
- elif [ "x$1" = "x--vdex" ]; then
- TEST_VDEX="y"
- shift
- elif [ "x$1" = "x--dex2oat-dm" ]; then
- TEST_DEX2OAT_DM="y"
- shift
- elif [ "x$1" = "x--runtime-dm" ]; then
- TEST_RUNTIME_DM="y"
- shift
- elif [ "x$1" = "x--vdex-filter" ]; then
- shift
- option="$1"
- VDEX_ARGS="${VDEX_ARGS} --compiler-filter=$option"
- shift
- elif [ "x$1" = "x--vdex-arg" ]; then
- shift
- VDEX_ARGS="${VDEX_ARGS} $1"
- shift
- elif [ "x$1" = "x--sync" ]; then
- SYNC_BEFORE_RUN="y"
- shift
- elif [ "x$1" = "x--profile" ]; then
- PROFILE="y"
- shift
- elif [ "x$1" = "x--random-profile" ]; then
- RANDOM_PROFILE="y"
- shift
- elif expr "x$1" : "x--" >/dev/null 2>&1; then
- error_msg "unknown $0 option: $1"
- exit 1
- else
- break
- fi
-done
-
-# HACK: Force the use of `signal_dumper` on host.
-if [[ "$HOST" = "y" ]]; then
- TIME_OUT="timeout"
-fi
-
-# If you change this, update the timeout in testrunner.py as well.
-if [ -z "$TIME_OUT_VALUE" ] ; then
- # 10 minutes is the default.
- TIME_OUT_VALUE=600
-
- # For sanitized builds use a larger base.
- # TODO: Consider sanitized target builds?
- if [ "x$SANITIZE_HOST" != "x" ] ; then
- TIME_OUT_VALUE=1500 # 25 minutes.
- fi
-
- TIME_OUT_VALUE=$((${TIME_OUT_VALUE} + ${TIME_OUT_EXTRA}))
-fi
-
-# Escape hatch for slow hosts or devices. Accept an environment variable as a timeout factor.
-if [ ! -z "$ART_TIME_OUT_MULTIPLIER" ] ; then
- TIME_OUT_VALUE=$((${TIME_OUT_VALUE} * ${ART_TIME_OUT_MULTIPLIER}))
-fi
-
-# The DEX_LOCATION with the chroot prefix, if any.
-CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION"
-
-# If running on device, determine the ISA of the device.
-if [ "$HOST" = "n" -a "$USE_JVM" = "n" ]; then
- ISA=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-isa" "$GET_DEVICE_ISA_BITNESS_FLAG")
-fi
-
-if [ "$USE_JVM" = "n" ]; then
- FLAGS="${FLAGS} ${ANDROID_FLAGS}"
- # we don't want to be trying to get adbconnections since the plugin might
- # not have been built.
- FLAGS="${FLAGS} -XjdwpProvider:none"
- for feature in ${EXPERIMENTAL}; do
- FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}"
- COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}"
- done
-fi
-
-if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
- ANDROID_ROOT=$DEX_LOCATION/android-root
-fi
-
-if [ "x$1" = "x" ] ; then
- MAIN="Main"
-else
- MAIN="$1"
- shift
-fi
-
-if [ "$ZYGOTE" = "" ]; then
- if [ "$OPTIMIZE" = "y" ]; then
- if [ "$VERIFY" = "y" ]; then
- DEX_OPTIMIZE="-Xdexopt:verified"
- else
- DEX_OPTIMIZE="-Xdexopt:all"
- fi
- msg "Performing optimizations"
- else
- DEX_OPTIMIZE="-Xdexopt:none"
- msg "Skipping optimizations"
- fi
-
- if [ "$VERIFY" = "y" ]; then
- JVM_VERIFY_ARG="-Xverify:all"
- msg "Performing verification"
- elif [ "$VERIFY" = "s" ]; then
- JVM_VERIFY_ARG="Xverify:all"
- DEX_VERIFY="-Xverify:softfail"
- msg "Forcing verification to be soft fail"
- else # VERIFY = "n"
- DEX_VERIFY="-Xverify:none"
- JVM_VERIFY_ARG="-Xverify:none"
- msg "Skipping verification"
- fi
-fi
-
-msg "------------------------------"
-
-if [ "$DEBUGGER" = "y" ]; then
- # Use this instead for ddms and connect by running 'ddms':
- # DEBUGGER_OPTS="-XjdwpOptions=server=y,suspend=y -XjdwpProvider:adbconnection"
- # TODO: add a separate --ddms option?
-
- PORT=12345
- msg "Waiting for jdb to connect:"
- if [ "$HOST" = "n" ]; then
- msg " adb forward tcp:$PORT tcp:$PORT"
- fi
- msg " jdb -attach localhost:$PORT"
- if [ "$USE_JVM" = "n" ]; then
- # Use the default libjdwp agent. Use --debug-agent to use a custom one.
- DEBUGGER_OPTS="-agentpath:libjdwp.so=transport=dt_socket,address=$PORT,server=y,suspend=y -XjdwpProvider:internal"
- else
- DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
- fi
-elif [ "$DEBUGGER" = "agent" ]; then
- PORT=12345
- # TODO Support ddms connection and support target.
- if [ "$HOST" = "n" ]; then
- error_msg "--debug-agent not supported yet for target!"
- exit 1
- fi
- AGENTPATH=${DEBUGGER_AGENT}
- if [ "$WRAP_DEBUGGER_AGENT" = "y" ]; then
- WRAPPROPS="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}/libwrapagentpropertiesd.so"
- if [ "$TEST_IS_NDEBUG" = "y" ]; then
- WRAPPROPS="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}/libwrapagentproperties.so"
- fi
- AGENTPATH="${WRAPPROPS}=${ANDROID_BUILD_TOP}/art/tools/libjdwp-compat.props,${AGENTPATH}"
- fi
- msg "Connect to localhost:$PORT"
- DEBUGGER_OPTS="-agentpath:${AGENTPATH}=transport=dt_socket,address=$PORT,server=y,suspend=y"
-fi
-
-for agent in "${WITH_AGENT[@]}"; do
- FLAGS="${FLAGS} -agentpath:${agent}"
-done
-
-if [ "$USE_JVMTI" = "y" ]; then
- if [ "$USE_JVM" = "n" ]; then
- plugin=libopenjdkjvmtid.so
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- plugin=libopenjdkjvmti.so
- fi
- # We used to add flags here that made the runtime debuggable but that is not
- # needed anymore since the plugin can do it for us now.
- FLAGS="${FLAGS} -Xplugin:${plugin}"
-
- # For jvmti tests, set the threshold of compilation to 1, so we jit early to
- # provide better test coverage for jvmti + jit. This means we won't run
- # the default --jit configuration but it is not too important test scenario for
- # jvmti tests. This is art specific flag, so don't use it with jvm.
- FLAGS="${FLAGS} -Xjitthreshold:1"
- fi
-fi
-
-# Add the libdir to the argv passed to the main function.
-if [ "$ADD_LIBDIR_ARGUMENTS" = "y" ]; then
- if [[ "$HOST" = "y" ]]; then
- ARGS="${ARGS} ${ANDROID_HOST_OUT}/${TEST_DIRECTORY}/"
- else
- ARGS="${ARGS} /data/${TEST_DIRECTORY}/art/${ISA}/"
- fi
-fi
-if [ "$IS_JVMTI_TEST" = "y" ]; then
- agent=libtiagentd.so
- lib=tiagentd
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- agent=libtiagent.so
- lib=tiagent
- fi
-
- ARGS="${ARGS} ${lib}"
- if [[ "$USE_JVM" = "y" ]]; then
- FLAGS="${FLAGS} -agentpath:${ANDROID_HOST_OUT}/nativetest64/${agent}=${TEST_NAME},jvm"
- else
- FLAGS="${FLAGS} -agentpath:${agent}=${TEST_NAME},art"
- fi
-fi
-
-if [[ "$JVMTI_STRESS" = "y" ]]; then
- agent=libtistressd.so
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- agent=libtistress.so
- fi
-
- # Just give it a default start so we can always add ',' to it.
- agent_args="jvmti-stress"
- if [[ "$JVMTI_REDEFINE_STRESS" = "y" ]]; then
- # We really cannot do this on RI so don't both passing it in that case.
- if [[ "$USE_JVM" = "n" ]]; then
- agent_args="${agent_args},redefine"
- fi
- fi
- if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then
- agent_args="${agent_args},field"
- fi
- if [[ "$JVMTI_STEP_STRESS" = "y" ]]; then
- agent_args="${agent_args},step"
- fi
- if [[ "$JVMTI_TRACE_STRESS" = "y" ]]; then
- agent_args="${agent_args},trace"
- fi
- # In the future add onto this;
- if [[ "$USE_JVM" = "y" ]]; then
- FLAGS="${FLAGS} -agentpath:${ANDROID_HOST_OUT}/nativetest64/${agent}=${agent_args}"
- else
- FLAGS="${FLAGS} -agentpath:${agent}=${agent_args}"
- fi
-fi
-
-if [ "$USE_JVM" = "y" ]; then
- export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
- # Some jvmti tests are flaky without -Xint on the RI.
- if [ "$IS_JVMTI_TEST" = "y" ]; then
- FLAGS="${FLAGS} -Xint"
- fi
- # Xmx is necessary since we don't pass down the ART flags to JVM.
- # We pass the classes2 path whether it's used (src-multidex) or not.
- cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 ${FLAGS} $MAIN $@ ${ARGS}"
- if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline
- fi
- if [ "$CREATE_RUNNER" = "y" ]; then
- echo "#!/bin/bash" > runit.sh
- echo "export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\""
- echo $cmdline >> runit.sh
- chmod u+x runit.sh
- echo "Runnable test script written to $PWD/runit.sh"
- else
- $cmdline
- fi
- exit
-fi
-
-readonly b_path=$(get_apex_bootclasspath ${HOST})
-readonly b_path_locations=$(get_apex_bootclasspath_locations ${HOST})
-
-BCPEX=
-if [ -f "$TEST_NAME-bcpex.jar" ] ; then
- BCPEX=":$DEX_LOCATION/$TEST_NAME-bcpex.jar"
-fi
-
-# Pass down the bootclasspath
-FLAGS="${FLAGS} -Xbootclasspath:${b_path}${BCPEX}"
-FLAGS="${FLAGS} -Xbootclasspath-locations:${b_path_locations}${BCPEX}"
-COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xbootclasspath:${b_path}"
-COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xbootclasspath-locations:${b_path_locations}"
-
-if [ "$HAVE_IMAGE" = "n" ]; then
- # Disable image dex2oat - this will forbid the runtime to patch or compile an image.
- FLAGS="${FLAGS} -Xnoimage-dex2oat"
-
- # We'll abuse a second flag here to test different behavior. If --relocate, use the
- # existing image - relocation will fail as patching is disallowed. If --no-relocate,
- # pass a non-existent image - compilation will fail as dex2oat is disallowed.
- if [ "${RELOCATE}" = "n" ] ; then
- BOOT_IMAGE="/system/non-existent/boot.art"
- fi
- # App images cannot be generated without a boot image.
- APP_IMAGE="n"
-fi
-DALVIKVM_BOOT_OPT="-Ximage:${BOOT_IMAGE}"
-
-if [ "$USE_GDB_DEX2OAT" = "y" ]; then
- if [ "$HOST" = "n" ]; then
- echo "The --gdb-dex2oat option is not yet implemented for target." >&2
- exit 1
- fi
-fi
-
-if [ "$USE_GDB" = "y" ]; then
- if [ "$USE_GDBSERVER" = "y" ]; then
- error_msg "Cannot pass both --gdb and --gdbserver at the same time!"
- exit 1
- elif [ "$HOST" = "n" ]; then
- # We might not have any hostname resolution if we are using a chroot.
- GDB="$GDBSERVER_DEVICE --no-startup-with-shell 127.0.0.1$GDBSERVER_PORT"
- else
- if [ `uname` = "Darwin" ]; then
- GDB=lldb
- GDB_ARGS="$GDB_ARGS -- $DALVIKVM"
- DALVIKVM=
- else
- GDB=gdb
- GDB_ARGS="$GDB_ARGS --args $DALVIKVM"
- # Enable for Emacs "M-x gdb" support. TODO: allow extra gdb arguments on command line.
- # gdbargs="--annotate=3 $gdbargs"
- fi
- fi
-elif [ "$USE_GDBSERVER" = "y" ]; then
- if [ "$HOST" = "n" ]; then
- # We might not have any hostname resolution if we are using a chroot.
- GDB="$GDBSERVER_DEVICE --no-startup-with-shell 127.0.0.1$GDBSERVER_PORT"
- else
- GDB="$GDBSERVER_HOST $GDBSERVER_PORT"
- fi
-fi
-
-if [ "$INTERPRETER" = "y" ]; then
- INT_OPTS="${INT_OPTS} -Xint"
-fi
-
-if [ "$JIT" = "y" ]; then
- INT_OPTS="${INT_OPTS} -Xusejit:true"
-else
- INT_OPTS="${INT_OPTS} -Xusejit:false"
-fi
-
-if [ "$INTERPRETER" = "y" ] || [ "$JIT" = "y" ]; then
- if [ "$VERIFY" = "y" ] ; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify"
- elif [ "$VERIFY" = "s" ]; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=extract"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=extract"
- DEX_VERIFY="${DEX_VERIFY} -Xverify:softfail"
- else # VERIFY = "n"
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=assume-verified"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=assume-verified"
- DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
- fi
-fi
-
-JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-
-COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate"
-if [ "$RELOCATE" = "y" ]; then
- FLAGS="${FLAGS} -Xrelocate"
-else
- FLAGS="$FLAGS -Xnorelocate"
-fi
-
-if [ "$BIONIC" = "y" ]; then
- # This is the location that soong drops linux_bionic builds. Despite being
- # called linux_bionic-x86 the build is actually amd64 (x86_64) only.
- if [ ! -e "$OUT_DIR/soong/host/linux_bionic-x86" ]; then
- error_msg "linux_bionic-x86 target doesn't seem to have been built!"
- exit 1
- fi
- # Set TIMEOUT_DUMPER manually so it works even with apex's
- TIMEOUT_DUMPER=$OUT_DIR/soong/host/linux_bionic-x86/bin/signal_dumper
-fi
-
-# Prevent test from silently falling back to interpreter in no-prebuild mode. This happens
-# when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking
-# full path to dex, stripping leading '/', appending '@classes.vdex' and changing every
-# remaining '/' into '@'.
-if [ "$HOST" = "y" ]; then
- max_filename_size=$(getconf NAME_MAX $DEX_LOCATION)
-else
- # There is no getconf on device, fallback to standard value.
- # See NAME_MAX in kernel <linux/limits.h>
- max_filename_size=255
-fi
-# Compute VDEX_NAME.
-DEX_LOCATION_STRIPPED="${DEX_LOCATION#/}"
-VDEX_NAME="${DEX_LOCATION_STRIPPED//\//@}@$TEST_NAME.jar@classes.vdex"
-if [ ${#VDEX_NAME} -gt $max_filename_size ]; then
- echo "Dex location path too long:"
- error_msg "$VDEX_NAME is ${#VDEX_NAME} character long, and the limit is $max_filename_size."
- exit 1
-fi
-
-if [ "$HOST" = "y" ]; then
- # On host, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the `bin`
- # directory under the "Android Root" (usually `out/host/linux-x86`).
- #
- # TODO(b/130295968): Adjust this if/when ART host artifacts are installed
- # under the ART root (usually `out/host/linux-x86/com.android.art`).
- ANDROID_ART_BIN_DIR=$ANDROID_ROOT/bin
-else
- # On target, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the ART
- # APEX's `bin` directory. This means the linker will observe the ART APEX
- # linker configuration file (`/apex/com.android.art/etc/ld.config.txt`) for
- # these binaries.
- ANDROID_ART_BIN_DIR=$ANDROID_ART_ROOT/bin
-fi
-
-profman_cmdline="true"
-dex2oat_cmdline="true"
-vdex_cmdline="true"
-dm_cmdline="true"
-mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
-strip_cmdline="true"
-sync_cmdline="true"
-linkroot_cmdline="true"
-linkroot_overlay_cmdline="true"
-setupapex_cmdline="true"
-installapex_cmdline="true"
-installapex_test_cmdline="true"
-
-linkdirs() {
- find "$1" -maxdepth 1 -mindepth 1 -type d | xargs -i ln -sf '{}' "$2"
- # Also create a link for the boot image.
- ln -sf $ANDROID_HOST_OUT/apex/art_boot_images "$2"
-}
-
-if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
- mkdir_locations="${mkdir_locations} ${ANDROID_ROOT}"
- linkroot_cmdline="linkdirs ${ANDROID_HOST_OUT} ${ANDROID_ROOT}"
- if [ "${BIONIC}" = "y" ]; then
- # TODO Make this overlay more generic.
- linkroot_overlay_cmdline="linkdirs $OUT_DIR/soong/host/linux_bionic-x86 ${ANDROID_ROOT}"
- fi
- # Replace the boot image to a location expected by the runtime.
- DALVIKVM_BOOT_OPT="-Ximage:${ANDROID_ROOT}/art_boot_images/javalib/boot.art"
-fi
-
-if [ "$USE_ZIPAPEX" = "y" ]; then
- # TODO Currently this only works for linux_bionic zipapexes because those are
- # stripped and so small enough that the ulimit doesn't kill us.
- mkdir_locations="${mkdir_locations} $DEX_LOCATION/zipapex"
- zip_options="-qq"
- if [ "$DEV_MODE" = "y" ]; then
- zip_options=""
- fi
- setupapex_cmdline="unzip -o -u ${zip_options} ${ZIPAPEX_LOC} apex_payload.zip -d ${DEX_LOCATION}"
- installapex_cmdline="unzip -o -u ${zip_options} ${DEX_LOCATION}/apex_payload.zip -d ${DEX_LOCATION}/zipapex"
- ANDROID_ART_BIN_DIR=$DEX_LOCATION/zipapex/bin
-elif [ "$USE_EXTRACTED_ZIPAPEX" = "y" ]; then
- # Just symlink the zipapex binaries
- ANDROID_ART_BIN_DIR=$DEX_LOCATION/zipapex/bin
- # Force since some tests manually run this file twice.
- ln_options=""
- if [ "$DEV_MODE" = "y" ]; then
- ln_options="--verbose"
- fi
- # If the ${RUN} is executed multiple times we don't need to recreate the link
- installapex_test_cmdline="test -L ${DEX_LOCATION}/zipapex"
- installapex_cmdline="ln -s -f ${ln_options} ${EXTRACTED_ZIPAPEX_LOC} ${DEX_LOCATION}/zipapex"
-fi
-
-# PROFILE takes precedence over RANDOM_PROFILE, since PROFILE tests require a
-# specific profile to run properly.
-if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- profman_cmdline="$ANDROID_ART_BIN_DIR/profman \
- --apk=$DEX_LOCATION/$TEST_NAME.jar \
- --dex-location=$DEX_LOCATION/$TEST_NAME.jar"
- if [ -f "$TEST_NAME-ex.jar" ] && [ "$SECONDARY_COMPILATION" = "y" ] ; then
- profman_cmdline="${profman_cmdline} \
- --apk=$DEX_LOCATION/$TEST_NAME-ex.jar \
- --dex-location=$DEX_LOCATION/$TEST_NAME-ex.jar"
- fi
- COMPILE_FLAGS="${COMPILE_FLAGS} --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
- FLAGS="${FLAGS} -Xcompiler-option --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
- if [ "$PROFILE" = "y" ]; then
- profman_cmdline="${profman_cmdline} --create-profile-from=$DEX_LOCATION/profile \
- --reference-profile-file=$DEX_LOCATION/$TEST_NAME.prof"
- else
- profman_cmdline="${profman_cmdline} --generate-test-profile=$DEX_LOCATION/$TEST_NAME.prof \
- --generate-test-profile-seed=0"
- fi
-fi
-
-function get_prebuilt_lldb_path {
- local CLANG_BASE="prebuilts/clang/host"
- local CLANG_VERSION="$("$ANDROID_BUILD_TOP/build/soong/scripts/get_clang_version.py")"
- case "$(uname -s)" in
- Darwin)
- local PREBUILT_NAME="darwin-x86"
- ;;
- Linux)
- local PREBUILT_NAME="linux-x86"
- ;;
- *)
- >&2 echo "Unknown host $(uname -s). Unsupported for debugging dex2oat with LLDB."
- return
- ;;
- esac
- local CLANG_PREBUILT_HOST_PATH="$ANDROID_BUILD_TOP/$CLANG_BASE/$PREBUILT_NAME/$CLANG_VERSION"
- # If the clang prebuilt directory exists and the reported clang version
- # string does not, then it is likely that the clang version reported by the
- # get_clang_version.py script does not match the expected directory name.
- if [ -d "${ANDROID_BUILD_TOP}/${CLANG_BASE}/${PREBUILT_NAME}" ] && \
- [ ! -d "${CLANG_PREBUILT_HOST_PATH}" ]; then
- error_msg "The prebuilt clang directory exists, but the specific clang"\
- "\nversion reported by get_clang_version.py does not exist in that path."\
- "\nPlease make sure that the reported clang version resides in the"\
- "\nprebuilt clang directory!"
- exit 1
- fi
-
- # The lldb-server binary is a dependency of lldb.
- export LLDB_DEBUGSERVER_PATH="${CLANG_PREBUILT_HOST_PATH}/runtimes_ndk_cxx/x86_64/lldb-server"
-
- # Set the current terminfo directory to TERMINFO so that LLDB can read the
- # termcap database.
- local terminfo_regexp_path='\/.*\/*terminfo\/'
- if [[ $(infocmp) =~ $terminfo_regexp_path ]] ; then
- export TERMINFO="${BASH_REMATCH[0]}"
- fi
-
- prebuilt_lldb_path="$CLANG_PREBUILT_HOST_PATH/bin/lldb.sh"
-}
-
-function write_dex2oat_cmdlines {
- local name="$1"
-
- local class_loader_context=""
- local enable_app_image=false
- if [ "$APP_IMAGE" = "y" ]; then
- enable_app_image=true
- fi
-
- # If the name ends in -ex then this is a secondary dex file
- if [ "${name:${#name}-3}" = "-ex" ]; then
- # Lazily realize the default value in case DEX_LOCATION/TEST_NAME change
- if [ "x$SECONDARY_CLASS_LOADER_CONTEXT" = "x" ]; then
- if [ "x$SECONDARY_DEX" = "x" ]; then
- # Tests without `--secondary` load the "-ex" jar in a separate PathClassLoader
- # that is a child of the main PathClassLoader. If the class loader is constructed
- # in any other way, the test needs to specify the secondary CLC explicitly.
- SECONDARY_CLASS_LOADER_CONTEXT="PCL[];PCL[$DEX_LOCATION/$TEST_NAME.jar]"
- else
- # Tests with `--secondary` load the `-ex` jar a part of the main PathClassLoader.
- SECONDARY_CLASS_LOADER_CONTEXT="PCL[$DEX_LOCATION/$TEST_NAME.jar]"
- fi
- fi
- class_loader_context="'--class-loader-context=$SECONDARY_CLASS_LOADER_CONTEXT'"
- $enable_app_image && [ "$SECONDARY_APP_IMAGE" = "y" ] || enable_app_image=false
- fi
-
- local app_image=""
- $enable_app_image && app_image="--app-image-file=$DEX_LOCATION/oat/$ISA/$name.art --resolve-startup-const-strings=true"
-
- if [ "$USE_GDB_DEX2OAT" = "y" ]; then
- get_prebuilt_lldb_path
- GDB_DEX2OAT="$prebuilt_lldb_path -f"
- GDB_DEX2OAT_ARGS+=" -- "
- fi
-
- local dex2oat_binary
- dex2oat_binary=${DEX2OAT_DEBUG_BINARY}
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- dex2oat_binary=${DEX2OAT_NDEBUG_BINARY}
- fi
- dex2oat_cmdline="$INVOKE_WITH $GDB_DEX2OAT \
- $ANDROID_ART_BIN_DIR/$dex2oat_binary \
- $GDB_DEX2OAT_ARGS \
- $COMPILE_FLAGS \
- --boot-image=${BOOT_IMAGE} \
- --dex-file=$DEX_LOCATION/$name.jar \
- --oat-file=$DEX_LOCATION/oat/$ISA/$name.odex \
- "$app_image" \
- --generate-mini-debug-info \
- --instruction-set=$ISA \
- $class_loader_context"
- if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
- dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
- fi
-
- # Add in a timeout. This is important for testing the compilation/verification time of
- # pathological cases. We do not append a timeout when debugging dex2oat because we
- # do not want it to exit while debugging.
- # Note: as we don't know how decent targets are (e.g., emulator), only do this on the host for
- # now. We should try to improve this.
- # The current value is rather arbitrary. run-tests should compile quickly.
- # Watchdog timeout is in milliseconds so add 3 '0's to the dex2oat timeout.
- if [ "$HOST" != "n" ] && [ "$USE_GDB_DEX2OAT" != "y" ]; then
- # Use SIGRTMIN+2 to try to dump threads.
- # Use -k 1m to SIGKILL it a minute later if it hasn't ended.
- dex2oat_cmdline="timeout -k ${DEX2OAT_TIMEOUT}s -s SIGRTMIN+2 ${DEX2OAT_RT_TIMEOUT}s ${dex2oat_cmdline} --watchdog-timeout=${DEX2OAT_TIMEOUT}000"
- fi
- if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
- elif [ "$TEST_VDEX" = "y" ]; then
- if [ "$VDEX_ARGS" = "" ]; then
- # If no arguments need to be passed, just delete the odex file so that the runtime only picks up the vdex file.
- vdex_cmdline="rm $DEX_LOCATION/oat/$ISA/$name.odex"
- else
- vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
- fi
- elif [ "$TEST_DEX2OAT_DM" = "y" ]; then
- vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$name.dm"
- dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
- dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
- elif [ "$TEST_RUNTIME_DM" = "y" ]; then
- dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
- dm_cmdline="zip -qj $DEX_LOCATION/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
- fi
-}
-
-# Enable mini-debug-info for JIT (if JIT is used).
-FLAGS="$FLAGS -Xcompiler-option --generate-mini-debug-info"
-
-if [ "$PREBUILD" = "y" ]; then
- mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA"
-
- # "Primary".
- write_dex2oat_cmdlines "$TEST_NAME"
- dex2oat_cmdline=$(echo $dex2oat_cmdline)
- dm_cmdline=$(echo $dm_cmdline)
- vdex_cmdline=$(echo $vdex_cmdline)
-
- # Enable mini-debug-info for JIT (if JIT is used).
- FLAGS="$FLAGS -Xcompiler-option --generate-mini-debug-info"
-
- if [ -f "$TEST_NAME-ex.jar" ] && [ "$SECONDARY_COMPILATION" = "y" ] ; then
- # "Secondary" for test coverage.
-
- # Store primary values.
- base_dex2oat_cmdline="$dex2oat_cmdline"
- base_dm_cmdline="$dm_cmdline"
- base_vdex_cmdline="$vdex_cmdline"
-
- write_dex2oat_cmdlines "$TEST_NAME-ex"
- dex2oat_cmdline=$(echo $dex2oat_cmdline)
- dm_cmdline=$(echo $dm_cmdline)
- vdex_cmdline=$(echo $vdex_cmdline)
-
- # Concatenate.
- dex2oat_cmdline="$base_dex2oat_cmdline && $dex2oat_cmdline"
- dm_cmdline="$base_dm_cmdline" # Only use primary dm.
- vdex_cmdline="$base_vdex_cmdline && $vdex_cmdline"
- fi
-fi
-
-if [ "$SYNC_BEFORE_RUN" = "y" ]; then
- sync_cmdline="sync"
-fi
-
-DALVIKVM_ISA_FEATURES_ARGS=""
-if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
- DALVIKVM_ISA_FEATURES_ARGS="-Xcompiler-option --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
-fi
-
-# java.io.tmpdir can only be set at launch time.
-TMP_DIR_OPTION=""
-if [ "$HOST" = "n" ]; then
- TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp"
-fi
-
-# The build servers have an ancient version of bash so we cannot use @Q.
-if [ "$USE_GDBSERVER" == "y" ]; then
- printf -v QUOTED_DALVIKVM_BOOT_OPT "%q" "$DALVIKVM_BOOT_OPT"
-else
- QUOTED_DALVIKVM_BOOT_OPT="$DALVIKVM_BOOT_OPT"
-fi
-
-DALVIKVM_CLASSPATH=$DEX_LOCATION/$TEST_NAME.jar
-if [ -f "$TEST_NAME-aotex.jar" ] ; then
- DALVIKVM_CLASSPATH=$DALVIKVM_CLASSPATH:$DEX_LOCATION/$TEST_NAME-aotex.jar
-fi
-DALVIKVM_CLASSPATH=$DALVIKVM_CLASSPATH$SECONDARY_DEX
-
-# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind.
-# b/27185632
-# b/24664297
-
-dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ART_BIN_DIR/$DALVIKVM \
- $GDB_ARGS \
- $FLAGS \
- $DEX_VERIFY \
- -XXlib:$LIB \
- $DEX2OAT \
- $DALVIKVM_ISA_FEATURES_ARGS \
- $ZYGOTE \
- $JNI_OPTS \
- $INT_OPTS \
- $DEBUGGER_OPTS \
- ${QUOTED_DALVIKVM_BOOT_OPT} \
- $TMP_DIR_OPTION \
- -XX:DumpNativeStackOnSigQuit:false \
- -cp $DALVIKVM_CLASSPATH $MAIN $ARGS"
-
-if [ "x$SIMPLEPERF" == xyes ]; then
- dalvikvm_cmdline="simpleperf record ${dalvikvm_cmdline} && simpleperf report"
-fi
-
-sanitize_dex2oat_cmdline() {
- local args=()
- for arg in "$@"; do
- if [ "$arg" = "--class-loader-context=&" ]; then
- arg="--class-loader-context=\&"
- fi
- args+=("$arg")
- done
- echo -n "${args[@]}"
-}
-
-# Remove whitespace.
-dex2oat_cmdline=$(sanitize_dex2oat_cmdline $(echo $dex2oat_cmdline))
-dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
-dm_cmdline=$(echo $dm_cmdline)
-vdex_cmdline=$(sanitize_dex2oat_cmdline $(echo $vdex_cmdline))
-profman_cmdline=$(echo $profman_cmdline)
-
-# Use an empty ASAN_OPTIONS to enable defaults.
-# Note: this is required as envsetup right now exports detect_leaks=0.
-RUN_TEST_ASAN_OPTIONS=""
-
-# Multiple shutdown leaks. b/38341789
-if [ "x$RUN_TEST_ASAN_OPTIONS" != "x" ] ; then
- RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}:"
-fi
-RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}detect_leaks=0"
-
-# For running, we must turn off logging when dex2oat is missing. Otherwise we use
-# the same defaults as for prebuilt: everything when --dev, otherwise errors and above only.
-if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then
- if [ "$DEV_MODE" = "y" ]; then
- export ANDROID_LOG_TAGS='*:d'
- elif [ "$HAVE_IMAGE" = "n" ]; then
- # All tests would log the error of missing image. Be silent here and only log fatal
- # events.
- export ANDROID_LOG_TAGS='*:s'
- else
- # We are interested in LOG(ERROR) output.
- export ANDROID_LOG_TAGS='*:e'
- fi
-fi
-
-if [ "$HOST" = "n" ]; then
- adb root > /dev/null
- adb wait-for-device
- if [ "$QUIET" = "n" ]; then
- adb shell rm -rf $CHROOT_DEX_LOCATION
- adb shell mkdir -p $CHROOT_DEX_LOCATION
- adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION
- adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION
- adb push $TEST_NAME-aotex.jar $CHROOT_DEX_LOCATION
- adb push $TEST_NAME-bcpex.jar $CHROOT_DEX_LOCATION
- if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- adb push profile $CHROOT_DEX_LOCATION
- fi
- # Copy resource folder
- if [ -d res ]; then
- adb push res $CHROOT_DEX_LOCATION
- fi
- else
- adb shell rm -rf $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb shell mkdir -p $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME-aotex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME-bcpex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- adb push profile $CHROOT_DEX_LOCATION >/dev/null 2>&1
- fi
- # Copy resource folder
- if [ -d res ]; then
- adb push res $CHROOT_DEX_LOCATION >/dev/null 2>&1
- fi
- fi
-
- # Populate LD_LIBRARY_PATH.
- LD_LIBRARY_PATH=
- if [ "$ANDROID_ROOT" != "/system" ]; then
- # Current default installation is dalvikvm 64bits and dex2oat 32bits,
- # so we can only use LD_LIBRARY_PATH when testing on a local
- # installation.
- LD_LIBRARY_PATH="$ANDROID_ROOT/$LIBRARY_DIRECTORY"
- fi
-
- # This adds libarttest(d).so to the default linker namespace when dalvikvm
- # is run from /apex/com.android.art/bin. Since that namespace is essentially
- # an alias for the com_android_art namespace, that gives libarttest(d).so
- # full access to the internal ART libraries.
- LD_LIBRARY_PATH="/data/$TEST_DIRECTORY/com.android.art/lib${SUFFIX64}:$LD_LIBRARY_PATH"
- if [ "$TEST_IS_NDEBUG" = "y" ]; then dlib=""; else dlib="d"; fi
- art_test_internal_libraries=(
- libartagent${dlib}.so
- libarttest${dlib}.so
- libtiagent${dlib}.so
- libtistress${dlib}.so
- )
- art_test_internal_libraries="${art_test_internal_libraries[*]}"
- NATIVELOADER_DEFAULT_NAMESPACE_LIBS="${art_test_internal_libraries// /:}"
- dlib=
- art_test_internal_libraries=
-
- # Needed to access the test's Odex files.
- LD_LIBRARY_PATH="$DEX_LOCATION/oat/$ISA:$LD_LIBRARY_PATH"
- # Needed to access the test's native libraries (see e.g. 674-hiddenapi,
- # which generates `libhiddenapitest_*.so` libraries in `$DEX_LOCATION`).
- LD_LIBRARY_PATH="$DEX_LOCATION:$LD_LIBRARY_PATH"
-
- # Prepend directories to the path on device.
- PREPEND_TARGET_PATH=$ANDROID_ART_BIN_DIR
- if [ "$ANDROID_ROOT" != "/system" ]; then
- PREPEND_TARGET_PATH="$PREPEND_TARGET_PATH:$ANDROID_ROOT/bin"
- fi
-
- timeout_dumper_cmd=
-
- # Check whether signal_dumper is available.
- if [ "$TIMEOUT_DUMPER" = signal_dumper ] ; then
- # Chroot? Use as prefix for tests.
- TIMEOUT_DUMPER_PATH_PREFIX=
- if [ -n "$CHROOT" ]; then
- TIMEOUT_DUMPER_PATH_PREFIX="$CHROOT/"
- fi
-
- # Testing APEX?
- if adb shell "test -x ${TIMEOUT_DUMPER_PATH_PREFIX}/apex/com.android.art/bin/signal_dumper" ; then
- TIMEOUT_DUMPER="/apex/com.android.art/bin/signal_dumper"
- # Is it in /system/bin?
- elif adb shell "test -x ${TIMEOUT_DUMPER_PATH_PREFIX}/system/bin/signal_dumper" ; then
- TIMEOUT_DUMPER="/system/bin/signal_dumper"
- else
- TIMEOUT_DUMPER=
- fi
- else
- TIMEOUT_DUMPER=
- fi
-
- if [ ! -z "$TIMEOUT_DUMPER" ] ; then
- # Use "-l" to dump to logcat. That is convenience for the build bot crash symbolization.
- # Use exit code 124 for toybox timeout (b/141007616).
- timeout_dumper_cmd="${TIMEOUT_DUMPER} -l -s 15 -e 124"
- fi
-
- timeout_prefix=
- if [ "$TIME_OUT" = "timeout" ]; then
- # Add timeout command if time out is desired.
- #
- # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
- # will induce a full thread dump before killing the process. To ensure any issues in
- # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
- # child.
- # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
- timeout_prefix="timeout --foreground -k 120s ${TIME_OUT_VALUE}s ${timeout_dumper_cmd} $cmdline"
- fi
-
- # Create a script with the command. The command can get longer than the longest
- # allowed adb command and there is no way to get the exit status from a adb shell
- # command. Dalvik cache is cleaned before running to make subsequent executions
- # of the script follow the same runtime path.
- cmdline="cd $DEX_LOCATION && \
- export ASAN_OPTIONS=$RUN_TEST_ASAN_OPTIONS && \
- export ANDROID_DATA=$DEX_LOCATION && \
- export DEX_LOCATION=$DEX_LOCATION && \
- export ANDROID_ROOT=$ANDROID_ROOT && \
- export ANDROID_I18N_ROOT=$ANDROID_I18N_ROOT && \
- export ANDROID_ART_ROOT=$ANDROID_ART_ROOT && \
- export ANDROID_TZDATA_ROOT=$ANDROID_TZDATA_ROOT && \
- export ANDROID_LOG_TAGS=$ANDROID_LOG_TAGS && \
- rm -rf ${DEX_LOCATION}/dalvik-cache/ && \
- mkdir -p ${mkdir_locations} && \
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
- export NATIVELOADER_DEFAULT_NAMESPACE_LIBS=$NATIVELOADER_DEFAULT_NAMESPACE_LIBS && \
- export PATH=$PREPEND_TARGET_PATH:\$PATH && \
- $profman_cmdline && \
- $dex2oat_cmdline && \
- $dm_cmdline && \
- $vdex_cmdline && \
- $strip_cmdline && \
- $sync_cmdline && \
- $timeout_prefix $dalvikvm_cmdline"
-
- cmdfile=$(mktemp cmd-XXXX --suffix "-$TEST_NAME")
- echo "$cmdline" >> $cmdfile
-
- if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline
- if [ "$USE_GDB" = "y" ] || [ "$USE_GDBSERVER" = "y" ]; then
- echo "Forward ${GDBSERVER_PORT} to local port and connect GDB"
- fi
- fi
-
- if [ "$QUIET" = "n" ]; then
- adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh
- else
- adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh >/dev/null 2>&1
- fi
-
- exit_status=0
- if [ "$DRY_RUN" != "y" ]; then
- if [ -n "$CHROOT" ]; then
- adb shell chroot "$CHROOT" sh $DEX_LOCATION/cmdline.sh
- else
- adb shell sh $DEX_LOCATION/cmdline.sh
- fi
- exit_status=$?
- fi
-
- rm -f $cmdfile
- exit $exit_status
-else
- # Host run.
- export ANDROID_PRINTF_LOG=brief
-
- export ANDROID_DATA="$DEX_LOCATION"
- export ANDROID_ROOT="${ANDROID_ROOT}"
- export ANDROID_I18N_ROOT="${ANDROID_I18N_ROOT}"
- export ANDROID_ART_ROOT="${ANDROID_ART_ROOT}"
- export ANDROID_TZDATA_ROOT="${ANDROID_TZDATA_ROOT}"
- if [ "$USE_ZIPAPEX" = "y" ] || [ "$USE_EXRACTED_ZIPAPEX" = "y" ]; then
- # Put the zipapex files in front of the ld-library-path
- export LD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- export DYLD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- else
- export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- fi
- export PATH="$PATH:$ANDROID_ART_BIN_DIR"
-
- # Temporarily disable address space layout randomization (ASLR).
- # This is needed on the host so that the linker loads core.oat at the necessary address.
- export LD_USE_LOAD_BIAS=1
-
- cmdline="$dalvikvm_cmdline"
-
- if [ "$TIME_OUT" = "gdb" ]; then
- if [ `uname` = "Darwin" ]; then
- # Fall back to timeout on Mac.
- TIME_OUT="timeout"
- elif [ "$ISA" = "x86" ]; then
- # prctl call may fail in 32-bit on an older (3.2) 64-bit Linux kernel. Fall back to timeout.
- TIME_OUT="timeout"
- else
- # Check if gdb is available.
- gdb --eval-command="quit" > /dev/null 2>&1
- if [ $? != 0 ]; then
- # gdb isn't available. Fall back to timeout.
- TIME_OUT="timeout"
- fi
- fi
- fi
-
- if [ "$TIME_OUT" = "timeout" ]; then
- # Add timeout command if time out is desired.
- #
- # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
- # will induce a full thread dump before killing the process. To ensure any issues in
- # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
- # child.
- # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
- cmdline="timeout --foreground -k 120s ${TIME_OUT_VALUE}s ${TIMEOUT_DUMPER} -s 15 $cmdline"
- fi
-
- if [ "$DEV_MODE" = "y" ]; then
- for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT ANDROID_I18N_ROOT ANDROID_TZDATA_ROOT ANDROID_ART_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do
- echo EXPORT $var=${!var}
- done
- echo "$(declare -f linkdirs)"
- echo "mkdir -p ${mkdir_locations} && $setupapex_cmdline && ( $installapex_test_cmdline || $installapex_cmdline ) && $linkroot_cmdline && $linkroot_overlay_cmdline && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
- fi
-
- cd $ANDROID_BUILD_TOP
-
- # Make sure we delete any existing compiler artifacts.
- # This enables tests to call the RUN script multiple times in a row
- # without worrying about interference.
- rm -rf ${DEX_LOCATION}/oat
- rm -rf ${DEX_LOCATION}/dalvik-cache/
-
- export ASAN_OPTIONS=$RUN_TEST_ASAN_OPTIONS
-
- mkdir -p ${mkdir_locations} || exit 1
- $setupapex_cmdline || { echo "zipapex extraction failed." >&2 ; exit 2; }
- $installapex_test_cmdline || $installapex_cmdline || { echo "zipapex install failed. cmd was: ${installapex_test_cmdline} || ${installapex_cmdline}." >&2; find ${mkdir_locations} -type f >&2; exit 2; }
- $linkroot_cmdline || { echo "create symlink android-root failed." >&2 ; exit 2; }
- $linkroot_overlay_cmdline || { echo "overlay android-root failed." >&2 ; exit 2; }
- $profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
- eval "$dex2oat_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
- eval "$dm_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
- eval "$vdex_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
- $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
- $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
-
- if [ "$CREATE_RUNNER" = "y" ]; then
- echo "#!/bin/bash" > ${DEX_LOCATION}/runit.sh
- for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT ANDROID_I18N_ROOT ANDROID_TZDATA_ROOT ANDROID_ART_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do
- echo export $var="${!var}" >> ${DEX_LOCATION}/runit.sh
- done
- if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline >> ${DEX_LOCATION}/runit.sh
- else
- echo 'STDERR=$(mktemp)' >> ${DEX_LOCATION}/runit.sh
- echo 'STDOUT=$(mktemp)' >> ${DEX_LOCATION}/runit.sh
- echo $cmdline '>${STDOUT} 2>${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo 'if diff ${STDOUT} $ANDROID_DATA/expected-stdout.txt; then' \
- >> ${DEX_LOCATION}/runit.sh
- echo ' rm -f ${STDOUT} ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' exit 0' >> ${DEX_LOCATION}/runit.sh
- echo 'elif diff ${STDERR} $ANDROID_DATA/expected-stderr.txt; then' \
- >> ${DEX_LOCATION}/runit.sh
- echo ' rm -f ${STDOUT} ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' exit 0' >> ${DEX_LOCATION}/runit.sh
- echo 'else' >> ${DEX_LOCATION}/runit.sh
- echo ' echo STDOUT:' >> ${DEX_LOCATION}/runit.sh
- echo ' cat ${STDOUT}' >> ${DEX_LOCATION}/runit.sh
- echo ' echo STDERR:' >> ${DEX_LOCATION}/runit.sh
- echo ' cat ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' rm -f ${STDOUT} ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' exit 1' >> ${DEX_LOCATION}/runit.sh
- echo 'fi' >> ${DEX_LOCATION}/runit.sh
- fi
- chmod u+x $DEX_LOCATION/runit.sh
- echo "Runnable test script written to ${DEX_LOCATION}/runit.sh"
- fi
- if [ "$DRY_RUN" = "y" ]; then
- exit 0
- fi
-
- if [ "$USE_GDB" = "y" ]; then
- # When running under gdb, we cannot do piping and grepping...
- $cmdline "$@"
- elif [ "$USE_GDBSERVER" = "y" ]; then
- echo "Connect to $GDBSERVER_PORT"
- # When running under gdb, we cannot do piping and grepping...
- $cmdline "$@"
- else
- if [ "$TIME_OUT" != "gdb" ]; then
- trap 'kill -INT -$pid' INT
- $cmdline "$@" & pid=$!
- wait $pid
- exit_value=$?
- # Add extra detail if time out is enabled.
- if [ $exit_value = 124 ] && [ "$TIME_OUT" = "timeout" ]; then
- echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
- fi
- exit $exit_value
- else
- # With a thread dump that uses gdb if a timeout.
- trap 'kill -INT -$pid' INT
- $cmdline "$@" & pid=$!
- # Spawn a watcher process.
- ( sleep $TIME_OUT_VALUE && \
- echo "##### Thread dump using gdb on test timeout" && \
- ( gdb -q -p $pid --eval-command="info thread" --eval-command="thread apply all bt" \
- --eval-command="call exit(124)" --eval-command=quit || \
- kill $pid )) 2> /dev/null & watcher=$!
- wait $pid
- test_exit_status=$?
- pkill -P $watcher 2> /dev/null # kill the sleep which will in turn end the watcher as well
- if [ $test_exit_status = 0 ]; then
- # The test finished normally.
- exit 0
- else
- # The test failed or timed out.
- if [ $test_exit_status = 124 ]; then
- # The test timed out.
- echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
- fi
- exit $test_exit_status
- fi
- fi
- fi
-fi
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 873a6b2..44ba3e9 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -81,7 +81,7 @@
},
{
"tests" : "629-vdex-speed",
- "variant": "interp-ac | interpreter | jit",
+ "variant": "interp-ac | interpreter | no-prebuild | debuggable | trace | stream",
"description": "629 requires compilation."
},
{
@@ -339,12 +339,6 @@
"variant": "optimizing | regalloc_gc"
},
{
- "tests": "089-many-methods",
- "description": "The test tests a build failure",
- "env_vars": {"ART_TEST_BISECTION": "true"},
- "variant": "optimizing | regalloc_gc"
- },
- {
"tests": ["018-stack-overflow",
"116-nodex2oat",
"118-noimage-dex2oat",
@@ -383,16 +377,10 @@
"variant": "interp-ac"
},
{
- "tests": ["629-vdex-speed",
- "634-vdex-duplicate"],
- "description": ["Profile driven dexlayout does not work with vdex or dex verifier."],
- "variant": "speed-profile"
- },
- {
"test_patterns": ["616-cha.*"],
"description": ["cha tests rely on knowing the exact set of optimizations available. ",
"Debuggable runtimes change the set of optimizations."],
- "variant": "debuggable"
+ "variant": "debuggable | trace | stream"
},
{
"test_patterns": ["616-cha.*"],
@@ -550,6 +538,7 @@
"674-hiddenapi",
"690-hiddenapi-same-name-methods",
"804-class-extends-itself",
+ "842-vdex-hard-failure",
"921-hello-failure",
"999-redefine-hiddenapi"
],
@@ -712,10 +701,9 @@
"description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
},
{
- "tests": ["137-cfi", "629-vdex-speed"],
- "description": [ "Tests require speed compilation which is no longer the default for",
- "no-prebuild or no-image configs."],
- "variant": "no-prebuild | no-image"
+ "tests": ["137-cfi"],
+ "description": [ "Tests require speed compilation which is no longer the default for no-prebuild"],
+ "variant": "no-prebuild"
},
{
"tests": ["059-finalizer-throw", "063-process-manager"],
@@ -760,20 +748,13 @@
"bug": "b/64683522"
},
{
- "tests": ["628-vdex",
- "629-vdex-speed",
- "634-vdex-duplicate"],
- "variant": "cdex-fast",
- "description": ["Tests that depend on input-vdex are not supported with compact dex"]
- },
- {
"tests": ["661-oat-writer-layout"],
"variant": "interp-ac | interpreter | jit | jit-on-first-use | no-prebuild | no-image | trace | redefine-stress | jvmti-stress",
"description": ["Test is designed to only check --optimizing"]
},
{
"tests": ["004-StackWalk"],
- "variant": "speed-profile | interp-ac | interpreter | jit | no-prebuild | no-image | trace | redefine-stress | jvmti-stress | debuggable",
+ "variant": "speed-profile | interp-ac | interpreter | jit | no-prebuild | no-image | trace | redefine-stress | jvmti-stress | debuggable | stream",
"description": ["Test is designed to only check --optimizing"]
},
{
@@ -1074,6 +1055,7 @@
"988-method-trace",
"989-method-trace-throw",
"993-breakpoints",
+ "993-breakpoints-non-debuggable",
"1002-notify-startup",
"1003-metadata-section-strings",
"1336-short-finalizer-timeout",
@@ -1091,13 +1073,21 @@
"1946-list-descriptors",
"1947-breakpoint-redefine-deopt",
"2041-bad-cleaner",
- "2230-profile-save-hotness"
+ "2230-profile-save-hotness",
+ "2245-checker-smali-instance-of-comparison",
+ "2251-checker-irreducible-loop-do-not-inline"
],
"variant": "jvm",
"bug": "b/73888836",
"description": ["Failing on RI. Needs further investigating. Some of these use smali."]
},
{
+ "tests": ["2042-reference-processing",
+ "2043-reference-pauses"],
+ "variant": "jvm",
+ "description": ["Flakey behavior of RI."]
+ },
+ {
"tests": [
"1974-resize-array",
"1975-hello-structural-transformation",
@@ -1169,6 +1159,12 @@
"831-unresolved-field",
"833-background-verification",
"836-32768classes",
+ "837-deopt",
+ "844-exception",
+ "844-exception2",
+ "845-fast-verify",
+ "845-data-image",
+ "846-multidex-data-image",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",
@@ -1219,7 +1215,10 @@
"2036-structural-subclass-shadow",
"2038-hiddenapi-jvmti-ext",
"2040-huge-native-alloc",
- "2238-checker-polymorphic-recursive-inlining"],
+ "2238-checker-polymorphic-recursive-inlining",
+ "2240-tracing-non-invokable-method",
+ "2246-trace-stream",
+ "2254-class-value-before-and-after-u"],
"variant": "jvm",
"description": ["Doesn't run on RI."]
},
@@ -1262,7 +1261,7 @@
"tests": ["141-class-unload", "071-dexfile"],
"variant": "gcstress",
"bug": "b/111543628",
- "description" : ["Test seems to timeout when run with gcstress due to slower unwinding by libbacktrace"]
+ "description" : ["Test seems to timeout when run with gcstress due to slower unwinding by libunwindstack"]
},
{
"tests": ["708-jit-cache-churn"],
@@ -1274,7 +1273,7 @@
"tests": ["712-varhandle-invocations"],
"variant": "gcstress",
"bug": "b/111630237",
- "description": ["Test timing out under gcstress possibly due to slower unwinding by libbacktrace"]
+ "description": ["Test timing out under gcstress possibly due to slower unwinding by libunwindstack"]
},
{
"tests": ["1336-short-finalizer-timeout"],
@@ -1308,7 +1307,7 @@
},
{
"tests": ["1339-dead-reference-safe"],
- "variant": "debuggable",
+ "variant": "debuggable | trace | stream",
"description": [ "Fails to eliminate dead reference when debuggable." ]
},
{
@@ -1366,11 +1365,23 @@
"description": "Interpreting BigInteger.add() is too slow (timeouts)"
},
{
- "tests": ["2029-contended-monitors"],
- "variant": "interpreter | interp-ac | gcstress | trace",
+ "tests": ["2029-contended-monitors", "2043-reference-pauses"],
+ "variant": "interpreter | interp-ac | gcstress | trace | stream",
+ "description": ["Slow tests. Prone to timeouts."]
+ },
+ {
+ "tests": ["2042-reference-processing"],
+ "variant": "interpreter | interp-ac | gcstress | trace | debuggable | stream",
"description": ["Slow test. Prone to timeouts."]
},
{
+ "tests": ["2043-reference-pauses"],
+ "env_vars": {"ART_USE_READ_BARRIER": "false", "ART_DEFAULT_GC_TYPE": "CMS"},
+ "variant": "host",
+ "bug": "b/232459100",
+ "description": ["Fails intermittently for CMS."]
+ },
+ {
"tests": ["096-array-copy-concurrent-gc"],
"variant": "gcstress & debuggable & debug & host",
"bug": "b/149708943",
@@ -1505,5 +1516,45 @@
"variant": "target & ndebug & 64",
"bug": "b/224733324",
"description": ["segfault in VarHandle::GetMethodTypeMatchForAccessMode"]
+ },
+ {
+ "tests": ["2043-reference-pauses"],
+ "env_vars": {"ART_TEST_DEBUG_GC": "true"},
+ "description": ["Test timing out on debug gc."]
+ },
+ {
+ "tests": ["837-deopt"],
+ "description": ["Tests deoptimization and OSR, which never happens when tracing."],
+ "variant": "trace | stream | jit-on-first-use"
+ },
+ {
+ "tests": ["1912-get-set-local-primitive"],
+ "description": ["JVMTI error code used changed in JDK 17."],
+ "bug": "b/243356199",
+ "variant": "jvm"
+ },
+ {
+ "tests": ["715-clinit-implicit-parameter-annotations"],
+ "description": ["Change for Annotation.toString() in JDK 17."],
+ "bug": "b/243500721",
+ "variant": "jvm"
+ },
+ {
+ "tests": ["1907-suspend-list-self-twice"],
+ "description": ["Change of behavior when used with JDK 17."],
+ "bug": "b/243139124",
+ "variant": "jvm"
+ },
+ {
+ "tests": ["1921-suspend-native-recursive-monitor"],
+ "description": ["Change of behavior when used with JDK 17."],
+ "bug": "b/242985234",
+ "variant": "jvm"
+ },
+ {
+ "tests": ["2246-trace-stream"],
+ "env_vars": {"ART_TEST_DEBUG_GC": "true"},
+ "bug": "b/264844668",
+ "description": ["Test timing out on debug gc."]
}
]
diff --git a/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java
index 8c2c5b2..60d7642 100644
--- a/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java
@@ -16,7 +16,6 @@
package com.android.tests.odsign;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
@@ -36,7 +35,8 @@
"/data/misc/apexdata/com.android.art/compos-pending";
/** Maximum time for a slow VM like cuttlefish to boot and finish odrefresh. */
- private static final int VM_ODREFRESH_MAX_SECONDS = 360;
+ // odrefresh overall timeout is currently 480s; add some generous padding for VM startup.
+ private static final int VM_ODREFRESH_MAX_SECONDS = 480 + 60;
/** Waiting time for the job to be scheduled after staging an APEX */
private static final int JOB_CREATION_MAX_SECONDS = 5;
@@ -74,8 +74,6 @@
// Sort by filename (second column) to make comparison easier.
// Filter out compos.info* (which will be deleted at boot) and cache-info.xml
// compos.info.signature since it's only generated by CompOS.
- // TODO(b/210473615): Remove irrelevant APEXes (i.e. those aren't contributing to the
- // classpaths, thus not in the VM) from cache-info.xml.
return assertCommandSucceeds("cd " + path + "; find -type f -exec sha256sum {} \\;"
+ "| grep -v cache-info.xml | grep -v compos.info"
+ "| sort -k2");
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
index 731ea38..2bd24f9 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
@@ -27,6 +27,7 @@
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,10 +47,6 @@
private static final String CACHE_INFO_FILE =
OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME + "/cache-info.xml";
private static final String ODREFRESH_BIN = "odrefresh";
- private static final String ODREFRESH_COMMAND =
- ODREFRESH_BIN + " --partial-compilation --no-refresh --compile";
- private static final String ODREFRESH_MINIMAL_COMMAND =
- ODREFRESH_BIN + " --partial-compilation --no-refresh --minimal --compile";
private static final String TAG = "OdrefreshHostTest";
private static final String ZYGOTE_ARTIFACTS_KEY = TAG + ":ZYGOTE_ARTIFACTS";
@@ -87,11 +84,27 @@
mTestUtils = new OdsignTestUtils(getTestInformation());
}
+ @After
+ public void tearDown() throws Exception {
+ Set<String> artifacts = new HashSet<>();
+ artifacts.addAll(getZygoteArtifacts());
+ artifacts.addAll(getSystemServerArtifacts());
+
+ for (String artifact : artifacts) {
+ if (!getDevice().doesFileExist(artifact)) {
+ // Things went wrong during the test. Run odrefresh to revert to a normal state.
+ mTestUtils.removeCompilationLogToAvoidBackoff();
+ runOdrefresh();
+ break;
+ }
+ }
+ }
+
@Test
public void verifyArtSamegradeUpdateTriggersCompilation() throws Exception {
simulateArtApexUpgrade();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -101,7 +114,7 @@
public void verifyOtherApexSamegradeUpdateTriggersCompilation() throws Exception {
simulateApexUpgrade();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -111,7 +124,7 @@
public void verifyBootClasspathOtaTriggersCompilation() throws Exception {
simulateBootClasspathOta();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -121,7 +134,7 @@
public void verifySystemServerOtaTriggersCompilation() throws Exception {
simulateSystemServerOta();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -137,7 +150,7 @@
mTestUtils.removeCompilationLogToAvoidBackoff();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(remainingArtifacts, timeMs);
assertArtifactsModifiedAfter(missingArtifacts, timeMs);
@@ -158,7 +171,7 @@
"device_config put runtime_native_boot enable_uffd_gc false");
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -169,7 +182,7 @@
"device_config put runtime_native_boot enable_uffd_gc true");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -177,7 +190,7 @@
// Run odrefresh again with the flag unchanged.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -188,7 +201,7 @@
"device_config put runtime_native_boot enable_uffd_gc false");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -201,11 +214,77 @@
}
@Test
+ public void verifySystemServerCompilerFilterOverrideChangeTriggersCompilation()
+ throws Exception {
+ try {
+ // Disable phenotype flag syncing. Potentially, we can set
+ // `set_sync_disabled_for_tests` to `until_reboot`, but setting it to
+ // `persistent` prevents unrelated system crashes/restarts from affecting the
+ // test. `set_sync_disabled_for_tests` is reset in the `finally` block anyway.
+ getDevice()
+ .executeShellV2Command("device_config set_sync_disabled_for_tests persistent");
+
+ // Simulate that the phenotype flag is set to the default value.
+ getDevice()
+ .executeShellV2Command(
+ "device_config put runtime_native_boot"
+ + " systemservercompilerfilter_override");
+
+ long timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should not be re-compiled.
+ assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+ // Simulate that the phenotype flag is set to "speed".
+ getDevice()
+ .executeShellV2Command(
+ "device_config put runtime_native_boot"
+ + " systemservercompilerfilter_override speed");
+
+ timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should be re-compiled.
+ assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+ // Run odrefresh again with the flag unchanged.
+ timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should not be re-compiled.
+ assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+ // Simulate that the phenotype flag is set to "verify".
+ getDevice()
+ .executeShellV2Command(
+ "device_config put runtime_native_boot"
+ + " systemservercompilerfilter_override verify");
+
+ timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should be re-compiled.
+ assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
+ } finally {
+ getDevice().executeShellV2Command("device_config set_sync_disabled_for_tests none");
+ getDevice()
+ .executeShellV2Command(
+ "device_config delete runtime_native_boot"
+ + " systemservercompilerfilter_override");
+ }
+ }
+
+ @Test
public void verifySystemPropertyMismatchTriggersCompilation() throws Exception {
// Change a system property from empty to a value.
getDevice().setProperty("dalvik.vm.foo", "1");
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -213,7 +292,7 @@
// Run again with the same value.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -222,7 +301,7 @@
// Change the system property to another value.
getDevice().setProperty("dalvik.vm.foo", "2");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -230,7 +309,7 @@
// Run again with the same value.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -239,7 +318,7 @@
// Change the system property to empty.
getDevice().setProperty("dalvik.vm.foo", "");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -247,7 +326,7 @@
// Run again with the same value.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -258,7 +337,7 @@
public void verifyNoCompilationWhenCacheIsGood() throws Exception {
mTestUtils.removeCompilationLogToAvoidBackoff();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -267,8 +346,8 @@
@Test
public void verifyUnexpectedFilesAreCleanedUp() throws Exception {
String unexpected = OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME + "/unexpected";
- getDevice().pushString(/*contents=*/"", unexpected);
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ getDevice().pushString("" /* contents */, unexpected);
+ runOdrefresh();
assertFalse(getDevice().doesFileExist(unexpected));
}
@@ -292,9 +371,7 @@
mTestUtils.removeCompilationLogToAvoidBackoff();
simulateApexUpgrade();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(
- ODREFRESH_BIN + " --no-refresh --partial-compilation"
- + " --compilation-os-mode --compile");
+ runOdrefresh("--compilation-os-mode");
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -307,7 +384,7 @@
// Simulate the odrefresh invocation on the next boot.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// odrefresh should not re-compile anything.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -319,7 +396,7 @@
mTestUtils.removeCompilationLogToAvoidBackoff();
getDevice().executeShellV2Command(
"rm -rf " + OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME);
- getDevice().executeShellV2Command(ODREFRESH_MINIMAL_COMMAND);
+ runOdrefresh("--minimal");
mTestUtils.restartZygote();
@@ -330,21 +407,14 @@
// Running the command again should not overwrite the minimal boot image.
mTestUtils.removeCompilationLogToAvoidBackoff();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_MINIMAL_COMMAND);
-
- assertArtifactsNotModifiedAfter(minimalZygoteArtifacts, timeMs);
-
- // `odrefresh --check` should keep the minimal boot image.
- mTestUtils.removeCompilationLogToAvoidBackoff();
- timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_BIN + " --check");
+ runOdrefresh("--minimal");
assertArtifactsNotModifiedAfter(minimalZygoteArtifacts, timeMs);
// A normal odrefresh invocation should replace the minimal boot image with a full one.
mTestUtils.removeCompilationLogToAvoidBackoff();
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
for (String artifact : minimalZygoteArtifacts) {
assertFalse(
@@ -488,4 +558,14 @@
private Set<String> getSystemServerArtifacts() {
return getColonSeparatedSet(SYSTEM_SERVER_ARTIFACTS_KEY);
}
+
+ private void runOdrefresh() throws Exception {
+ runOdrefresh("" /* extraArgs */);
+ }
+
+ private void runOdrefresh(String extraArgs) throws Exception {
+ getDevice().executeShellV2Command(ODREFRESH_BIN + " --check");
+ getDevice().executeShellV2Command(
+ ODREFRESH_BIN + " --partial-compilation --no-refresh " + extraArgs + " --compile");
+ }
}
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
index caf94a7..5951600 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
@@ -314,18 +314,14 @@
// We can't use the "-c '%.3Y'" flag when to get the timestamp because the Toybox's `stat`
// implementation truncates the timestamp to seconds, which is not accurate enough, so we
// use "-c '%%y'" and parse the time ourselves.
- String dateTimeStr = mTestInfo.getDevice()
- .executeShellCommand(String.format("stat -c '%%y' '%s'", filename))
- .trim();
+ String dateTimeStr = assertCommandSucceeds(String.format("stat -c '%%y' '%s'", filename));
return parseFormattedDateTime(dateTimeStr);
}
public long getCurrentTimeMs() throws Exception {
// We can't use getDevice().getDeviceDate() because it truncates the timestamp to seconds,
// which is not accurate enough.
- String dateTimeStr = mTestInfo.getDevice()
- .executeShellCommand("date +'%Y-%m-%d %H:%M:%S.%N %z'")
- .trim();
+ String dateTimeStr = assertCommandSucceeds("date +'%Y-%m-%d %H:%M:%S.%N %z'");
return parseFormattedDateTime(dateTimeStr);
}
diff --git a/test/run-test b/test/run-test
index dccc9f6..f2be1d8 100755
--- a/test/run-test
+++ b/test/run-test
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env python3
#
# Copyright (C) 2007 The Android Open Source Project
#
@@ -14,1059 +14,1085 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-args="$@"
-while [ -h "${prog}" ]; do
- newProg=`/bin/ls -ld "${prog}"`
- newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
- if expr "x${newProg}" : 'x/' >/dev/null; then
- prog="${newProg}"
- else
- progdir=`dirname "${prog}"`
- prog="${progdir}/${newProg}"
- fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-test_dir="test-$$"
-if [ -z "$TMPDIR" ]; then
- tmp_dir="/tmp/$USER/${test_dir}"
-else
- tmp_dir="${TMPDIR}/${test_dir}"
-fi
-checker="${progdir}/../tools/checker/checker.py"
-export JAVA="java"
-export JAVAC="javac -g -Xlint:-options -source 1.8 -target 1.8"
-export RUN="${progdir}/etc/run-test-jar"
-export DEX_LOCATION=/data/run-test/${test_dir}
+import os, sys, glob, re, shutil, subprocess, shlex, resource, atexit
-# ANDROID_BUILD_TOP is not set in a build environment.
-if [ -z "$ANDROID_BUILD_TOP" ]; then
- export ANDROID_BUILD_TOP=$oldwd
-fi
+import default_run as default_run_module
-# OUT_DIR defaults to out, and may be relative to $ANDROID_BUILD_TOP.
-# Convert it to an absolute path, since we cd into the tmp_dir to run the tests.
-export OUT_DIR=${OUT_DIR:-out}
-if [[ "$OUT_DIR" != /* ]]; then
- export OUT_DIR=$ANDROID_BUILD_TOP/$OUT_DIR
-fi
+from default_run import get_target_arch
+from importlib.machinery import SourceFileLoader
+from inspect import currentframe, getframeinfo, FrameInfo
+from pathlib import Path
+from shutil import copyfile
+from testrunner import env
+from typing import Optional, Dict, List
+from zipfile import ZipFile
+
+COLOR = (os.environ.get("LUCI_CONTEXT") == None) # Disable colors on LUCI.
+COLOR_BLUE = '\033[94m' if COLOR else ''
+COLOR_GREEN = '\033[92m' if COLOR else ''
+COLOR_NORMAL = '\033[0m' if COLOR else ''
+COLOR_RED = '\033[91m' if COLOR else ''
+
+# Helper class which allows us to access the environment using syntax sugar.
+# E.g. `env.ANDROID_BUILD_TOP` instead of `os.environ["ANDROID_BUILD_TOP"]`.
+class Environment:
+
+ def __getattr__(self, name):
+ return os.environ.get(name)
+
+ def __setattr__(self, name, value):
+ os.environ[name] = str(value)
+
+
+# Context passed to individual tests to let them customize the behaviour.
+class RunTestContext:
+
+ def __init__(self, tmp_dir: Path, target: bool, chroot, dex_location, test_name) -> None:
+ self.env = Environment()
+ self.target = target
+ self.chroot = chroot
+ self.dex_location = dex_location
+ self.test_name = test_name
+
+ # Note: The expected path can be modified by the tests.
+ self.expected_stdout = tmp_dir / "expected-stdout.txt"
+ self.expected_stderr = tmp_dir / "expected-stderr.txt"
+
+ self.runner: List[str] = ["#!/bin/bash"]
+
+ def echo(self, text):
+ self.run(f"echo {text} > {test_stdout}")
+
+ def export(self, **env: str) -> None:
+ self.runner.append("")
+ for name, value in env.items():
+ self.runner.append(f"export {name}={value}")
+
+ # Add "runner" script command. It is not executed now.
+ # All "runner" commands are executed later via single bash call.
+ def run(self, cmd: str, check: bool=True, expected_exit_code: int=0, desc:str = None) -> None:
+ if cmd == "true":
+ return
+ cmd_esc = cmd.replace("'", r"'\''")
+ self.runner.append("")
+ self.runner.append(f"echo '{COLOR_BLUE}$$ {cmd_esc}{COLOR_NORMAL}'")
+ self.runner.append(cmd)
+
+ # Check the exit code.
+ if check:
+ caller = getframeinfo(currentframe().f_back) # type: ignore
+ source = "{}:{}".format(Path(caller.filename).name, caller.lineno)
+ msg = f"{self.test_name} FAILED: [{source}] "
+ msg += "{} returned exit code ${{exit_code}}.".format(desc or "Command")
+ if expected_exit_code:
+ msg += f" Expected {expected_exit_code}."
+ self.runner.append(
+ f"exit_code=$?; if [ $exit_code -ne {expected_exit_code} ]; then "
+ f"echo {COLOR_RED}{msg}{COLOR_NORMAL}; exit 100; "
+ f"fi; ")
+ else:
+ self.runner.append("true; # Ignore previous exit code")
+
+ # Execute the default runner (possibly with modified arguments).
+ def default_run(self, args, **kwargs):
+ default_run_module.default_run(self, args, **kwargs)
+
+
+# TODO: Replace with 'def main():' (which might change variables from globals to locals)
+if True:
+ progdir = os.path.dirname(__file__)
+ oldwd = os.getcwd()
+ os.chdir(progdir)
+ test_dir = "test-{}".format(os.getpid())
+ TMPDIR = os.environ.get("TMPDIR")
+ USER = os.environ.get("USER")
+ PYTHON3 = os.environ.get("PYTHON3")
+ if not TMPDIR:
+ tmp_dir = f"/tmp/{USER}/{test_dir}"
+ else:
+ tmp_dir = f"{TMPDIR}/{test_dir}"
+ checker = f"{progdir}/../tools/checker/checker.py"
+
+ ON_VM = os.environ.get("ART_TEST_ON_VM")
+ SSH_USER = os.environ.get("ART_TEST_SSH_USER")
+ SSH_HOST = os.environ.get("ART_TEST_SSH_HOST")
+ SSH_PORT = os.environ.get("ART_TEST_SSH_PORT")
+ SSH_CMD = os.environ.get("ART_SSH_CMD")
+ SCP_CMD = os.environ.get("ART_SCP_CMD")
+ CHROOT = os.environ.get("ART_TEST_CHROOT")
+ CHROOT_CMD = os.environ.get("ART_CHROOT_CMD")
+
+ def fail(message: str, caller:Optional[FrameInfo]=None):
+ caller = caller or getframeinfo(currentframe().f_back) # type: ignore
+ assert caller
+ source = "{}:{}".format(Path(caller.filename).name, caller.lineno)
+ print(f"{COLOR_RED}{TEST_NAME} FAILED: [{source}] {message}{COLOR_NORMAL}",
+ file=sys.stderr)
+ sys.exit(1)
+
+ def run(cmdline: str, check=True, fail_message=None) -> subprocess.CompletedProcess:
+ print(f"{COLOR_BLUE}$ {cmdline}{COLOR_NORMAL}", flush=True)
+ proc = subprocess.run([cmdline],
+ shell=True,
+ executable="/bin/bash",
+ stderr=subprocess.STDOUT)
+ if (check and proc.returncode != 0):
+ if fail_message:
+ # If we have custom fail message, exit without printing the full backtrace.
+ fail(fail_message, getframeinfo(currentframe().f_back)) # type: ignore
+ raise Exception(f"Command failed (exit code {proc.returncode})")
+ return proc
+
+ def export(env: str, value: str) -> None:
+ os.environ[env] = value
+ globals()[env] = value
+
+ def error(msg) -> None:
+ print(msg, file=sys.stderr, flush=True)
+
+ # ANDROID_BUILD_TOP is not set in a build environment.
+ ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP")
+ if not ANDROID_BUILD_TOP:
+ export("ANDROID_BUILD_TOP", oldwd)
+
+ export("JAVA", "java")
+ export("JAVAC", "javac -g -Xlint:-options -source 1.8 -target 1.8")
+ export("PYTHON3",
+ f"{ANDROID_BUILD_TOP}/prebuilts/build-tools/path/linux-x86/python3")
+ export("RUN", f"{PYTHON3} {progdir}/etc/run-test-jar")
+ export("DEX_LOCATION", f"/data/run-test/{test_dir}")
+
+ # OUT_DIR defaults to out, and may be relative to ANDROID_BUILD_TOP.
+ # Convert it to an absolute path, since we cd into the tmp_dir to run the tests.
+ OUT_DIR = os.environ.get("OUT_DIR", "")
+ export("OUT_DIR", OUT_DIR or "out")
+ if not OUT_DIR.startswith("/"):
+ export("OUT_DIR", f"{ANDROID_BUILD_TOP}/{OUT_DIR}")
# ANDROID_HOST_OUT is not set in a build environment.
-if [ -z "$ANDROID_HOST_OUT" ]; then
- export ANDROID_HOST_OUT=${OUT_DIR}/host/linux-x86
-fi
+ ANDROID_HOST_OUT = os.environ.get("ANDROID_HOST_OUT")
+ if not ANDROID_HOST_OUT:
+ export("ANDROID_HOST_OUT", f"{OUT_DIR}/host/linux-x86")
-host_lib_root=${ANDROID_HOST_OUT}
-chroot=
-info="info.txt"
-run="run"
-expected_stdout="expected-stdout.txt"
-expected_stderr="expected-stderr.txt"
-check_cmd="check"
-test_stdout="test-stdout.txt"
-test_stderr="test-stderr.txt"
-cfg_output="graph.cfg"
-strace_output="strace-output.txt"
-lib="libartd.so"
-testlib="arttestd"
-run_args=(--quiet)
+ host_lib_root = ANDROID_HOST_OUT
+ chroot = ""
+ info = "info.txt"
+ run_cmd = "run"
+ test_stdout = "test-stdout.txt"
+ test_stderr = "test-stderr.txt"
+ cfg_output = "graph.cfg"
+ strace_output = "strace-output.txt"
+ lib = "libartd.so"
+ testlib = "arttestd"
+ run_args = []
+ run_checker = "no"
-quiet="no"
-debuggable="no"
-prebuild_mode="yes"
-target_mode="yes"
-dev_mode="no"
-create_runner="no"
-update_mode="no"
-debug_mode="no"
-relocate="no"
-runtime="art"
-usage="no"
-suffix64=""
-trace="false"
-trace_stream="false"
-basic_verify="false"
-gc_verify="false"
-gc_stress="false"
-jvmti_trace_stress="false"
-jvmti_field_stress="false"
-jvmti_step_stress="false"
-jvmti_redefine_stress="false"
-strace="false"
-always_clean="no"
-never_clean="no"
-have_image="yes"
-android_root="/system"
-bisection_search="no"
-timeout=""
-suspend_timeout="500000"
-run_optimizing="false"
-dump_cfg="false"
-dump_cfg_path=""
-# To cause tests to fail fast, limit the file sizes created by dx, dex2oat and
-# ART output to approximately 128MB. This should be more than sufficient
-# for any test while still catching cases of runaway output.
-# Set a hard limit to encourage ART developers to increase the ulimit here if
-# needed to support a test case rather than resetting the limit in the run
-# script for the particular test in question. Adjust this if needed for
-# particular configurations.
-file_ulimit=128000
+ quiet = "no"
+ debuggable = "no"
+ prebuild_mode = "yes"
+ target_mode = "yes"
+ dev_mode = "no"
+ create_runner = "no"
+ update_mode = "no"
+ debug_mode = "no"
+ relocate = "no"
+ runtime = "art"
+ usage = "no"
+ suffix64 = ""
+ trace = "false"
+ trace_stream = "false"
+ basic_verify = "false"
+ gc_verify = "false"
+ gc_stress = "false"
+ jvmti_trace_stress = "false"
+ jvmti_field_stress = "false"
+ jvmti_step_stress = "false"
+ jvmti_redefine_stress = "false"
+ strace = "false"
+ always_clean = "no"
+ never_clean = "no"
+ have_image = "yes"
+ android_root = "/system"
+ bisection_search = "no"
+ timeout = ""
+ suspend_timeout = "500000"
+ run_optimizing = "false"
+ dump_cfg = "false"
+ dump_cfg_path = ""
+ # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and
+ # ART output to approximately 128MB. This should be more than sufficient
+ # for any test while still catching cases of runaway output.
+ # Set a hard limit to encourage ART developers to increase the ulimit here if
+ # needed to support a test case rather than resetting the limit in the run
+ # script for the particular test in question. Adjust this if needed for
+ # particular configurations.
+ file_ulimit = 128000
+ args = list(sys.argv)
+ arg = args[0]
-while true; do
- if [ "x$1" = "x--host" ]; then
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- run_args+=(--host)
- shift
- elif [ "x$1" = "x--quiet" ]; then
- quiet="yes"
- shift
- elif [ "x$1" = "x--use-java-home" ]; then
- if [ -n "${JAVA_HOME}" ]; then
- export JAVA="${JAVA_HOME}/bin/java"
- export JAVAC="${JAVA_HOME}/bin/javac -g"
- else
- echo "Passed --use-java-home without JAVA_HOME variable set!"
- usage="yes"
- fi
- shift
- elif [ "x$1" = "x--jvm" ]; then
- target_mode="no"
- DEX_LOCATION="$tmp_dir"
- runtime="jvm"
- prebuild_mode="no"
- run_args+=(--jvm)
- shift
- elif [ "x$1" = "x-O" ]; then
- lib="libart.so"
- testlib="arttest"
- run_args+=(-O)
- shift
- elif [ "x$1" = "x--dalvik" ]; then
- lib="libdvm.so"
- runtime="dalvik"
- shift
- elif [ "x$1" = "x--no-image" ]; then
- have_image="no"
- shift
- elif [ "x$1" = "x--relocate" ]; then
- relocate="yes"
- shift
- elif [ "x$1" = "x--no-relocate" ]; then
- relocate="no"
- shift
- elif [ "x$1" = "x--prebuild" ]; then
- run_args+=(--prebuild)
- prebuild_mode="yes"
- shift;
- elif [ "x$1" = "x--compact-dex-level" ]; then
- option="$1"
- shift
- run_args+=("$option" "$1")
- shift;
- elif [ "x$1" = "x--strip-dex" ]; then
- run_args+=(--strip-dex)
- shift;
- elif [ "x$1" = "x--debuggable" ]; then
- run_args+=(-Xcompiler-option --debuggable)
- debuggable="yes"
- shift;
- elif [ "x$1" = "x--no-prebuild" ]; then
- run_args+=(--no-prebuild)
- prebuild_mode="no"
- shift;
- elif [ "x$1" = "x--gcverify" ]; then
- basic_verify="true"
- gc_verify="true"
- shift
- elif [ "x$1" = "x--gcstress" ]; then
- basic_verify="true"
- gc_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-step-stress" ]; then
- jvmti_step_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-redefine-stress" ]; then
- jvmti_redefine_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-field-stress" ]; then
- jvmti_field_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-trace-stress" ]; then
- jvmti_trace_stress="true"
- shift
- elif [ "x$1" = "x--suspend-timeout" ]; then
- shift
- suspend_timeout="$1"
- shift
- elif [ "x$1" = "x--image" ]; then
- shift
- image="$1"
- run_args+=(--image "$image")
- shift
- elif [ "x$1" = "x-Xcompiler-option" ]; then
- shift
- option="$1"
- run_args+=(-Xcompiler-option "$option")
- shift
- elif [ "x$1" = "x--runtime-option" ]; then
- shift
- option="$1"
- run_args+=(--runtime-option "$option")
- shift
- elif [ "x$1" = "x--gdb-arg" ]; then
- shift
- gdb_arg="$1"
- run_args+=(--gdb-arg "$gdb_arg")
- shift
- elif [ "x$1" = "x--gdb-dex2oat-args" ]; then
- shift
- gdb_dex2oat_args="$1"
- run_args+=(--gdb-dex2oat-args "$gdb_dex2oat_args")
- shift
- elif [ "x$1" = "x--debug" ]; then
- run_args+=(--debug)
- shift
- elif [ "x$1" = "x--debug-wrap-agent" ]; then
- run_args+=(--debug-wrap-agent)
- shift
- elif [ "x$1" = "x--with-agent" ]; then
- shift
- option="$1"
- run_args+=(--with-agent "$1")
- shift
- elif [ "x$1" = "x--debug-agent" ]; then
- shift
- option="$1"
- run_args+=(--debug-agent "$1")
- shift
- elif [ "x$1" = "x--dump-cfg" ]; then
- shift
- dump_cfg="true"
- dump_cfg_path="$1"
- shift
- elif [ "x$1" = "x--gdb" ]; then
- run_args+=(--gdb)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--gdb-dex2oat" ]; then
- run_args+=(--gdb-dex2oat)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--gdbserver-bin" ]; then
- shift
- run_args+=(--gdbserver-bin "$1")
- shift
- elif [ "x$1" = "x--gdbserver-port" ]; then
- shift
- run_args+=(--gdbserver-port "$1")
- shift
- elif [ "x$1" = "x--gdbserver" ]; then
- run_args+=(--gdbserver)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--strace" ]; then
- strace="yes"
- run_args+=(--invoke-with strace --invoke-with -o --invoke-with "$tmp_dir/$strace_output")
- timeout="${timeout:-1800}"
- shift
- elif [ "x$1" = "x--zygote" ]; then
- run_args+=(--zygote)
- shift
- elif [ "x$1" = "x--interpreter" ]; then
- run_args+=(--interpreter)
- shift
- elif [ "x$1" = "x--jit" ]; then
- run_args+=(--jit)
- shift
- elif [ "x$1" = "x--baseline" ]; then
- run_args+=(--baseline)
- shift
- elif [ "x$1" = "x--optimizing" ]; then
- run_optimizing="true"
- shift
- elif [ "x$1" = "x--no-verify" ]; then
- run_args+=(--no-verify)
- shift
- elif [ "x$1" = "x--verify-soft-fail" ]; then
- run_args+=(--verify-soft-fail)
- shift
- elif [ "x$1" = "x--no-optimize" ]; then
- run_args+=(--no-optimize)
- shift
- elif [ "x$1" = "x--no-precise" ]; then
- run_args+=(--no-precise)
- shift
- elif [ "x$1" = "x--external-log-tags" ]; then
- run_args+=(--external-log-tags)
- shift
- elif [ "x$1" = "x--invoke-with" ]; then
- shift
- what="$1"
- if [ "x$what" = "x" ]; then
- echo "$0 missing argument to --invoke-with" 1>&2
- usage="yes"
- break
- fi
- run_args+=(--invoke-with "${what}")
- shift
- elif [ "x$1" = "x--create-runner" ]; then
- run_args+=(--create-runner --dry-run)
- dev_mode="yes"
- never_clean="yes"
- create_runner="yes"
- shift
- elif [ "x$1" = "x--dev" ]; then
- run_args+=(--dev)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--temp-path" ]; then
- shift
- tmp_dir=$1
- if [ "x$tmp_dir" = "x" ]; then
- echo "$0 missing argument to --temp-path" 1>&2
- usage="yes"
- break
- fi
- shift
- elif [ "x$1" = "x--chroot" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --chroot" 1>&2
- usage="yes"
- break
- fi
- chroot="$1"
- run_args+=(--chroot "$1")
- shift
- elif [ "x$1" = "x--simpleperf" ]; then
- run_args+=(--simpleperf)
- shift
- elif [ "x$1" = "x--android-root" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --android-root" 1>&2
- usage="yes"
- break
- fi
- android_root="$1"
- run_args+=(--android-root "$1")
- shift
- elif [ "x$1" = "x--android-art-root" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --android-art-root" 1>&2
- usage="yes"
- break
- fi
- run_args+=(--android-art-root "$1")
- shift
- elif [ "x$1" = "x--android-tzdata-root" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --android-tzdata-root" 1>&2
- usage="yes"
- break
- fi
- run_args+=(--android-tzdata-root "$1")
- shift
- elif [ "x$1" = "x--update" ]; then
- update_mode="yes"
- shift
- elif [ "x$1" = "x--help" ]; then
- usage="yes"
- shift
- elif [ "x$1" = "x--64" ]; then
- run_args+=(--64)
- suffix64="64"
- shift
- elif [ "x$1" = "x--bionic" ]; then
- # soong linux_bionic builds are 64bit only.
- run_args+=(--bionic --host --64)
- suffix64="64"
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- host_lib_root=$OUT_DIR/soong/host/linux_bionic-x86
- shift
- elif [ "x$1" = "x--runtime-extracted-zipapex" ]; then
- shift
- # TODO Should we allow the java.library.path to search the zipapex too?
- # Not needed at the moment and adding it will be complicated so for now
- # we'll ignore this.
- run_args+=(--host --runtime-extracted-zipapex "$1")
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- shift
- elif [ "x$1" = "x--runtime-zipapex" ]; then
- shift
- # TODO Should we allow the java.library.path to search the zipapex too?
- # Not needed at the moment and adding it will be complicated so for now
- # we'll ignore this.
- run_args+=(--host --runtime-zipapex "$1")
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- # apex_payload.zip is quite large we need a high enough ulimit to
- # extract it. 512mb should be good enough.
- file_ulimit=512000
- shift
- elif [ "x$1" = "x--timeout" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --timeout" 1>&2
- usage="yes"
- break
- fi
- timeout="$1"
- shift
- elif [ "x$1" = "x--trace" ]; then
- trace="true"
- shift
- elif [ "x$1" = "x--stream" ]; then
- trace_stream="true"
- shift
- elif [ "x$1" = "x--always-clean" ]; then
- always_clean="yes"
- shift
- elif [ "x$1" = "x--never-clean" ]; then
- never_clean="yes"
- shift
- elif [ "x$1" = "x--dex2oat-swap" ]; then
- run_args+=(--dex2oat-swap)
- shift
- elif [ "x$1" = "x--instruction-set-features" ]; then
- shift
- run_args+=(--instruction-set-features "$1")
- shift
- elif [ "x$1" = "x--bisection-search" ]; then
- bisection_search="yes"
- shift
- elif [ "x$1" = "x--vdex" ]; then
- run_args+=(--vdex)
- shift
- elif [ "x$1" = "x--dm" ]; then
- run_args+=(--dm)
- shift
- elif [ "x$1" = "x--vdex-filter" ]; then
- shift
- filter=$1
- run_args+=(--vdex-filter "$filter")
- shift
- elif [ "x$1" = "x--random-profile" ]; then
- run_args+=(--random-profile)
- shift
- elif [ "x$1" = "x--dex2oat-jobs" ]; then
- shift
- run_args+=(-Xcompiler-option "-j$1")
- shift
- elif expr "x$1" : "x--" >/dev/null 2>&1; then
- echo "unknown $0 option: $1" 1>&2
- usage="yes"
+ def shift():
+ global arg
+ args.pop(0)
+ arg = args[0] if args else ""
+
+ shift()
+
+ while True:
+ if arg == "--host":
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ run_args += ["--host"]
+ os.environ["RUN_MODE"] = "host"
+ shift()
+ elif arg == "--quiet":
+ quiet = "yes"
+ shift()
+ elif arg == "--use-java-home":
+ JAVA_HOME = os.environ.get("JAVA_HOME")
+ if JAVA_HOME:
+ export("JAVA", f"{JAVA_HOME}/bin/java")
+ export("JAVAC", f"{JAVA_HOME}/bin/javac -g")
+ else:
+ error("Passed --use-java-home without JAVA_HOME variable set!")
+ usage = "yes"
+ shift()
+ elif arg == "--jvm":
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ runtime = "jvm"
+ prebuild_mode = "no"
+ run_args += ["--jvm"]
+ shift()
+ elif arg == "-O":
+ lib = "libart.so"
+ testlib = "arttest"
+ run_args += ["-O"]
+ shift()
+ elif arg == "--dalvik":
+ lib = "libdvm.so"
+ runtime = "dalvik"
+ shift()
+ elif arg == "--no-image":
+ have_image = "no"
+ shift()
+ elif arg == "--relocate":
+ relocate = "yes"
+ shift()
+ elif arg == "--no-relocate":
+ relocate = "no"
+ shift()
+ elif arg == "--prebuild":
+ run_args += ["--prebuild"]
+ prebuild_mode = "yes"
+ shift()
+ elif arg == "--compact-dex-level":
+ option = arg
+ shift()
+ run_args += [f'"{option}" "{arg}"']
+ shift()
+ elif arg == "--strip-dex":
+ run_args += ["--strip-dex"]
+ shift()
+ elif arg == "--debuggable":
+ run_args += ["-Xcompiler-option --debuggable"]
+ debuggable = "yes"
+ shift()
+ elif arg == "--no-prebuild":
+ run_args += ["--no-prebuild"]
+ prebuild_mode = "no"
+ shift()
+ elif arg == "--gcverify":
+ basic_verify = "true"
+ gc_verify = "true"
+ shift()
+ elif arg == "--gcstress":
+ basic_verify = "true"
+ gc_stress = "true"
+ shift()
+ elif arg == "--jvmti-step-stress":
+ jvmti_step_stress = "true"
+ os.environ["JVMTI_STEP_STRESS"] = "true"
+ shift()
+ elif arg == "--jvmti-redefine-stress":
+ jvmti_redefine_stress = "true"
+ os.environ["JVMTI_REDEFINE_STRESS"] = "true"
+ shift()
+ elif arg == "--jvmti-field-stress":
+ jvmti_field_stress = "true"
+ os.environ["JVMTI_FIELD_STRESS"] = "true"
+ shift()
+ elif arg == "--jvmti-trace-stress":
+ jvmti_trace_stress = "true"
+ os.environ["JVMTI_TRACE_STRESS"] = "true"
+ shift()
+ elif arg == "--suspend-timeout":
+ shift()
+ suspend_timeout = arg
+ shift()
+ elif arg == "--image":
+ shift()
+ image = arg
+ run_args += [f'--image "{image}"']
+ shift()
+ elif arg == "-Xcompiler-option":
+ shift()
+ option = arg
+ run_args += [f'-Xcompiler-option "{option}"']
+ shift()
+ elif arg == "--runtime-option":
+ shift()
+ option = arg
+ run_args += [f'--runtime-option "{option}"']
+ shift()
+ elif arg == "--gdb-arg":
+ shift()
+ gdb_arg = arg
+ run_args += [f'--gdb-arg "{gdb_arg}"']
+ shift()
+ elif arg == "--gdb-dex2oat-args":
+ shift()
+ gdb_dex2oat_args = arg
+ run_args += ['--gdb-dex2oat-args "{gdb_dex2oat_args}"']
+ shift()
+ elif arg == "--debug":
+ run_args += ["--debug"]
+ shift()
+ elif arg == "--debug-wrap-agent":
+ run_args += ["--debug-wrap-agent"]
+ shift()
+ elif arg == "--with-agent":
+ shift()
+ option = arg
+ run_args += [f'--with-agent "{arg}"']
+ shift()
+ elif arg == "--debug-agent":
+ shift()
+ option = arg
+ run_args += [f'--debug-agent "{arg}"']
+ shift()
+ elif arg == "--dump-cfg":
+ shift()
+ dump_cfg = "true"
+ dump_cfg_path = arg
+ shift()
+ elif arg == "--gdb":
+ run_args += ["--gdb"]
+ dev_mode = "yes"
+ shift()
+ elif arg == "--gdb-dex2oat":
+ run_args += ["--gdb-dex2oat"]
+ dev_mode = "yes"
+ shift()
+ elif arg == "--gdbserver-bin":
+ shift()
+ run_args += [f'--gdbserver-bin "{arg}"']
+ shift()
+ elif arg == "--gdbserver-port":
+ shift()
+ run_args += [f'--gdbserver-port "{arg}"']
+ shift()
+ elif arg == "--gdbserver":
+ run_args += ["--gdbserver"]
+ dev_mode = "yes"
+ shift()
+ elif arg == "--strace":
+ strace = "yes"
+ run_args += [
+ f'--invoke-with=strace --invoke-with=-o --invoke-with="{tmp_dir}/{strace_output}"'
+ ]
+ timeout = timeout or "1800"
+ shift()
+ elif arg == "--zygote":
+ run_args += ["--zygote"]
+ shift()
+ elif arg == "--interpreter":
+ run_args += ["--interpreter"]
+ shift()
+ elif arg == "--jit":
+ run_args += ["--jit"]
+ shift()
+ elif arg == "--baseline":
+ run_args += ["--baseline"]
+ shift()
+ elif arg == "--optimizing":
+ run_optimizing = "true"
+ shift()
+ elif arg == "--no-verify":
+ run_args += ["--no-verify"]
+ shift()
+ elif arg == "--verify-soft-fail":
+ run_args += ["--verify-soft-fail"]
+ os.environ["VERIFY_SOFT_FAIL"] = "true"
+ shift()
+ elif arg == "--no-optimize":
+ run_args += ["--no-optimize"]
+ shift()
+ elif arg == "--no-precise":
+ run_args += ["--no-precise"]
+ shift()
+ elif arg.startswith("--android-log-tags"):
+ run_args += [arg]
+ shift()
+ elif arg == "--external-log-tags":
+ run_args += ["--external-log-tags"]
+ shift()
+ elif arg == "--invoke-with":
+ shift()
+ what = arg
+ if not arg:
+ error("missing argument to --invoke-with")
+ usage = "yes"
break
- else
+ run_args += [f'--invoke-with "{what}"']
+ shift()
+ elif arg == "--create-runner":
+ run_args += ["--create-runner --dry-run"]
+ dev_mode = "yes"
+ never_clean = "yes"
+ create_runner = "yes"
+ shift()
+ elif arg == "--dev":
+ dev_mode = "yes"
+ shift()
+ elif arg == "--temp-path":
+ shift()
+ if not arg:
+ error("missing argument to --temp-path")
+ usage = "yes"
break
- fi
-done
+ shift()
+ elif arg == "--chroot":
+ shift()
+ if not arg:
+ error("missing argument to --chroot")
+ usage = "yes"
+ break
+ chroot = arg
+ run_args += [f'--chroot "{arg}"']
+ shift()
+ elif arg == "--simpleperf":
+ run_args += ["--simpleperf"]
+ shift()
+ elif arg == "--android-root":
+ shift()
+ if not arg:
+ error("missing argument to --android-root")
+ usage = "yes"
+ break
+ android_root = arg
+ run_args += [f'--android-root "{arg}"']
+ shift()
+ elif arg == "--android-art-root":
+ shift()
+ if not arg:
+ error("missing argument to --android-art-root")
+ usage = "yes"
+ break
+ run_args += [f'--android-art-root "{arg}"']
+ shift()
+ elif arg == "--android-tzdata-root":
+ shift()
+ if not arg:
+ error("missing argument to --android-tzdata-root")
+ usage = "yes"
+ break
+ run_args += [f'--android-tzdata-root "{arg}"']
+ shift()
+ elif arg == "--update":
+ update_mode = "yes"
+ shift()
+ elif arg == "--help":
+ usage = "yes"
+ shift()
+ elif arg == "--64":
+ run_args += ["--64"]
+ suffix64 = "64"
+ shift()
+ elif arg == "--bionic":
+ # soong linux_bionic builds are 64bit only.
+ run_args += ["--bionic --host --64"]
+ suffix64 = "64"
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ host_lib_root = f"{OUT_DIR}/soong/host/linux_bionic-x86"
+ shift()
+ elif arg == "--runtime-extracted-zipapex":
+ shift()
+ # TODO Should we allow the java.library.path to search the zipapex too?
+ # Not needed at the moment and adding it will be complicated so for now
+ # we'll ignore this.
+ run_args += [f'--host --runtime-extracted-zipapex "{arg}"']
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ shift()
+ elif arg == "--runtime-zipapex":
+ shift()
+ # TODO Should we allow the java.library.path to search the zipapex too?
+ # Not needed at the moment and adding it will be complicated so for now
+ # we'll ignore this.
+ run_args += [f'--host --runtime-zipapex "{arg}"']
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ # apex_payload.zip is quite large we need a high enough ulimit to
+ # extract it. 512mb should be good enough.
+ file_ulimit = 512000
+ shift()
+ elif arg == "--timeout":
+ shift()
+ if not arg:
+ error("missing argument to --timeout")
+ usage = "yes"
+ break
+ timeout = arg
+ shift()
+ elif arg == "--trace":
+ trace = "true"
+ shift()
+ elif arg == "--stream":
+ trace_stream = "true"
+ shift()
+ elif arg == "--always-clean":
+ always_clean = "yes"
+ shift()
+ elif arg == "--never-clean":
+ never_clean = "yes"
+ shift()
+ elif arg == "--dex2oat-swap":
+ run_args += ["--dex2oat-swap"]
+ shift()
+ elif arg == "--instruction-set-features":
+ shift()
+ run_args += [f'--instruction-set-features "{arg}"']
+ shift()
+ elif arg == "--bisection-search":
+ bisection_search = "yes"
+ shift()
+ elif arg == "--vdex":
+ run_args += ["--vdex"]
+ shift()
+ elif arg == "--dm":
+ run_args += ["--dm"]
+ shift()
+ elif arg == "--vdex-filter":
+ shift()
+ filter = arg
+ run_args += ['--vdex-filter "{filter}"']
+ shift()
+ elif arg == "--random-profile":
+ run_args += ["--random-profile"]
+ shift()
+ elif arg == "--dex2oat-jobs":
+ shift()
+ run_args += [f'-Xcompiler-option "-j{arg}"']
+ shift()
+ elif arg.startswith("--"):
+ error(f"unknown option: {arg}")
+ usage = "yes"
+ break
+ else:
+ break
-if [ "$usage" = "no" -a "x$1" = "x" ]; then
- echo "missing test to run" 1>&2
- usage="yes"
-fi
+ export("DEX_LOCATION", DEX_LOCATION)
+
+ if usage == "no" and not arg:
+ error("missing test to run")
+ usage = "yes"
# The DEX_LOCATION with the chroot prefix, if any.
-chroot_dex_location="$chroot$DEX_LOCATION"
+ chroot_dex_location = f"{chroot}{DEX_LOCATION}"
-# Allocate file descriptor real_stderr and redirect it to the shell's error
-# output (fd 2).
-if [ ${BASH_VERSINFO[1]} -ge 4 ] && [ ${BASH_VERSINFO[2]} -ge 1 ]; then
- exec {real_stderr}>&2
-else
- # In bash before version 4.1 we need to do a manual search for free file
- # descriptors.
- FD=3
- while [ -e /dev/fd/$FD ]; do FD=$((FD + 1)); done
- real_stderr=$FD
- eval "exec ${real_stderr}>&2"
-fi
-if [ "$quiet" = "yes" ]; then
- # Force the default standard output and error to go to /dev/null so we will
- # not print them.
- exec 1>/dev/null
- exec 2>/dev/null
-fi
+ # tmp_dir may be relative, resolve.
+ os.chdir(oldwd)
+ tmp_dir = os.path.realpath(tmp_dir)
+ os.chdir(progdir)
+ if not tmp_dir:
+ error(f"Failed to resolve {tmp_dir}")
+ sys.exit(1)
+ os.makedirs(tmp_dir, exist_ok=True)
-function err_echo() {
- echo "$@" 1>&${real_stderr}
-}
+ # Add thread suspend timeout flag
+ if runtime != "jvm":
+ run_args += [
+ f'--runtime-option "-XX:ThreadSuspendTimeout={suspend_timeout}"'
+ ]
-# tmp_dir may be relative, resolve.
-#
-# Cannot use realpath, as it does not exist on Mac.
-# Cannot use a simple "cd", as the path might not be created yet.
-# Cannot use readlink -m, as it does not exist on Mac.
-# Fallback to nuclear option:
-noncanonical_tmp_dir=$tmp_dir
-tmp_dir="`cd $oldwd ; python3 -c "import os; import sys; sys.stdout.write(os.path.realpath('$tmp_dir'))"`"
-if [ -z $tmp_dir ] ; then
- err_echo "Failed to resolve $tmp_dir"
- exit 1
-fi
-mkdir -p $tmp_dir
-
-# Add thread suspend timeout flag
-if [ ! "$runtime" = "jvm" ]; then
- run_args+=(--runtime-option "-XX:ThreadSuspendTimeout=$suspend_timeout")
-fi
-
-if [ "$basic_verify" = "true" ]; then
- # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
- run_args+=(--runtime-option -Xgc:preverify --runtime-option -Xgc:postverify --runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0)
-fi
-if [ "$gc_verify" = "true" ]; then
- run_args+=(--runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc)
-fi
-if [ "$gc_stress" = "true" ]; then
- run_args+=(--gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m --runtime-option -Xmx16m)
-fi
-if [ "$jvmti_redefine_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-redefine-stress)
-fi
-if [ "$jvmti_step_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-step-stress)
-fi
-if [ "$jvmti_field_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-field-stress)
-fi
-if [ "$jvmti_trace_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-trace-stress)
-fi
-if [ "$trace" = "true" ]; then
- run_args+=(--runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file-size:2000000)
- if [ "$trace_stream" = "true" ]; then
- # Streaming mode uses the file size as the buffer size. So output gets really large. Drop
- # the ability to analyze the file and just write to /dev/null.
- run_args+=(--runtime-option -Xmethod-trace-file:/dev/null)
- # Enable streaming mode.
- run_args+=(--runtime-option -Xmethod-trace-stream)
- else
- run_args+=(--runtime-option "-Xmethod-trace-file:${DEX_LOCATION}/trace.bin")
- fi
-elif [ "$trace_stream" = "true" ]; then
- err_echo "Cannot use --stream without --trace."
- exit 1
-fi
-if [ -n "$timeout" ]; then
- run_args+=(--timeout "$timeout")
-fi
+ if basic_verify == "true":
+ # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
+ run_args += [
+ "--runtime-option -Xgc:preverify --runtime-option -Xgc:postverify "
+ "--runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0"
+ ]
+ if gc_verify == "true":
+ run_args += [
+ "--runtime-option -Xgc:preverify_rosalloc --runtime-option "
+ "-Xgc:postverify_rosalloc"
+ ]
+ if gc_stress == "true":
+ run_args += [
+ "--gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m "
+ "--runtime-option -Xmx16m"
+ ]
+ if jvmti_redefine_stress == "true":
+ run_args += ["--no-app-image --jvmti-redefine-stress"]
+ if jvmti_step_stress == "true":
+ run_args += ["--no-app-image --jvmti-step-stress"]
+ if jvmti_field_stress == "true":
+ run_args += ["--no-app-image --jvmti-field-stress"]
+ if jvmti_trace_stress == "true":
+ run_args += ["--no-app-image --jvmti-trace-stress"]
+ if trace == "true":
+ run_args += [
+ "--runtime-option -Xmethod-trace --runtime-option "
+ "-Xmethod-trace-file-size:2000000"
+ ]
+ if trace_stream == "true":
+ # Streaming mode uses the file size as the buffer size. So output gets really large. Drop
+ # the ability to analyze the file and just write to /dev/null.
+ run_args += ["--runtime-option -Xmethod-trace-file:/dev/null"]
+ # Enable streaming mode.
+ run_args += ["--runtime-option -Xmethod-trace-stream"]
+ else:
+ run_args += [
+ f'--runtime-option "-Xmethod-trace-file:{DEX_LOCATION}/trace.bin"'
+ ]
+ elif trace_stream == "true":
+ error("Cannot use --stream without --trace.")
+ sys.exit(1)
+ if timeout:
+ run_args += [f'--timeout "{timeout}"']
# Most interesting target architecture variables are Makefile variables, not environment variables.
-# Try to map the suffix64 flag and what we find in ${ANDROID_PRODUCT_OUT}/data/art-test to an architecture name.
-function guess_target_arch_name() {
- # Check whether this is a device with native bridge. Currently this is hardcoded
- # to x86 + arm.
- local guess_path=$chroot/system/framework/art_boot_images
- local x86_arm=`adb shell ls ${guess_path} | sort | grep -E '^(arm|x86)$'`
- # Collapse line-breaks into spaces
- x86_arm=$(echo $x86_arm)
- if [ "x$x86_arm" = "xarm x86" ] ; then
- err_echo "Native-bridge configuration detected."
- # We only support the main arch for tests.
- if [ "x${suffix64}" = "x64" ]; then
- target_arch_name=""
- else
- target_arch_name=x86
- fi
- else
- local grep32bit=`adb shell ls ${guess_path} | grep -E '^(arm|x86)$'`
- local grep64bit=`adb shell ls ${guess_path} | grep -E '^(arm64|x86_64)$'`
- if [ "x${suffix64}" = "x64" ]; then
- target_arch_name=${grep64bit}
- else
- target_arch_name=${grep32bit}
- fi
- fi
-}
+# Try to map the suffix64 flag and what we find in {ANDROID_PRODUCT_OUT}/data/art-test to an architecture name.
-function guess_host_arch_name() {
- if [ "x${suffix64}" = "x64" ]; then
- host_arch_name="x86_64"
- else
- host_arch_name="x86"
- fi
-}
+ def guess_target_arch_name():
+ return get_target_arch(suffix64 == "64")
-if [ "$target_mode" = "no" ]; then
- if [ "$runtime" = "jvm" ]; then
- if [ "$prebuild_mode" = "yes" ]; then
- err_echo "--prebuild with --jvm is unsupported"
- exit 1
- fi
- else
- # ART/Dalvik host mode.
- if [ -n "$chroot" ]; then
- err_echo "--chroot with --host is unsupported"
- exit 1
- fi
- fi
-fi
+ def guess_host_arch_name():
+ if suffix64 == "64":
+ return "x86_64"
+ else:
+ return "x86"
-if [ ! "$runtime" = "jvm" ]; then
- run_args+=(--lib "$lib")
-fi
+ if target_mode == "no":
+ if runtime == "jvm":
+ if prebuild_mode == "yes":
+ error("--prebuild with --jvm is unsupported")
+ sys.exit(1)
+ else:
+ # ART/Dalvik host mode.
+ if chroot:
+ error("--chroot with --host is unsupported")
+ sys.exit(1)
-if [ "$runtime" = "dalvik" ]; then
- if [ "$target_mode" = "no" ]; then
- framework="${ANDROID_PRODUCT_OUT}/system/framework"
- bpath="${framework}/core-icu4j.jar:${framework}/core-libart.jar:${framework}/core-oj.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/bouncycastle.jar:${framework}/ext.jar"
- run_args+=(--boot --runtime-option "-Xbootclasspath:${bpath}")
- else
- true # defaults to using target BOOTCLASSPATH
- fi
-elif [ "$runtime" = "art" ]; then
- if [ "$target_mode" = "no" ]; then
- guess_host_arch_name
- run_args+=(--boot "${ANDROID_HOST_OUT}/apex/art_boot_images/javalib/boot.art")
- run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
- else
- guess_target_arch_name
- # Note that libarttest(d).so and other test libraries that depend on ART
- # internal libraries must not be in this path for JNI libraries - they
- # need to be loaded through LD_LIBRARY_PATH and
- # NATIVELOADER_DEFAULT_NAMESPACE_LIBS instead.
- run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
- run_args+=(--boot "/system/framework/art_boot_images/boot.art")
- fi
- if [ "$relocate" = "yes" ]; then
- run_args+=(--relocate)
- else
- run_args+=(--no-relocate)
- fi
-elif [ "$runtime" = "jvm" ]; then
+ if runtime != "jvm":
+ run_args += [f'--lib "{lib}"']
+
+ ANDROID_PRODUCT_OUT = os.environ.get("ANDROID_PRODUCT_OUT")
+ if runtime == "dalvik":
+ if target_mode == "no":
+ framework = f"{ANDROID_PRODUCT_OUT}/system/framework"
+ bpath = f"{framework}/core-icu4j.jar:{framework}/core-libart.jar:{framework}/core-oj.jar:{framework}/conscrypt.jar:{framework}/okhttp.jar:{framework}/bouncycastle.jar:{framework}/ext.jar"
+ run_args += [f'--boot --runtime-option "-Xbootclasspath:{bpath}"']
+ else:
+ pass # defaults to using target BOOTCLASSPATH
+ elif runtime == "art":
+ if target_mode == "no":
+ host_arch_name = guess_host_arch_name()
+ run_args += [
+ f'--boot "{ANDROID_HOST_OUT}/apex/art_boot_images/javalib/boot.art"'
+ ]
+ run_args += [
+ f'--runtime-option "-Djava.library.path={host_lib_root}/lib{suffix64}:{host_lib_root}/nativetest{suffix64}"'
+ ]
+ else:
+ target_arch_name = guess_target_arch_name()
+ # Note that libarttest(d).so and other test libraries that depend on ART
+ # internal libraries must not be in this path for JNI libraries - they
+ # need to be loaded through LD_LIBRARY_PATH and
+ # NATIVELOADER_DEFAULT_NAMESPACE_LIBS instead.
+ run_args += [
+ f'--runtime-option "-Djava.library.path=/data/nativetest{suffix64}/art/{target_arch_name}"'
+ ]
+ run_args += ['--boot "/system/framework/art_boot_images/boot.art"']
+ if relocate == "yes":
+ run_args += ["--relocate"]
+ else:
+ run_args += ["--no-relocate"]
+ elif runtime == "jvm":
# TODO: Detect whether the host is 32-bit or 64-bit.
- run_args+=(--runtime-option "-Djava.library.path=${ANDROID_HOST_OUT}/lib64:${ANDROID_HOST_OUT}/nativetest64")
-fi
+ run_args += [
+ f'--runtime-option "-Djava.library.path={ANDROID_HOST_OUT}/lib64:{ANDROID_HOST_OUT}/nativetest64"'
+ ]
-if [ "$have_image" = "no" ]; then
- if [ "$runtime" != "art" ]; then
- err_echo "--no-image is only supported on the art runtime"
- exit 1
- fi
- run_args+=(--no-image)
-fi
+ if have_image == "no":
+ if runtime != "art":
+ error("--no-image is only supported on the art runtime")
+ sys.exit(1)
+ run_args += ["--no-image"]
-if [ "$create_runner" = "yes" -a "$target_mode" = "yes" ]; then
- err_echo "--create-runner does not function for non --host tests"
- usage="yes"
-fi
+ if create_runner == "yes" and target_mode == "yes":
+ error("--create-runner does not function for non --host tests")
+ usage = "yes"
-if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
- err_echo "--dev and --update are mutually exclusive"
- usage="yes"
-fi
+ if dev_mode == "yes" and update_mode == "yes":
+ error("--dev and --update are mutually exclusive")
+ usage = "yes"
-if [ "$dev_mode" = "yes" -a "$quiet" = "yes" ]; then
- err_echo "--dev and --quiet are mutually exclusive"
- usage="yes"
-fi
+ if dev_mode == "yes" and quiet == "yes":
+ error("--dev and --quiet are mutually exclusive")
+ usage = "yes"
-if [ "$bisection_search" = "yes" -a "$prebuild_mode" = "yes" ]; then
- err_echo "--bisection-search and --prebuild are mutually exclusive"
- usage="yes"
-fi
+ if bisection_search == "yes" and prebuild_mode == "yes":
+ error("--bisection-search and --prebuild are mutually exclusive")
+ usage = "yes"
# TODO: Chroot-based bisection search is not supported yet (see below); implement it.
-if [ "$bisection_search" = "yes" -a -n "$chroot" ]; then
- err_echo "--chroot with --bisection-search is unsupported"
- exit 1
-fi
+ if bisection_search == "yes" and chroot:
+ error("--chroot with --bisection-search is unsupported")
+ sys.exit(1)
-if [ "$usage" = "no" ]; then
- if [ "x$1" = "x" -o "x$1" = "x-" ]; then
- test_dir=`basename "$oldwd"`
- else
- test_dir="$1"
- fi
+ if usage == "no":
+ if not arg or arg == "-":
+ test_dir = os.path.basename(oldwd)
+ else:
+ test_dir = arg
- if [ '!' -d "$test_dir" ]; then
- td2=`echo ${test_dir}-*`
- if [ '!' -d "$td2" ]; then
- err_echo "${test_dir}: no such test directory"
- usage="yes"
- fi
- test_dir="$td2"
- fi
+ if not os.path.isdir(test_dir):
+ td2 = glob.glob(f"{test_dir}-*")
+ if len(td2) == 1 and os.path.isdir(td2[0]):
+ test_dir = td2[0]
+ else:
+ error(f"{test_dir}: no such test directory")
+ usage = "yes"
# Shift to get rid of the test name argument. The rest of the arguments
# will get passed to the test run.
- shift
-fi
+ shift()
-if [ "$usage" = "yes" ]; then
- prog=`basename $prog`
- (
- echo "usage:"
- echo " $prog --help Print this message."
- echo " $prog [options] [test-name] Run test normally."
- echo " $prog --dev [options] [test-name] Development mode" \
- "(dumps to stdout)."
- echo " $prog --create-runner [options] [test-name]"
- echo " Creates a runner script for use with other " \
- "tools (e.g. parallel_run.py)."
- echo " The script will only run the test portion, and " \
- "share oat and dex files."
- echo " $prog --update [options] [test-name] Update mode" \
- "(replaces expected-stdout.txt and expected-stderr.txt)."
- echo ' Omitting the test name or specifying "-" will use the' \
- "current directory."
- echo " Runtime Options:"
- echo " -O Run non-debug rather than debug build (off by default)."
- echo " -Xcompiler-option Pass an option to the compiler."
- echo " --runtime-option Pass an option to the runtime."
- echo " --compact-dex-level Specify a compact dex level to the compiler."
- echo " --debug Wait for the default debugger to attach."
- echo " --debug-agent <agent-path>"
- echo " Wait for the given debugger agent to attach. Currently"
- echo " only supported on host."
- echo " --debug-wrap-agent use libwrapagentproperties and tools/libjdwp-compat.props"
- echo " to load the debugger agent specified by --debug-agent."
- echo " --with-agent <agent> Run the test with the given agent loaded with -agentpath:"
- echo " --debuggable Whether to compile Java code for a debugger."
- echo " --gdb Run under gdb; incompatible with some tests."
- echo " --gdb-dex2oat Run dex2oat under the prebuilt lldb."
- echo " --gdbserver Start gdbserver (defaults to port :5039)."
- echo " --gdbserver-port <port>"
- echo " Start gdbserver with the given COMM (see man gdbserver)."
- echo " --gdbserver-bin <binary>"
- echo " Use the given binary as gdbserver."
- echo " --gdb-arg Pass an option to gdb or gdbserver."
- echo " --gdb-dex2oat-args Pass options separated by ';' to lldb for dex2oat."
- echo " --simpleperf Wraps the dalvikvm invocation in 'simpleperf record ..."
- echo " ... simpleperf report' and dumps stats to stdout."
- echo " --temp-path [path] Location where to execute the tests."
- echo " --interpreter Enable interpreter only mode (off by default)."
- echo " --jit Enable jit (off by default)."
- echo " --optimizing Enable optimizing compiler (default)."
- echo " --no-verify Turn off verification (on by default)."
- echo " --verify-soft-fail Force soft fail verification (off by default)."
- echo " Verification is enabled if neither --no-verify"
- echo " nor --verify-soft-fail is specified."
- echo " --no-optimize Turn off optimization (on by default)."
- echo " --no-precise Turn off precise GC (on by default)."
- echo " --zygote Spawn the process from the Zygote." \
- "If used, then the"
- echo " other runtime options are ignored."
- echo " --prebuild Run dex2oat on the files before starting test. (default)"
- echo " --no-prebuild Do not run dex2oat on the files before starting"
- echo " the test."
- echo " --strip-dex Strip the dex files before starting test."
- echo " --relocate Force the use of relocating in the test, making"
- echo " the image and oat files be relocated to a random"
- echo " address before running."
- echo " --no-relocate Force the use of no relocating in the test. (default)"
- echo " --image Run the test using a precompiled boot image. (default)"
- echo " --no-image Run the test without a precompiled boot image."
- echo " --host Use the host-mode virtual machine."
- echo " --invoke-with Pass --invoke-with option to runtime."
- echo " --dalvik Use Dalvik (off by default)."
- echo " --jvm Use a host-local RI virtual machine."
- echo " --use-java-home Use the JAVA_HOME environment variable"
- echo " to find the java compiler and runtime"
- echo " (if applicable) to run the test with."
- echo " --64 Run the test in 64-bit mode"
- echo " --bionic Use the (host, 64-bit only) linux_bionic libc runtime"
- echo " --runtime-zipapex [file]"
- echo " Use the given zipapex file to provide runtime binaries"
- echo " --runtime-extracted-zipapex [dir]"
- echo " Use the given extracted zipapex directory to provide"
- echo " runtime binaries"
- echo " --timeout n Test timeout in seconds"
- echo " --trace Run with method tracing"
- echo " --strace Run with syscall tracing from strace."
- echo " --stream Run method tracing in streaming mode (requires --trace)"
- echo " --gcstress Run with gc stress testing"
- echo " --gcverify Run with gc verification"
- echo " --jvmti-trace-stress Run with jvmti method tracing stress testing"
- echo " --jvmti-step-stress Run with jvmti single step stress testing"
- echo " --jvmti-redefine-stress"
- echo " Run with jvmti method redefinition stress testing"
- echo " --always-clean Delete the test files even if the test fails."
- echo " --never-clean Keep the test files even if the test succeeds."
- echo " --chroot [newroot] Run with root directory set to newroot."
- echo " --android-root [path] The path on target for the android root. (/system by default)."
- echo " --android-i18n-root [path]"
- echo " The path on target for the i18n module root."
- echo " (/apex/com.android.i18n by default)."
- echo " --android-art-root [path]"
- echo " The path on target for the ART module root."
- echo " (/apex/com.android.art by default)."
- echo " --android-tzdata-root [path]"
- echo " The path on target for the Android Time Zone Data root."
- echo " (/apex/com.android.tzdata by default)."
- echo " --dex2oat-swap Use a dex2oat swap file."
- echo " --instruction-set-features [string]"
- echo " Set instruction-set-features for compilation."
- echo " --quiet Don't print anything except failure messages"
- echo " --external-log-tags Use ANDROID_LOG_TAGS to set a custom logging level for"
- echo " a test run."
- echo " --bisection-search Perform bisection bug search."
- echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild."
- echo " --suspend-timeout Change thread suspend timeout ms (default 500000)."
- echo " --dex2oat-jobs Number of dex2oat jobs."
- ) 1>&2 # Direct to stderr so usage is not printed if --quiet is set.
- exit 1
-fi
+ if usage == "yes":
+ prog = os.path.basename(__file__)
+ # pyformat: disable
+ help=(
+ "usage:\n"
+ f" $prog --help Print this message.\n"
+ f" $prog [options] [test-name] Run test normally.\n"
+ f" $prog --dev [options] [test-name] Development mode\n"
+ "(dumps to stdout).\n"
+ f" $prog --create-runner [options] [test-name]\n"
+ " Creates a runner script for use with other \n"
+ "tools (e.g. parallel_run.py).\n"
+ " The script will only run the test portion, and \n"
+ "share oat and dex files.\n"
+ f" $prog --update [options] [test-name] Update mode\n"
+ "(replaces expected-stdout.txt and expected-stderr.txt).\n"
+ ' Omitting the test name or specifying "-" will use the\n'
+ "current directory.\n"
+ " Runtime Options:\n"
+ " -O Run non-debug rather than debug build (off by default).\n"
+ " -Xcompiler-option Pass an option to the compiler.\n"
+ " --runtime-option Pass an option to the runtime.\n"
+ " --compact-dex-level Specify a compact dex level to the compiler.\n"
+ " --debug Wait for the default debugger to attach.\n"
+ " --debug-agent <agent-path>\n"
+ " Wait for the given debugger agent to attach. Currently\n"
+ " only supported on host.\n"
+ " --debug-wrap-agent use libwrapagentproperties and tools/libjdwp-compat.props\n"
+ " to load the debugger agent specified by --debug-agent.\n"
+ " --with-agent <agent> Run the test with the given agent loaded with -agentpath:\n"
+ " --debuggable Whether to compile Java code for a debugger.\n"
+ " --gdb Run under gdb; incompatible with some tests.\n"
+ " --gdb-dex2oat Run dex2oat under the prebuilt lldb.\n"
+ " --gdbserver Start gdbserver (defaults to port :5039).\n"
+ " --gdbserver-port <port>\n"
+ " Start gdbserver with the given COMM (see man gdbserver).\n"
+ " --gdbserver-bin <binary>\n"
+ " Use the given binary as gdbserver.\n"
+ " --gdb-arg Pass an option to gdb or gdbserver.\n"
+ " --gdb-dex2oat-args Pass options separated by ';' to lldb for dex2oat.\n"
+ " --simpleperf Wraps the dalvikvm invocation in 'simpleperf record ...\n"
+ " ... simpleperf report' and dumps stats to stdout.\n"
+ " --temp-path [path] Location where to execute the tests.\n"
+ " --interpreter Enable interpreter only mode (off by default).\n"
+ " --jit Enable jit (off by default).\n"
+ " --optimizing Enable optimizing compiler (default).\n"
+ " --no-verify Turn off verification (on by default).\n"
+ " --verify-soft-fail Force soft fail verification (off by default).\n"
+ " Verification is enabled if neither --no-verify\n"
+ " nor --verify-soft-fail is specified.\n"
+ " --no-optimize Turn off optimization (on by default).\n"
+ " --no-precise Turn off precise GC (on by default).\n"
+ " --zygote Spawn the process from the Zygote.\n"
+ "If used, then the\n"
+ " other runtime options are ignored.\n"
+ " --prebuild Run dex2oat on the files before starting test. (default)\n"
+ " --no-prebuild Do not run dex2oat on the files before starting\n"
+ " the test.\n"
+ " --strip-dex Strip the dex files before starting test.\n"
+ " --relocate Force the use of relocating in the test, making\n"
+ " the image and oat files be relocated to a random\n"
+ " address before running.\n"
+ " --no-relocate Force the use of no relocating in the test. (default)\n"
+ " --image Run the test using a precompiled boot image. (default)\n"
+ " --no-image Run the test without a precompiled boot image.\n"
+ " --host Use the host-mode virtual machine.\n"
+ " --invoke-with Pass --invoke-with option to runtime.\n"
+ " --dalvik Use Dalvik (off by default).\n"
+ " --jvm Use a host-local RI virtual machine.\n"
+ " --use-java-home Use the JAVA_HOME environment variable\n"
+ " to find the java compiler and runtime\n"
+ " (if applicable) to run the test with.\n"
+ " --64 Run the test in 64-bit mode\n"
+ " --bionic Use the (host, 64-bit only) linux_bionic libc runtime\n"
+ " --runtime-zipapex [file]\n"
+ " Use the given zipapex file to provide runtime binaries\n"
+ " --runtime-extracted-zipapex [dir]\n"
+ " Use the given extracted zipapex directory to provide\n"
+ " runtime binaries\n"
+ " --timeout n Test timeout in seconds\n"
+ " --trace Run with method tracing\n"
+ " --strace Run with syscall tracing from strace.\n"
+ " --stream Run method tracing in streaming mode (requires --trace)\n"
+ " --gcstress Run with gc stress testing\n"
+ " --gcverify Run with gc verification\n"
+ " --jvmti-trace-stress Run with jvmti method tracing stress testing\n"
+ " --jvmti-step-stress Run with jvmti single step stress testing\n"
+ " --jvmti-redefine-stress\n"
+ " Run with jvmti method redefinition stress testing\n"
+ " --always-clean Delete the test files even if the test fails.\n"
+ " --never-clean Keep the test files even if the test succeeds.\n"
+ " --chroot [newroot] Run with root directory set to newroot.\n"
+ " --android-root [path] The path on target for the android root. (/system by default).\n"
+ " --android-i18n-root [path]\n"
+ " The path on target for the i18n module root.\n"
+ " (/apex/com.android.i18n by default).\n"
+ " --android-art-root [path]\n"
+ " The path on target for the ART module root.\n"
+ " (/apex/com.android.art by default).\n"
+ " --android-tzdata-root [path]\n"
+ " The path on target for the Android Time Zone Data root.\n"
+ " (/apex/com.android.tzdata by default).\n"
+ " --dex2oat-swap Use a dex2oat swap file.\n"
+ " --instruction-set-features [string]\n"
+ " Set instruction-set-features for compilation.\n"
+ " --quiet Don't print anything except failure messages\n"
+ " --external-log-tags Use ANDROID_LOG_TAGS to set a custom logging level for\n"
+ " a test run.\n"
+ " --bisection-search Perform bisection bug search.\n"
+ " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild.\n"
+ " --suspend-timeout Change thread suspend timeout ms (default 500000).\n"
+ " --dex2oat-jobs Number of dex2oat jobs.\n"
+ )
+ # pyformat: enable
+ error(help)
+ sys.exit(1)
-cd "$test_dir"
-test_dir=`pwd`
+ os.chdir(test_dir)
+ test_dir = os.getcwd()
-td_info="${test_dir}/${info}"
-td_expected_stdout="${test_dir}/${expected_stdout}"
-td_expected_stderr="${test_dir}/${expected_stderr}"
+ TEST_NAME = os.path.basename(test_dir)
+ export("TEST_NAME", TEST_NAME)
-for td_file in "$td_info" "$td_expected_stdout" "$td_expected_stderr"; do
- if [ ! -r "$td_file" ]; then
- err_echo "${test_dir}: missing file $td_file"
- exit 1
- fi
-done
+ # Tests named '<number>-checker-*' will also have their CFGs verified with
+ # Checker when compiled with Optimizing on host.
+ # Additionally, if the user specifies that the CFG must be dumped, it will
+ # run the checker for any type of test to generate the CFG.
+ if re.match("[0-9]+-checker-", TEST_NAME) or dump_cfg == "true":
+ if runtime == "art" and run_optimizing == "true":
+ # In no-prebuild or no-image mode, the compiler only quickens so disable the checker.
+ if prebuild_mode == "yes":
+ run_checker = "yes"
-export TEST_NAME=`basename ${test_dir}`
+ if target_mode == "no":
+ cfg_output_dir = tmp_dir
+ checker_args = f"--arch={host_arch_name.upper()}"
+ else:
+ cfg_output_dir = DEX_LOCATION
+ checker_args = f"--arch={target_arch_name.upper()}"
-# Tests named '<number>-checker-*' will also have their CFGs verified with
-# Checker when compiled with Optimizing on host.
-# Additionally, if the user specifies that the CFG must be dumped, it will
-# run the checker for any type of test to generate the CFG.
-if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]] || [ "$dump_cfg" = "true" ]; then
- if [ "$runtime" = "art" -a "$run_optimizing" = "true" ]; then
- # In no-prebuild or no-image mode, the compiler only quickens so disable the checker.
- if [ "$prebuild_mode" = "yes" ]; then
- run_checker="yes"
+ if debuggable == "yes":
+ checker_args += " --debuggable"
- if [ "$target_mode" = "no" ]; then
- cfg_output_dir="$tmp_dir"
- checker_args="--arch=${host_arch_name^^}"
- else
- cfg_output_dir="$DEX_LOCATION"
- checker_args="--arch=${target_arch_name^^}"
- fi
+ run_args += [
+ f'-Xcompiler-option "--dump-cfg={cfg_output_dir}/{cfg_output}" -Xcompiler-option -j1'
+ ]
+ checker_args = f"{checker_args} --print-cfg"
- if [ "$debuggable" = "yes" ]; then
- checker_args="$checker_args --debuggable"
- fi
+ run_args += [f'--testlib "{testlib}"']
- run_args+=(-Xcompiler-option "--dump-cfg=$cfg_output_dir/$cfg_output" -Xcompiler-option -j1)
- checker_args="$checker_args --print-cfg"
- fi
- fi
-fi
+ resource.setrlimit(resource.RLIMIT_FSIZE, (file_ulimit * 1024, resource.RLIM_INFINITY))
-run_args+=(--testlib "${testlib}")
+ # Extract run-test data from the zip file.
+ shutil.rmtree(tmp_dir)
+ os.makedirs(f"{tmp_dir}/.unzipped")
+ os.chdir(tmp_dir)
+ m = re.match("[0-9]*([0-9][0-9])-.*", TEST_NAME)
+ assert m, "Can not find test number in " + TEST_NAME
+ SHARD = "HiddenApi" if "hiddenapi" in TEST_NAME else m.group(1)
+ if target_mode == "yes":
+ zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-target-data-shard{SHARD}.zip"
+ zip_entry = f"target/{TEST_NAME}/"
+ elif runtime == "jvm":
+ zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-jvm-data-shard{SHARD}.zip"
+ zip_entry = f"jvm/{TEST_NAME}/"
+ else:
+ zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-host-data-shard{SHARD}.zip"
+ zip_entry = f"host/{TEST_NAME}/"
+ zip = ZipFile(zip_file, "r")
+ zip_entries = [e for e in zip.namelist() if e.startswith(zip_entry)]
+ zip.extractall(Path(tmp_dir) / ".unzipped", members=zip_entries)
+ for entry in (Path(tmp_dir) / ".unzipped" / zip_entry).iterdir():
+ entry.rename(Path(tmp_dir) / entry.name)
-if ! ulimit -f ${file_ulimit}; then
- err_echo "ulimit file size setting failed"
-fi
+ def clean_up(passed: bool):
+ if always_clean == "yes" or (passed and never_clean == "no"):
+ os.chdir(oldwd)
+ shutil.rmtree(tmp_dir)
+ if target_mode == "yes":
+ if ON_VM:
+ run(f"{SSH_CMD} \"rm -rf {chroot_dex_location}\"")
+ else:
+ run(f"adb shell rm -rf {chroot_dex_location}")
+ print(f"{TEST_NAME} files deleted from host" +
+ (" and from target" if target_mode == "yes" else ""))
+ else:
+ print(f"{TEST_NAME} files left in {tmp_dir} on host" +
+ (f" and in {chroot_dex_location} on target" if target_mode == "yes" else ""))
+ atexit.unregister(clean_up)
+ # TODO: Run this in global try-finally once the script is more refactored.
+ atexit.register(clean_up, passed=False)
-# Extract run-test data from the zip file.
-rm -rf "$tmp_dir"
-mkdir -p "$tmp_dir/.unzipped"
-cd "$tmp_dir"
-if [[ "$target_mode" == "yes" ]]; then
- zip_file="${ANDROID_HOST_OUT}/etc/art/art-run-test-target-data.zip"
- zip_entry="target/${TEST_NAME}"
-elif [[ $runtime == "jvm" ]]; then
- zip_file="${ANDROID_HOST_OUT}/etc/art/art-run-test-jvm-data.zip"
- zip_entry="jvm/${TEST_NAME}"
-else
- zip_file="${ANDROID_HOST_OUT}/etc/art/art-run-test-host-data.zip"
- zip_entry="host/${TEST_NAME}"
-fi
-unzip -q "${zip_file}" "${zip_entry}/*" -d "$tmp_dir/.unzipped"
-mv "$tmp_dir"/.unzipped/${zip_entry}/* "$tmp_dir"
+ ctx = RunTestContext(Path(tmp_dir), target_mode == "yes", chroot, DEX_LOCATION, TEST_NAME)
+ td_info = f"{test_dir}/{info}"
+ for td_file in [td_info, ctx.expected_stdout, ctx.expected_stderr]:
+ assert os.access(td_file, os.R_OK)
-good="no"
-good_run="yes"
-export TEST_RUNTIME="${runtime}"
-if [ "$dev_mode" = "yes" ]; then
- echo "${test_dir}: running..." 1>&2
- "./${run}" "${run_args[@]}" "$@"
- run_exit="$?"
+ joined_run_args = " ".join(run_args)
+ joined_args = " ".join(args)
- if [ "$run_exit" = "0" ]; then
- if [ "$run_checker" = "yes" ]; then
- if [ "$target_mode" = "yes" ]; then
- adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
- fi
- "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1
- checker_exit="$?"
- if [ "$checker_exit" = "0" ]; then
- good="yes"
- fi
- err_echo "checker exit status: $checker_exit"
- else
- good="yes"
- fi
- fi
- echo "run exit status: $run_exit" 1>&2
-elif [ "$update_mode" = "yes" ]; then
- echo "${test_dir}: running..." 1>&2
- "./${run}" "${run_args[@]}" "$@" >"$test_stdout" 2>"$test_stderr"
- if [ "$run_checker" = "yes" ]; then
- if [ "$target_mode" = "yes" ]; then
- adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
- fi
- "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >>"$test_stdout" 2>>"$test_stderr"
- fi
- sed -e 's/[[:cntrl:]]$//g' <"$test_stdout" >"${td_expected_stdout}"
- sed -e 's/[[:cntrl:]]$//g' <"$test_stderr" >"${td_expected_stderr}"
- good="yes"
-else
- echo "${test_dir}: running..." 1>&2
- "./${run}" "${run_args[@]}" "$@" >"$test_stdout" 2>"$test_stderr"
- run_exit="$?"
- if [ "$run_exit" != "0" ]; then
- err_echo "run exit status: $run_exit"
- good_run="no"
- elif [ "$run_checker" = "yes" ]; then
- if [ "$target_mode" = "yes" ]; then
- adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
- fi
- "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >>"$test_stdout" 2>>"$test_stderr"
- checker_exit="$?"
- if [ "$checker_exit" != "0" ]; then
- err_echo "checker exit status: $checker_exit"
- good_run="no"
- else
- good_run="yes"
- fi
- else
- good_run="yes"
- fi
- ./$check_cmd "$expected_stdout" "$test_stdout" "$expected_stderr" "$test_stderr"
- if [ "$?" = "0" ]; then
- if [ "$good_run" = "yes" ]; then
- # test_stdout == expected_stdout && test_stderr == expected_stderr
- good="yes"
- echo "${test_dir}: succeeded!" 1>&2
- fi
- fi
-fi
+ # Create runner (bash script that executes the whole test)
+ def create_runner_script() -> Path:
+ parsed_args = default_run_module.parse_args(shlex.split(" ".join(run_args + args)))
+ parsed_args.stdout_file = os.path.join(DEX_LOCATION, test_stdout)
+ parsed_args.stderr_file = os.path.join(DEX_LOCATION, test_stderr)
-(
- if [ "$good" != "yes" -a "$update_mode" != "yes" ]; then
- echo "${test_dir}: FAILED!"
- echo ' '
- echo '#################### info'
- cat "${td_info}" | sed 's/^/# /g'
- echo '#################### stdout diffs'
- if [ "$run_checker" == "yes" ]; then
- # Checker failures dump the whole CFG, so we output the whole diff.
- diff --strip-trailing-cr -u "$expected_stdout" "$test_stdout"
- else
- diff --strip-trailing-cr -u "$expected_stdout" "$test_stdout" | tail -n 10000
- fi
- echo '####################'
- echo '#################### stderr diffs'
- diff --strip-trailing-cr -u "$expected_stderr" "$test_stderr" | tail -n 10000
- echo '####################'
- if [ "$strace" = "yes" ]; then
- echo '#################### strace output'
- tail -n 3000 "$tmp_dir/$strace_output"
- echo '####################'
- fi
- if [ "x$target_mode" = "xno" -a "x$SANITIZE_HOST" = "xaddress" ]; then
- # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The
- # tools used by the given ABI work for both x86 and x86-64.
- echo "ABI: 'x86_64'" | cat - "$test_stdout" "$test_stderr" \
- | $ANDROID_BUILD_TOP/development/scripts/stack | tail -n 3000
- fi
- echo ' '
- fi
+ ctx.run(f"cd {DEX_LOCATION}")
+ if target_mode != "yes":
+ # Make "out" directory accessible from test directory.
+ ctx.run(f"ln -s -f -t {DEX_LOCATION} {ANDROID_BUILD_TOP}/out")
+ # Clear the stdout/stderr files (create empty files).
+ ctx.run(f"echo -n > {test_stdout} && echo -n > {test_stderr}")
-) 2>&${real_stderr} 1>&2
+ script = Path(tmp_dir) / "run.py"
+ if script.exists():
+ module = SourceFileLoader("run_" + TEST_NAME, str(script)).load_module()
+ module.run(ctx, parsed_args)
+ else:
+ default_run_module.default_run(ctx, parsed_args)
-# Copy the generated CFG to the specified path.
-if [ $dump_cfg = "true" ]; then
- if [ $run_optimizing != "true" ]; then
- err_echo "Can't dump the .cfg if the compiler type isn't set to \"optimizing\"."
- else
- if [ "$target_mode" = "yes" ]; then
- adb pull $chroot/$cfg_output_dir/$cfg_output $dump_cfg_path
- else
- cp $cfg_output_dir/$cfg_output $dump_cfg_path
- fi
- fi
-fi
+ runner = Path(tmp_dir) / "run.sh"
+ runner.write_text("\n".join(ctx.runner))
+ runner.chmod(0o777)
+ return runner
-# Attempt bisection only if the test failed.
-# TODO: Implement support for chroot-based bisection search.
-if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then
- # Bisecting works by skipping different optimization passes which breaks checker assertions.
- if [ "$run_checker" == "yes" ]; then
- echo "${test_dir}: not bisecting, checker test." 1>&2
- else
- # Increase file size limit, bisection search can generate large logfiles.
- echo "${test_dir}: bisecting..." 1>&2
- cwd=`pwd`
- maybe_device_mode=""
- raw_cmd=""
- if [ "$target_mode" = "yes" ]; then
- # Produce cmdline.sh in $chroot_dex_location. "$@" is passed as a runtime option
- # so that cmdline.sh forwards its arguments to dalvikvm. invoke-with is set
- # to exec in order to preserve pid when calling dalvikvm. This is required
- # for bisection search to correctly retrieve logs from device.
- "./${run}" "${run_args[@]}" --runtime-option '"$@"' --invoke-with exec --dry-run "$@" &> /dev/null
- adb shell chmod u+x "$chroot_dex_location/cmdline.sh"
- maybe_device_mode="--device"
- raw_cmd="$DEX_LOCATION/cmdline.sh"
- else
- raw_cmd="$cwd/${run} --external-log-tags "${run_args[@]}" $@"
- fi
- # TODO: Pass a `--chroot` option to the bisection_search.py script and use it there.
- $ANDROID_BUILD_TOP/art/tools/bisection_search/bisection_search.py \
- $maybe_device_mode \
- --raw-cmd="$raw_cmd" \
- --check-script="$cwd/check" \
- --expected-output="$cwd/expected-stdout.txt" \
- --logfile="$cwd/bisection_log.txt" \
- --timeout=${timeout:-300}
- fi
-fi
+ # Test might not execute anything but we still expect the output files to exist.
+ Path(test_stdout).touch()
+ Path(test_stderr).touch()
-# Clean up test files.
-if [ "$always_clean" = "yes" -o "$good" = "yes" ] && [ "$never_clean" = "no" ]; then
- cd "$oldwd"
- rm -rf "$tmp_dir"
- if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then
- adb shell rm -rf $chroot_dex_location
- fi
- if [ "$good" = "yes" ]; then
- exit 0
- fi
-fi
+ export("TEST_RUNTIME", runtime)
+ print(f"{test_dir}: Create runner script...")
+ runner = create_runner_script()
-(
- if [ "$always_clean" = "yes" ]; then
- echo "${TEST_NAME} files deleted from host "
- if [ "$target_mode" == "yes" ]; then
- echo "and from target"
- fi
- else
- echo "${TEST_NAME} files left in ${tmp_dir} on host"
- if [ "$target_mode" == "yes" ]; then
- echo "and in ${chroot_dex_location} on target"
- fi
- fi
+ print(f"{test_dir}: Run...")
+ if target_mode == "yes":
+ # Prepare the on-device test directory
+ if ON_VM:
+ run(f"{SSH_CMD} 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'")
+ else:
+ run("adb root")
+ run("adb wait-for-device")
+ run(f"adb shell 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'")
+ push_files = [Path(runner.name)]
+ push_files += list(Path(".").glob(f"{TEST_NAME}*.jar"))
+ push_files += list(Path(".").glob(f"expected-*.txt"))
+ push_files += [p for p in [Path("profile"), Path("res")] if p.exists()]
+ push_files = " ".join(map(str, push_files))
+ if ON_VM:
+ run(f"{SCP_CMD} {push_files} {SSH_USER}@{SSH_HOST}:{chroot_dex_location}")
+ else:
+ run("adb push {} {}".format(push_files, chroot_dex_location))
-) 2>&${real_stderr} 1>&2
+ if ON_VM:
+ run(f"{SSH_CMD} {CHROOT_CMD} bash {DEX_LOCATION}/run.sh",
+ fail_message=f"Runner {chroot_dex_location}/run.sh failed")
+ else:
+ chroot_prefix = f"chroot {chroot}" if chroot else ""
+ run(f"adb shell {chroot_prefix} sh {DEX_LOCATION}/run.sh",
+ fail_message=f"Runner {chroot_dex_location}/run.sh failed")
-if [ "$never_clean" = "yes" ] && [ "$good" = "yes" ]; then
- exit 0
-else
- exit 1
-fi
+ # Copy the on-device stdout/stderr to host.
+ pull_files = [test_stdout, test_stderr, "expected-stdout.txt", "expected-stderr.txt"]
+ if ON_VM:
+ srcs = " ".join(f"{SSH_USER}@{SSH_HOST}:{chroot_dex_location}/{f}" for f in pull_files)
+ run(f"{SCP_CMD} {srcs} .")
+ else:
+ run("adb pull {} .".format(" ".join(f"{chroot_dex_location}/{f}" for f in pull_files)))
+ else:
+ run(str(runner), fail_message=f"Runner {str(runner)} failed")
+
+ # NB: There is no exit code or return value.
+ # Failing tests just raise python exception.
+ os.chdir(tmp_dir)
+ if update_mode == "yes":
+ for src, dst in [(test_stdout, os.path.join(test_dir, ctx.expected_stdout.name)),
+ (test_stderr, os.path.join(test_dir, ctx.expected_stderr.name))]:
+ if "[DO_NOT_UPDATE]" not in open(dst).readline():
+ copyfile(src, dst)
+
+ print("#################### info")
+ run(f'cat "{td_info}" | sed "s/^/# /g"')
+ print("#################### stdout diff")
+ proc_out = run(f'diff --strip-trailing-cr -u '
+ f'"{ctx.expected_stdout}" "{test_stdout}"', check=False)
+ print("#################### stderr diff")
+ proc_err = run(f'diff --strip-trailing-cr -u '
+ f'"{ctx.expected_stderr}" "{test_stderr}"', check=False)
+ if strace == "yes":
+ print("#################### strace output (trimmed to 3000 lines)")
+ # Some tests do not run dalvikvm, in which case the trace does not exist.
+ run(f'tail -n 3000 "{tmp_dir}/{strace_output}"', check=False)
+ SANITIZE_HOST = os.environ.get("SANITIZE_HOST")
+ if target_mode == "no" and SANITIZE_HOST == "address":
+ # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The
+ # tools used by the given ABI work for both x86 and x86-64.
+ print("#################### symbolizer (trimmed to 3000 lines)")
+ run(f'''echo "ABI: 'x86_64'" | cat - "{test_stdout}" "{test_stderr}"'''
+ f"""| {ANDROID_BUILD_TOP}/development/scripts/stack | tail -n 3000""")
+ print("####################", flush=True)
+ if proc_out.returncode != 0 or proc_err.returncode != 0:
+ kind = ((["stdout"] if proc_out.returncode != 0 else []) +
+ (["stderr"] if proc_err.returncode != 0 else []))
+ fail("{} did not match the expected file".format(" and ".join(kind)))
+
+ if run_checker == "yes":
+ if target_mode == "yes":
+ if ON_VM:
+ run(f'{SCP_CMD} "{SSH_USER}@${SSH_HOST}:{CHROOT}/{cfg_output_dir}/{cfg_output}"')
+ else:
+ run(f'adb pull "{chroot}/{cfg_output_dir}/{cfg_output}"')
+ run(f'"{checker}" -q {checker_args} "{cfg_output}" "{tmp_dir}"',
+ fail_message="CFG checker failed")
+
+ # Copy the generated CFG to the specified path.
+ if dump_cfg == "true":
+ assert run_optimizing == "true", "The CFG can be dumped only in optimizing mode"
+ if target_mode == "yes":
+ if ON_VM:
+ run(f'{SCP_CMD} "{SSH_USER}@${SSH_HOST}:{CHROOT}/{cfg_output_dir}/{cfg_output} {dump_cfg_output}"')
+ else:
+ run(f"adb pull {chroot}/{cfg_output_dir}/{cfg_output} {dump_cfg_path}")
+ else:
+ run(f"cp {cfg_output_dir}/{cfg_output} {dump_cfg_path}")
+
+ clean_up(passed=True)
+ print(f"{COLOR_GREEN}{test_dir}: PASSED{COLOR_NORMAL}")
diff --git a/test/run-test-build.py b/test/run-test-build.py
deleted file mode 100755
index f8eb283..0000000
--- a/test/run-test-build.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-
-"""
-This scripts compiles Java files which are needed to execute run-tests.
-It is intended to be used only from soong genrule.
-"""
-
-import argparse, os, tempfile, shutil, subprocess, glob, textwrap, re, json, concurrent.futures
-
-ZIP = "prebuilts/build-tools/linux-x86/bin/soong_zip"
-BUILDFAILURES = json.loads(open(os.path.join("art", "test", "buildfailures.json"), "rt").read())
-
-def copy_sources(args, tmp, mode, srcdir):
- """Copy test files from Android tree into the build sandbox and return its path."""
-
- join = os.path.join
- test = os.path.basename(srcdir)
- dstdir = join(tmp, mode, test)
-
- # Don't build tests that are disabled since they might not compile (e.g. on jvm).
- def is_buildfailure(kf):
- return test in kf.get("tests", []) and mode == kf.get("variant") and not kf.get("env_vars")
- if any(is_buildfailure(kf) for kf in BUILDFAILURES):
- return None
-
- # Copy all source files to the temporary directory.
- shutil.copytree(srcdir, dstdir)
-
- # Copy the default scripts if the test does not have a custom ones.
- for name in ["build", "run", "check"]:
- src, dst = f"art/test/etc/default-{name}", join(dstdir, name)
- if os.path.exists(dst):
- shutil.copy2(src, dstdir) # Copy default script next to the custom script.
- else:
- shutil.copy2(src, dst) # Use just the default script.
- os.chmod(dst, 0o755)
-
- return dstdir
-
-def build_test(args, mode, dstdir):
- """Run the build script for single run-test"""
-
- join = os.path.join
- build_top = os.getcwd()
- java_home = os.environ.get("JAVA_HOME")
- tools_dir = os.path.abspath(join(os.path.dirname(__file__), "../../../out/bin"))
- env = {
- "PATH": os.environ.get("PATH"),
- "ANDROID_BUILD_TOP": build_top,
- "ART_TEST_RUN_TEST_BOOTCLASSPATH": join(build_top, args.bootclasspath),
- "TEST_NAME": os.path.basename(dstdir),
- "SOONG_ZIP": join(build_top, "prebuilts/build-tools/linux-x86/bin/soong_zip"),
- "ZIPALIGN": join(build_top, "prebuilts/build-tools/linux-x86/bin/zipalign"),
- "JAVA": join(java_home, "bin/java"),
- "JAVAC": join(java_home, "bin/javac"),
- "JAVAC_ARGS": "-g -Xlint:-options -source 1.8 -target 1.8",
- "D8": join(tools_dir, "d8"),
- "HIDDENAPI": join(tools_dir, "hiddenapi"),
- "JASMIN": join(tools_dir, "jasmin"),
- "SMALI": join(tools_dir, "smali"),
- "NEED_DEX": {"host": "true", "target": "true", "jvm": "false"}[mode],
- "USE_DESUGAR": "true",
- }
- proc = subprocess.run([join(dstdir, "build"), "--" + mode],
- cwd=dstdir,
- env=env,
- encoding=os.sys.stdout.encoding,
- stderr=subprocess.STDOUT,
- stdout=subprocess.PIPE)
- return proc.stdout, proc.returncode
-
-def main():
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument("--out", help="Path of the generated ZIP file with the build data")
- parser.add_argument('--mode', choices=['host', 'jvm', 'target'])
- parser.add_argument("--shard", help="Identifies subset of tests to build (00..99)")
- parser.add_argument("--bootclasspath", help="JAR files used for javac compilation")
- args = parser.parse_args()
-
- with tempfile.TemporaryDirectory(prefix=os.path.basename(__file__)) as tmp:
- srcdirs = sorted(glob.glob(os.path.join("art", "test", "*")))
- srcdirs = filter(lambda srcdir: re.match(".*/\d*{}-.*".format(args.shard), srcdir), srcdirs)
- dstdirs = [copy_sources(args, tmp, args.mode, srcdir) for srcdir in srcdirs]
- dstdirs = filter(lambda dstdir: dstdir, dstdirs) # Remove None (skipped tests).
- with concurrent.futures.ThreadPoolExecutor() as pool:
- for stdout, exitcode in pool.map(lambda dstdir: build_test(args, args.mode, dstdir), dstdirs):
- if stdout:
- print(stdout.strip())
- assert(exitcode == 0) # Build failed. Add test to buildfailures.json if this is expected.
-
- # Create the final zip file which contains the content of the temporary directory.
- proc = subprocess.run([ZIP, "-o", args.out, "-C", tmp, "-D", tmp], check=True)
-
-if __name__ == "__main__":
- main()
diff --git a/test/run_test_build.py b/test/run_test_build.py
new file mode 100755
index 0000000..75cd64f
--- /dev/null
+++ b/test/run_test_build.py
@@ -0,0 +1,522 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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.
+
+"""
+This scripts compiles Java files which are needed to execute run-tests.
+It is intended to be used only from soong genrule.
+"""
+
+import argparse
+import functools
+import glob
+import os
+import pathlib
+import shlex
+import shutil
+import subprocess
+import sys
+import zipfile
+
+from argparse import ArgumentParser
+from fcntl import lockf, LOCK_EX, LOCK_NB
+from importlib.machinery import SourceFileLoader
+from concurrent.futures import ThreadPoolExecutor
+from os import environ, getcwd, chdir, cpu_count, chmod
+from os.path import relpath
+from pathlib import Path
+from pprint import pprint
+from re import match
+from shutil import copytree, rmtree
+from subprocess import run
+from tempfile import TemporaryDirectory, NamedTemporaryFile
+from typing import Dict, List, Union, Set, Optional
+
+USE_RBE = 100 # Percentage of tests that can use RBE (between 0 and 100)
+
+lock_file = None # Keep alive as long as this process is alive.
+
+RBE_D8_DISABLED_FOR = {
+ "952-invoke-custom", # b/228312861: RBE uses wrong inputs.
+ "979-const-method-handle", # b/228312861: RBE uses wrong inputs.
+}
+
+class BuildTestContext:
+ def __init__(self, args, android_build_top, test_dir):
+ self.android_build_top = android_build_top.absolute()
+ self.bootclasspath = args.bootclasspath.absolute()
+ self.test_name = test_dir.name
+ self.test_dir = test_dir.absolute()
+ self.mode = args.mode
+ self.jvm = (self.mode == "jvm")
+ self.host = (self.mode == "host")
+ self.target = (self.mode == "target")
+ assert self.jvm or self.host or self.target
+
+ self.java_home = Path(os.environ.get("JAVA_HOME")).absolute()
+ self.java_path = self.java_home / "bin/java"
+ self.javac_path = self.java_home / "bin/javac"
+ self.javac_args = "-g -Xlint:-options -source 1.8 -target 1.8"
+
+ # Helper functions to execute tools.
+ self.d8 = functools.partial(self.run, args.d8.absolute())
+ self.jasmin = functools.partial(self.run, args.jasmin.absolute())
+ self.javac = functools.partial(self.run, self.javac_path)
+ self.smali = functools.partial(self.run, args.smali.absolute())
+ self.soong_zip = functools.partial(self.run, args.soong_zip.absolute())
+ self.zipalign = functools.partial(self.run, args.zipalign.absolute())
+ if args.hiddenapi:
+ self.hiddenapi = functools.partial(self.run, args.hiddenapi.absolute())
+
+ # RBE wrapper for some of the tools.
+ if "RBE_server_address" in os.environ and USE_RBE > (hash(self.test_name) % 100):
+ self.rbe_exec_root = os.environ.get("RBE_exec_root")
+ self.rbe_rewrapper = self.android_build_top / "prebuilts/remoteexecution-client/live/rewrapper"
+ if self.test_name not in RBE_D8_DISABLED_FOR:
+ self.d8 = functools.partial(self.rbe_d8, args.d8.absolute())
+ self.javac = functools.partial(self.rbe_javac, self.javac_path)
+ self.smali = functools.partial(self.rbe_smali, args.smali.absolute())
+
+ # Minimal environment needed for bash commands that we execute.
+ self.bash_env = {
+ "ANDROID_BUILD_TOP": self.android_build_top,
+ "D8": args.d8.absolute(),
+ "JAVA": self.java_path,
+ "JAVAC": self.javac_path,
+ "JAVAC_ARGS": self.javac_args,
+ "JAVA_HOME": self.java_home,
+ "PATH": os.environ["PATH"],
+ "PYTHONDONTWRITEBYTECODE": "1",
+ "SMALI": args.smali.absolute(),
+ "SOONG_ZIP": args.soong_zip.absolute(),
+ "TEST_NAME": self.test_name,
+ }
+
+ def bash(self, cmd):
+ return subprocess.run(cmd,
+ shell=True,
+ cwd=self.test_dir,
+ env=self.bash_env,
+ check=True)
+
+ def run(self, executable: pathlib.Path, args: List[Union[pathlib.Path, str]]):
+ assert isinstance(executable, pathlib.Path), executable
+ cmd: List[Union[pathlib.Path, str]] = []
+ if executable.suffix == ".sh":
+ cmd += ["/bin/bash"]
+ cmd += [executable]
+ cmd += args
+ env = self.bash_env
+ env.update({k: v for k, v in os.environ.items() if k.startswith("RBE_")})
+ # Make paths relative as otherwise we could create too long command line.
+ for i, arg in enumerate(cmd):
+ if isinstance(arg, pathlib.Path):
+ assert arg.absolute(), arg
+ cmd[i] = relpath(arg, self.test_dir)
+ elif isinstance(arg, list):
+ assert all(p.absolute() for p in arg), arg
+ cmd[i] = ":".join(relpath(p, self.test_dir) for p in arg)
+ else:
+ assert isinstance(arg, str), arg
+ p = subprocess.run(cmd,
+ encoding=sys.stdout.encoding,
+ cwd=self.test_dir,
+ env=self.bash_env,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE)
+ if p.returncode != 0:
+ raise Exception("Command failed with exit code {}\n$ {}\n{}".format(
+ p.returncode, " ".join(map(str, cmd)), p.stdout))
+ return p
+
+ def rbe_wrap(self, args, inputs: Set[pathlib.Path]=None):
+ with NamedTemporaryFile(mode="w+t") as input_list:
+ inputs = inputs or set()
+ for i, arg in enumerate(args):
+ if isinstance(arg, pathlib.Path):
+ assert arg.absolute(), arg
+ inputs.add(arg)
+ elif isinstance(arg, list):
+ assert all(p.absolute() for p in arg), arg
+ inputs.update(arg)
+ input_list.writelines([relpath(i, self.rbe_exec_root)+"\n" for i in inputs])
+ input_list.flush()
+ return self.run(self.rbe_rewrapper, [
+ "--platform=" + os.environ["RBE_platform"],
+ "--input_list_paths=" + input_list.name,
+ ] + args)
+
+ def rbe_javac(self, javac_path:Path, args):
+ output = relpath(Path(args[args.index("-d") + 1]), self.rbe_exec_root)
+ return self.rbe_wrap(["--output_directories", output, javac_path] + args)
+
+ def rbe_d8(self, d8_path:Path, args):
+ inputs = set([d8_path.parent.parent / "framework/d8.jar"])
+ output = relpath(Path(args[args.index("--output") + 1]), self.rbe_exec_root)
+ return self.rbe_wrap([
+ "--output_files" if output.endswith(".jar") else "--output_directories", output,
+ "--toolchain_inputs=prebuilts/jdk/jdk17/linux-x86/bin/java",
+ d8_path] + args, inputs)
+
+ def rbe_smali(self, smali_path:Path, args):
+ inputs = set([smali_path.parent.parent / "framework/smali.jar"])
+ output = relpath(Path(args[args.index("--output") + 1]), self.rbe_exec_root)
+ return self.rbe_wrap([
+ "--output_files", output,
+ "--toolchain_inputs=prebuilts/jdk/jdk17/linux-x86/bin/java",
+ smali_path] + args, inputs)
+
+ def build(self) -> None:
+ script = self.test_dir / "build.py"
+ if script.exists():
+ module = SourceFileLoader("build_" + self.test_name,
+ str(script)).load_module()
+ module.build(self)
+ else:
+ self.default_build()
+
+ def default_build(
+ self,
+ use_desugar=True,
+ use_hiddenapi=True,
+ need_dex=None,
+ zip_compression_method="deflate",
+ zip_align_bytes=None,
+ api_level:Union[int, str]=26, # Can also be named alias (string).
+ javac_args=[],
+ javac_classpath: List[Path]=[],
+ d8_flags=[],
+ smali_args=[],
+ use_smali=True,
+ use_jasmin=True,
+ ):
+ javac_classpath = javac_classpath.copy() # Do not modify default value.
+
+ # Wrap "pathlib.Path" with our own version that ensures all paths are absolute.
+ # Plain filenames are assumed to be relative to self.test_dir and made absolute.
+ class Path(pathlib.Path):
+ def __new__(cls, filename: str):
+ path = pathlib.Path(filename)
+ return path if path.is_absolute() else (self.test_dir / path)
+
+ need_dex = (self.host or self.target) if need_dex is None else need_dex
+
+ if self.jvm:
+ # No desugaring on jvm because it supports the latest functionality.
+ use_desugar = False
+
+ # Set API level for smali and d8.
+ if isinstance(api_level, str):
+ API_LEVEL = {
+ "default-methods": 24,
+ "parameter-annotations": 25,
+ "agents": 26,
+ "method-handles": 26,
+ "var-handles": 28,
+ }
+ api_level = API_LEVEL[api_level]
+ assert isinstance(api_level, int), api_level
+
+ def zip(zip_target: Path, *files: Path):
+ zip_args = ["-o", zip_target, "-C", zip_target.parent]
+ if zip_compression_method == "store":
+ zip_args.extend(["-L", "0"])
+ for f in files:
+ zip_args.extend(["-f", f])
+ self.soong_zip(zip_args)
+
+ if zip_align_bytes:
+ # zipalign does not operate in-place, so write results to a temp file.
+ with TemporaryDirectory() as tmp_dir:
+ tmp_file = Path(tmp_dir) / "aligned.zip"
+ self.zipalign(["-f", str(zip_align_bytes), zip_target, tmp_file])
+ # replace original zip target with our temp file.
+ tmp_file.rename(zip_target)
+
+
+ def make_jasmin(dst_dir: Path, src_dir: Path) -> Optional[Path]:
+ if not use_jasmin or not src_dir.exists():
+ return None # No sources to compile.
+ dst_dir.mkdir()
+ self.jasmin(["-d", dst_dir] + sorted(src_dir.glob("**/*.j")))
+ return dst_dir
+
+ def make_smali(dst_dex: Path, src_dir: Path) -> Optional[Path]:
+ if not use_smali or not src_dir.exists():
+ return None # No sources to compile.
+ self.smali(["-JXmx512m", "assemble"] + smali_args + ["--api", str(api_level)] +
+ ["--output", dst_dex] + sorted(src_dir.glob("**/*.smali")))
+ return dst_dex
+
+ def make_java(dst_dir: Path, *src_dirs: Path) -> Optional[Path]:
+ if not any(src_dir.exists() for src_dir in src_dirs):
+ return None # No sources to compile.
+ dst_dir.mkdir(exist_ok=True)
+ args = self.javac_args.split(" ") + javac_args
+ args += ["-implicit:none", "-encoding", "utf8", "-d", dst_dir]
+ if not self.jvm:
+ args += ["-bootclasspath", self.bootclasspath]
+ if javac_classpath:
+ args += ["-classpath", javac_classpath]
+ for src_dir in src_dirs:
+ args += sorted(src_dir.glob("**/*.java"))
+ self.javac(args)
+ javac_post = Path("javac_post.sh")
+ if javac_post.exists():
+ self.run(javac_post, [dst_dir])
+ return dst_dir
+
+
+ # Make a "dex" file given a directory of classes. This will be
+ # packaged in a jar file.
+ def make_dex(src_dir: Path):
+ dst_jar = Path(src_dir.name + ".jar")
+ args = d8_flags + ["--min-api", str(api_level), "--output", dst_jar]
+ args += ["--lib", self.bootclasspath] if use_desugar else ["--no-desugaring"]
+ args += sorted(src_dir.glob("**/*.class"))
+ self.d8(args)
+
+ # D8 outputs to JAR files today rather than DEX files as DX used
+ # to. To compensate, we extract the DEX from d8's output to meet the
+ # expectations of make_dex callers.
+ dst_dex = Path(src_dir.name + ".dex")
+ with TemporaryDirectory() as tmp_dir:
+ zipfile.ZipFile(dst_jar, "r").extractall(tmp_dir)
+ (Path(tmp_dir) / "classes.dex").rename(dst_dex)
+
+ # Merge all the dex files.
+ # Skip non-existing files, but at least 1 file must exist.
+ def make_dexmerge(dst_dex: Path, *src_dexs: Path):
+ # Include destination. Skip any non-existing files.
+ srcs = [f for f in [dst_dex] + list(src_dexs) if f.exists()]
+
+ # NB: We merge even if there is just single input.
+ # It is useful to normalize non-deterministic smali output.
+ tmp_dir = self.test_dir / "dexmerge"
+ tmp_dir.mkdir()
+ self.d8(["--min-api", str(api_level), "--output", tmp_dir] + srcs)
+ assert not (tmp_dir / "classes2.dex").exists()
+ for src_file in srcs:
+ src_file.unlink()
+ (tmp_dir / "classes.dex").rename(dst_dex)
+ tmp_dir.rmdir()
+
+
+ def make_hiddenapi(*dex_files: Path):
+ if not use_hiddenapi or not Path("hiddenapi-flags.csv").exists():
+ return # Nothing to do.
+ args: List[Union[str, Path]] = ["encode"]
+ for dex_file in dex_files:
+ args.extend(["--input-dex=" + str(dex_file), "--output-dex=" + str(dex_file)])
+ args.append("--api-flags=hiddenapi-flags.csv")
+ args.append("--no-force-assign-all")
+ self.hiddenapi(args)
+
+
+ if Path("classes.dex").exists():
+ zip(Path(self.test_name + ".jar"), Path("classes.dex"))
+ return
+
+ if Path("classes.dm").exists():
+ zip(Path(self.test_name + ".jar"), Path("classes.dm"))
+ return
+
+ if make_jasmin(Path("jasmin_classes"), Path("jasmin")):
+ javac_classpath.append(Path("jasmin_classes"))
+
+ if make_jasmin(Path("jasmin_classes2"), Path("jasmin-multidex")):
+ javac_classpath.append(Path("jasmin_classes2"))
+
+ # To allow circular references, compile src/, src-multidex/, src-aotex/,
+ # src-bcpex/, src-ex/ together and pass the output as class path argument.
+ # Replacement sources in src-art/, src2/ and src-ex2/ can replace symbols
+ # used by the other src-* sources we compile here but everything needed to
+ # compile the other src-* sources should be present in src/ (and jasmin*/).
+ extra_srcs = ["src-multidex", "src-aotex", "src-bcpex", "src-ex"]
+ replacement_srcs = ["src2", "src-ex2"] + ([] if self.jvm else ["src-art"])
+ if (Path("src").exists() and
+ any(Path(p).exists() for p in extra_srcs + replacement_srcs)):
+ make_java(Path("classes-tmp-all"), Path("src"), *map(Path, extra_srcs))
+ javac_classpath.append(Path("classes-tmp-all"))
+
+ if make_java(Path("classes-aotex"), Path("src-aotex")) and need_dex:
+ make_dex(Path("classes-aotex"))
+ # rename it so it shows up as "classes.dex" in the zip file.
+ Path("classes-aotex.dex").rename(Path("classes.dex"))
+ zip(Path(self.test_name + "-aotex.jar"), Path("classes.dex"))
+
+ if make_java(Path("classes-bcpex"), Path("src-bcpex")) and need_dex:
+ make_dex(Path("classes-bcpex"))
+ # rename it so it shows up as "classes.dex" in the zip file.
+ Path("classes-bcpex.dex").rename(Path("classes.dex"))
+ zip(Path(self.test_name + "-bcpex.jar"), Path("classes.dex"))
+
+ make_java(Path("classes"), Path("src"))
+
+ if not self.jvm:
+ # Do not attempt to build src-art directories on jvm,
+ # since it would fail without libcore.
+ make_java(Path("classes"), Path("src-art"))
+
+ if make_java(Path("classes2"), Path("src-multidex")) and need_dex:
+ make_dex(Path("classes2"))
+
+ make_java(Path("classes"), Path("src2"))
+
+ # If the classes directory is not-empty, package classes in a DEX file.
+ # NB: some tests provide classes rather than java files.
+ if any(Path("classes").glob("*")) and need_dex:
+ make_dex(Path("classes"))
+
+ if Path("jasmin_classes").exists():
+ # Compile Jasmin classes as if they were part of the classes.dex file.
+ if need_dex:
+ make_dex(Path("jasmin_classes"))
+ make_dexmerge(Path("classes.dex"), Path("jasmin_classes.dex"))
+ else:
+ # Move jasmin classes into classes directory so that they are picked up
+ # with -cp classes.
+ Path("classes").mkdir(exist_ok=True)
+ copytree(Path("jasmin_classes"), Path("classes"), dirs_exist_ok=True)
+
+ if need_dex and make_smali(Path("smali_classes.dex"), Path("smali")):
+ # Merge smali files into classes.dex,
+ # this takes priority over any jasmin files.
+ make_dexmerge(Path("classes.dex"), Path("smali_classes.dex"))
+
+ # Compile Jasmin classes in jasmin-multidex as if they were part of
+ # the classes2.jar
+ if Path("jasmin-multidex").exists():
+ if need_dex:
+ make_dex(Path("jasmin_classes2"))
+ make_dexmerge(Path("classes2.dex"), Path("jasmin_classes2.dex"))
+ else:
+ # Move jasmin classes into classes2 directory so that
+ # they are picked up with -cp classes2.
+ Path("classes2").mkdir()
+ copytree(Path("jasmin_classes2"), Path("classes2"), dirs_exist_ok=True)
+ rmtree(Path("jasmin_classes2"))
+
+ if need_dex and make_smali(Path("smali_classes2.dex"), Path("smali-multidex")):
+ # Merge smali_classes2.dex into classes2.dex
+ make_dexmerge(Path("classes2.dex"), Path("smali_classes2.dex"))
+
+ make_java(Path("classes-ex"), Path("src-ex"))
+
+ make_java(Path("classes-ex"), Path("src-ex2"))
+
+ if Path("classes-ex").exists() and need_dex:
+ make_dex(Path("classes-ex"))
+
+ if need_dex and make_smali(Path("smali_classes-ex.dex"), Path("smali-ex")):
+ # Merge smali files into classes-ex.dex.
+ make_dexmerge(Path("classes-ex.dex"), Path("smali_classes-ex.dex"))
+
+ if Path("classes-ex.dex").exists():
+ # Apply hiddenapi on the dex files if the test has API list file(s).
+ make_hiddenapi(Path("classes-ex.dex"))
+
+ # quick shuffle so that the stored name is "classes.dex"
+ Path("classes.dex").rename(Path("classes-1.dex"))
+ Path("classes-ex.dex").rename(Path("classes.dex"))
+ zip(Path(self.test_name + "-ex.jar"), Path("classes.dex"))
+ Path("classes.dex").rename(Path("classes-ex.dex"))
+ Path("classes-1.dex").rename(Path("classes.dex"))
+
+ # Apply hiddenapi on the dex files if the test has API list file(s).
+ if need_dex:
+ if any(Path(".").glob("*-multidex")):
+ make_hiddenapi(Path("classes.dex"), Path("classes2.dex"))
+ else:
+ make_hiddenapi(Path("classes.dex"))
+
+ # Create a single dex jar with two dex files for multidex.
+ if need_dex:
+ if Path("classes2.dex").exists():
+ zip(Path(self.test_name + ".jar"), Path("classes.dex"), Path("classes2.dex"))
+ else:
+ zip(Path(self.test_name + ".jar"), Path("classes.dex"))
+
+
+# If we build just individual shard, we want to split the work among all the cores,
+# but if the build system builds all shards, we don't want to overload the machine.
+# We don't know which situation we are in, so as simple work-around, we use a lock
+# file to allow only one shard to use multiprocessing at the same time.
+def use_multiprocessing(mode: str) -> bool:
+ if "RBE_server_address" in os.environ:
+ return True
+ global lock_file
+ lock_path = Path(environ["TMPDIR"]) / ("art-test-run-test-build-py-" + mode)
+ lock_file = open(lock_path, "w")
+ try:
+ lockf(lock_file, LOCK_EX | LOCK_NB)
+ return True # We are the only instance of this script in the build system.
+ except BlockingIOError:
+ return False # Some other instance is already running.
+
+
+def main() -> None:
+ parser = ArgumentParser(description=__doc__)
+ parser.add_argument("--out", type=Path, help="Final zip file")
+ parser.add_argument("--mode", choices=["host", "jvm", "target"])
+ parser.add_argument("--bootclasspath", type=Path)
+ parser.add_argument("--d8", type=Path)
+ parser.add_argument("--hiddenapi", type=Path)
+ parser.add_argument("--jasmin", type=Path)
+ parser.add_argument("--smali", type=Path)
+ parser.add_argument("--soong_zip", type=Path)
+ parser.add_argument("--zipalign", type=Path)
+ parser.add_argument("srcs", nargs="+", type=Path)
+ args = parser.parse_args()
+
+ android_build_top = Path(getcwd()).absolute()
+ ziproot = args.out.absolute().parent / "zip"
+ srcdirs = set(s.parents[-4].absolute() for s in args.srcs)
+
+ # Special hidden-api shard: If the --hiddenapi flag is provided, build only
+ # hiddenapi tests. Otherwise exclude all hiddenapi tests from normal shards.
+ def filter_by_hiddenapi(srcdir: Path) -> bool:
+ return (args.hiddenapi != None) == ("hiddenapi" in srcdir.name)
+
+ # Initialize the test objects.
+ # We need to do this before we change the working directory below.
+ tests: List[BuildTestContext] = []
+ for srcdir in filter(filter_by_hiddenapi, srcdirs):
+ dstdir = ziproot / args.mode / srcdir.name
+ copytree(srcdir, dstdir)
+ tests.append(BuildTestContext(args, android_build_top, dstdir))
+
+ # We can not change the working directory per each thread since they all run in parallel.
+ # Create invalid read-only directory to catch accidental use of current working directory.
+ with TemporaryDirectory("-do-not-use-cwd") as invalid_tmpdir:
+ os.chdir(invalid_tmpdir)
+ os.chmod(invalid_tmpdir, 0)
+ with ThreadPoolExecutor(cpu_count() if use_multiprocessing(args.mode) else 1) as pool:
+ jobs = {}
+ for ctx in tests:
+ jobs[ctx.test_name] = pool.submit(ctx.build)
+ for test_name, job in jobs.items():
+ try:
+ job.result()
+ except Exception as e:
+ raise Exception("Failed to build " + test_name) from e
+
+ # Create the final zip file which contains the content of the temporary directory.
+ proc = run([android_build_top / args.soong_zip, "-o", android_build_top / args.out,
+ "-C", ziproot, "-D", ziproot], check=True)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/testrunner/device_config.py b/test/testrunner/device_config.py
index 1fad7d2..c56a05d 100644
--- a/test/testrunner/device_config.py
+++ b/test/testrunner/device_config.py
@@ -11,10 +11,11 @@
#
##########################################
# Fugu's don't have enough memory to support a 128m heap with normal concurrency.
+ # Also update timeout value as some tests can go beyond the default 600s.
'aosp_fugu' : {
- 'run-test-args': [ "--runtime-option", "-Xmx128m" ],
+ 'run-test-args': [ "--runtime-option", "-Xmx128m", "--timeout", "900" ],
},
'fugu' : {
- 'run-test-args': [ "--runtime-option", "-Xmx128m" ],
+ 'run-test-args': [ "--runtime-option", "-Xmx128m", "--timeout", "900" ],
},
}
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 319e1a7..44d0db6 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -132,8 +132,8 @@
HOST_OUT_EXECUTABLES = os.path.join(ANDROID_BUILD_TOP,
_get_build_var("HOST_OUT_EXECUTABLES"))
-# Set up default values for $DX, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path.
-for tool in ['dx', 'smali', 'jasmin', 'd8']:
+# Set up default values for $D8, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path.
+for tool in ['smali', 'jasmin', 'd8']:
os.environ.setdefault(tool.upper(), HOST_OUT_EXECUTABLES + '/' + tool)
ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP,
@@ -146,3 +146,6 @@
SOONG_OUT_DIR = _get_build_var('SOONG_OUT_DIR')
ART_TEST_RUN_ON_ARM_FVP = _getEnvBoolean('ART_TEST_RUN_ON_ARM_FVP', False)
+
+ART_TEST_ON_VM = _env.get('ART_TEST_ON_VM')
+ART_SSH_CMD = _env.get('ART_SSH_CMD')
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 4191771..d271b80 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -29,12 +29,18 @@
import argparse
import os
import pathlib
+import re
import subprocess
import sys
from target_config import target_config
import env
+# Check that we are using reasonably recent version of python
+print("Using", sys.executable, sys.version, flush=True)
+version = tuple(map(int, re.match(r"(\d*)\.(\d*)", sys.version).groups()))
+assert (version >= (3, 9)), "Python version is too old"
+
parser = argparse.ArgumentParser()
parser.add_argument('-j', default='1', dest='n_threads')
# either -l/--list OR build-target is required (but not both).
@@ -60,6 +66,7 @@
n_threads = options.n_threads
custom_env = target.get('env', {})
custom_env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true'
+custom_env['BUILD_BROKEN_DISABLE_BAZEL'] = 'true'
# Switch the build system to unbundled mode in the reduced manifest branch.
if not os.path.isdir(env.ANDROID_BUILD_TOP + '/frameworks/base'):
custom_env['TARGET_BUILD_UNBUNDLED'] = 'true'
@@ -81,7 +88,7 @@
if 'build' in target:
build_command = target.get('build').format(
ANDROID_BUILD_TOP = env.ANDROID_BUILD_TOP,
- MAKE_OPTIONS='DX= -j{threads}'.format(threads = n_threads))
+ MAKE_OPTIONS='D8= -j{threads}'.format(threads = n_threads))
sys.stdout.write(str(build_command) + '\n')
sys.stdout.flush()
if subprocess.call(build_command.split()):
@@ -90,7 +97,7 @@
# make runs soong/kati to build the target listed in the entry.
if 'make' in target:
build_command = 'build/soong/soong_ui.bash --make-mode'
- build_command += ' DX='
+ build_command += ' D8='
build_command += ' -j' + str(n_threads)
build_command += ' ' + target.get('make')
if env.DIST_DIR:
@@ -119,7 +126,8 @@
sys.exit(1)
if 'run-test' in target:
- run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
+ run_test_command = [sys.executable, # Use the same python as we are using now.
+ os.path.join(env.ANDROID_BUILD_TOP,
'art/test/testrunner/testrunner.py')]
test_flags = target.get('run-test', [])
out_dir = pathlib.PurePath(env.SOONG_OUT_DIR)
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 907f4ec..eaf33b7 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -138,7 +138,8 @@
}
},
'art-tracing' : {
- 'run-test' : ['--trace']
+ 'run-test' : ['--trace',
+ '--stream']
},
'art-interpreter-tracing' : {
'run-test' : ['--interpreter',
@@ -246,6 +247,20 @@
'ASAN_OPTIONS' : 'detect_leaks=0'
}
},
+ 'art-gtest-asan32': {
+ 'make' : 'test-art-host-gtest32',
+ 'env': {
+ 'SANITIZE_HOST' : 'address',
+ 'ASAN_OPTIONS' : 'detect_leaks=0'
+ }
+ },
+ 'art-gtest-asan64': {
+ 'make' : 'test-art-host-gtest64',
+ 'env': {
+ 'SANITIZE_HOST' : 'address',
+ 'ASAN_OPTIONS' : 'detect_leaks=0'
+ }
+ },
'art-asan': {
'run-test' : ['--interpreter',
'--interp-ac',
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 935ce0c..4499ffd 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -354,8 +354,13 @@
def get_device_name():
"""
- Gets the value of ro.product.name from remote device.
+ Gets the value of ro.product.name from remote device (unless running on a VM).
"""
+ if env.ART_TEST_ON_VM:
+ return subprocess.Popen(f"{env.ART_SSH_CMD} uname -a".split(),
+ stdout = subprocess.PIPE,
+ universal_newlines=True).stdout.read().strip()
+
proc = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.product.name'],
stderr=subprocess.STDOUT,
stdout = subprocess.PIPE,
@@ -581,7 +586,9 @@
temp_path = tempfile.mkdtemp(dir=env.ART_HOST_TEST_DIR)
options_test = '--temp-path {} '.format(temp_path) + options_test
- run_test_sh = env.ANDROID_BUILD_TOP + '/art/test/run-test'
+ # Run the run-test script using the prebuilt python.
+ python3_bin = env.ANDROID_BUILD_TOP + "/prebuilts/build-tools/path/linux-x86/python3"
+ run_test_sh = python3_bin + ' ' + env.ANDROID_BUILD_TOP + '/art/test/run-test'
command = ' '.join((run_test_sh, options_test, ' '.join(extra_arguments[target]), test))
return executor.submit(run_test, command, test, variant_set, test_name)
@@ -665,9 +672,12 @@
test_start_time = time.monotonic()
if verbose:
print_text("Starting %s at %s\n" % (test_name, test_start_time))
+ env = dict(os.environ)
+ env["FULL_TEST_NAME"] = test_name
if gdb or gdb_dex2oat:
proc = _popen(
args=command.split(),
+ env=env,
stderr=subprocess.STDOUT,
universal_newlines=True,
start_new_session=True
@@ -675,6 +685,7 @@
else:
proc = _popen(
args=command.split(),
+ env=env,
stderr=subprocess.STDOUT,
stdout = subprocess.PIPE,
universal_newlines=True,
@@ -704,7 +715,7 @@
failed_tests.append((test_name, 'Timed out in %d seconds' % timeout))
# HACK(b/142039427): Print extra backtraces on timeout.
- if "-target-" in test_name:
+ if "-target-" in test_name and not env.ART_TEST_ON_VM:
for i in range(8):
proc_name = "dalvikvm" + test_name[-2:]
pidof = subprocess.run(["adb", "shell", "pidof", proc_name], stdout=subprocess.PIPE)
@@ -1064,8 +1075,11 @@
def get_target_cpu_count():
- adb_command = 'adb shell cat /sys/devices/system/cpu/present'
- cpu_info_proc = subprocess.Popen(adb_command.split(), stdout=subprocess.PIPE)
+ if env.ART_TEST_ON_VM:
+ command = f"{env.ART_SSH_CMD} cat /sys/devices/system/cpu/present"
+ else:
+ command = 'adb shell cat /sys/devices/system/cpu/present'
+ cpu_info_proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
cpu_info = cpu_info_proc.stdout.read()
if type(cpu_info) is bytes:
cpu_info = cpu_info.decode('utf-8')
@@ -1104,7 +1118,9 @@
global csv_result
parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
- parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)')
+ parser.add_argument('tests', action='extend', nargs="*", help='name(s) of the test(s)')
+ parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)'
+ ' (deprecated: use positional arguments at the end without any option instead)')
global_group = parser.add_argument_group('Global options',
'Options that affect all tests being run')
global_group.add_argument('-j', type=int, dest='n_thread', help="""Number of CPUs to use.
@@ -1225,26 +1241,31 @@
if options['run_all']:
run_all_configs = True
- return tests
+ return tests or RUN_TEST_SET
def main():
gather_test_info()
- user_requested_tests = parse_option()
+ tests = parse_option()
setup_test_env()
gather_disabled_test_info()
if build:
- build_targets = ''
- if 'host' in _user_input_variants['target']:
- build_targets += 'test-art-host-run-test-dependencies '
- if 'target' in _user_input_variants['target']:
- build_targets += 'test-art-target-run-test-dependencies '
- if 'jvm' in _user_input_variants['target']:
- build_targets += 'test-art-host-run-test-dependencies '
+ build_targets = []
+ # Build only the needed shards (depending on the selected tests).
+ shards = set(re.search("(\d\d)-", t).group(1) for t in tests)
+ if any("hiddenapi" in t for t in tests):
+ shards.add("HiddenApi") # Include special HiddenApi shard.
+ for mode in ['host', 'target', 'jvm']:
+ if mode in _user_input_variants['target']:
+ build_targets += ['test-art-{}-run-test-dependencies'.format(mode)]
+ if len(shards) >= 100:
+ build_targets += ["art-run-test-{}-data".format(mode)] # Build all.
+ else:
+ build_targets += ["art-run-test-{}-data-shard{}".format(mode, s) for s in shards]
build_command = env.ANDROID_BUILD_TOP + '/build/soong/soong_ui.bash --make-mode'
- build_command += ' DX='
+ build_command += ' D8='
if dist:
build_command += ' dist'
- build_command += ' ' + build_targets
+ build_command += ' ' + ' '.join(build_targets)
print_text('Build command: %s\n' % build_command)
if subprocess.call(build_command.split()):
# Debugging for b/62653020
@@ -1252,10 +1273,7 @@
shutil.copyfile(env.SOONG_OUT_DIR + '/build.ninja', env.DIST_DIR + '/soong.ninja')
sys.exit(1)
- if user_requested_tests:
- run_tests(user_requested_tests)
- else:
- run_tests(RUN_TEST_SET)
+ run_tests(tests)
print_analysis()
close_csv_file()
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index cc83ad3..ff8b3a8 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -29,6 +29,7 @@
#include "909-attach-agent/attach.h"
#include "936-search-onload/search_onload.h"
#include "1919-vminit-thread-start-timing/vminit.h"
+#include "993-breakpoints-non-debuggable/onload.h"
namespace art {
@@ -82,6 +83,7 @@
{ "939-hello-transformation-bcp", common_redefine::OnLoad, nullptr },
{ "941-recursive-obsolete-jit", common_redefine::OnLoad, nullptr },
{ "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
+ { "993-non-debuggable", nullptr, Test993BreakpointsNonDebuggable::OnLoad },
{ "1919-vminit-thread-start-timing", Test1919VMInitThreadStart::OnLoad, nullptr },
{ "2031-zygote-compiled-frame-deopt", nullptr, MinimalOnLoad },
{ "2039-load-transform-larger", common_retransform::OnLoad, nullptr },
diff --git a/test/ti-agent/jni_helper.h b/test/ti-agent/jni_helper.h
index 0cbc634..f99e627 100644
--- a/test/ti-agent/jni_helper.h
+++ b/test/ti-agent/jni_helper.h
@@ -61,7 +61,7 @@
ScopedLocalRef<jclass> exc_class(env, env->FindClass("java/lang/NullPointerException"));
if (exc_class.get() == nullptr) {
- return -1;
+ return false;
}
return env->ThrowNew(exc_class.get(), msg) == JNI_OK;
diff --git a/test/ti-agent/redefinition_helper.cc b/test/ti-agent/redefinition_helper.cc
index 0baa9fe..706531e 100644
--- a/test/ti-agent/redefinition_helper.cc
+++ b/test/ti-agent/redefinition_helper.cc
@@ -392,6 +392,7 @@
static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
std::vector<jclass> classes;
jint len = env->GetArrayLength(targets);
+ classes.reserve(len);
for (jint i = 0; i < len; i++) {
classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
}
diff --git a/test/ti-agent/scoped_utf_chars.h b/test/ti-agent/scoped_utf_chars.h
index 422caaf..ddf1bd5 100644
--- a/test/ti-agent/scoped_utf_chars.h
+++ b/test/ti-agent/scoped_utf_chars.h
@@ -38,7 +38,7 @@
}
}
- ScopedUtfChars(ScopedUtfChars&& rhs) :
+ ScopedUtfChars(ScopedUtfChars&& rhs) noexcept :
env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
rhs.env_ = nullptr;
rhs.string_ = nullptr;
@@ -51,7 +51,7 @@
}
}
- ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
+ ScopedUtfChars& operator=(ScopedUtfChars&& rhs) noexcept {
if (this != &rhs) {
// Delete the currently owned UTF chars.
this->~ScopedUtfChars();
diff --git a/test/ti-agent/suspension_helper.cc b/test/ti-agent/suspension_helper.cc
index b685cb2..2b1ab67 100644
--- a/test/ti-agent/suspension_helper.cc
+++ b/test/ti-agent/suspension_helper.cc
@@ -37,6 +37,7 @@
static std::vector<jthread> CopyToVector(JNIEnv* env, jobjectArray thrs) {
jsize len = env->GetArrayLength(thrs);
std::vector<jthread> ret;
+ ret.reserve(len);
for (jsize i = 0; i < len; i++) {
ret.push_back(reinterpret_cast<jthread>(env->GetObjectArrayElement(thrs, i)));
}
diff --git a/test/ti-agent/ti_macros.h b/test/ti-agent/ti_macros.h
index a871270..abd54e0 100644
--- a/test/ti-agent/ti_macros.h
+++ b/test/ti-agent/ti_macros.h
@@ -21,4 +21,10 @@
#define UNREACHABLE __builtin_unreachable
+#ifndef NDEBUG
+#define ALWAYS_INLINE
+#else
+#define ALWAYS_INLINE __attribute__ ((always_inline))
+#endif
+
#endif // ART_TEST_TI_AGENT_TI_MACROS_H_
diff --git a/test/ti-agent/ti_utf.h b/test/ti-agent/ti_utf.h
index 341e106..15fe22c 100644
--- a/test/ti-agent/ti_utf.h
+++ b/test/ti-agent/ti_utf.h
@@ -21,6 +21,7 @@
#include <string.h>
#include "android-base/logging.h"
+#include "ti_macros.h"
namespace art {
namespace ti {
@@ -104,6 +105,56 @@
return surrogate_pair;
}
+// Note: This is a copy of the code in `libdexfile`.
+template <bool kUseShortZero, bool kUse4ByteSequence, bool kReplaceBadSurrogates, typename Append>
+inline void ConvertUtf16ToUtf8(const uint16_t* utf16, size_t char_count, Append&& append) {
+ static_assert(kUse4ByteSequence || !kReplaceBadSurrogates);
+
+ // Use local helpers instead of macros from `libicu` to avoid the dependency on `libicu`.
+ auto is_lead = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0xfc00u) == 0xd800u; };
+ auto is_trail = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0xfc00u) == 0xdc00u; };
+ auto is_surrogate = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0xf800u) == 0xd800u; };
+ auto is_surrogate_lead = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0x0400u) == 0u; };
+ auto get_supplementary = [](uint16_t lead, uint16_t trail) ALWAYS_INLINE {
+ constexpr uint32_t offset = (0xd800u << 10) + 0xdc00u - 0x10000u;
+ return (static_cast<uint32_t>(lead) << 10) + static_cast<uint32_t>(trail) - offset;
+ };
+
+ for (size_t i = 0u; i < char_count; ++i) {
+ auto has_trail = [&]() { return i + 1u != char_count && is_trail(utf16[i + 1u]); };
+
+ uint16_t ch = utf16[i];
+ if (ch < 0x80u && (kUseShortZero || ch != 0u)) {
+ // One byte.
+ append(ch);
+ } else if (ch < 0x800u) {
+ // Two bytes.
+ append((ch >> 6) | 0xc0);
+ append((ch & 0x3f) | 0x80);
+ } else if (kReplaceBadSurrogates
+ ? is_surrogate(ch)
+ : kUse4ByteSequence && is_lead(ch) && has_trail()) {
+ if (kReplaceBadSurrogates && (!is_surrogate_lead(ch) || !has_trail())) {
+ append('?');
+ } else {
+ // We have a *valid* surrogate pair.
+ uint32_t code_point = get_supplementary(ch, utf16[i + 1u]);
+ ++i; // Consume the leading surrogate.
+ // Four bytes.
+ append((code_point >> 18) | 0xf0);
+ append(((code_point >> 12) & 0x3f) | 0x80);
+ append(((code_point >> 6) & 0x3f) | 0x80);
+ append((code_point & 0x3f) | 0x80);
+ }
+ } else {
+ // Three bytes.
+ append((ch >> 12) | 0xe0);
+ append(((ch >> 6) & 0x3f) | 0x80);
+ append((ch & 0x3f) | 0x80);
+ }
+ }
+}
+
inline void ConvertUtf16ToModifiedUtf8(char* utf8_out,
size_t byte_count,
const uint16_t* utf16_in,
@@ -118,75 +169,20 @@
}
// String contains non-ASCII characters.
- while (char_count--) {
- const uint16_t ch = *utf16_in++;
- if (ch > 0 && ch <= 0x7f) {
- *utf8_out++ = ch;
- } else {
- // Char_count == 0 here implies we've encountered an unpaired
- // surrogate and we have no choice but to encode it as 3-byte UTF
- // sequence. Note that unpaired surrogates can occur as a part of
- // "normal" operation.
- if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
- const uint16_t ch2 = *utf16_in;
-
- // Check if the other half of the pair is within the expected
- // range. If it isn't, we will have to emit both "halves" as
- // separate 3 byte sequences.
- if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
- utf16_in++;
- char_count--;
- const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
- *utf8_out++ = (code_point >> 18) | 0xf0;
- *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
- *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (code_point & 0x3f) | 0x80;
- continue;
- }
- }
-
- if (ch > 0x07ff) {
- // Three byte encoding.
- *utf8_out++ = (ch >> 12) | 0xe0;
- *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- } else /*(ch > 0x7f || ch == 0)*/ {
- // Two byte encoding.
- *utf8_out++ = (ch >> 6) | 0xc0;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- }
- }
- }
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
+ auto append = [&](char c) { *utf8_out++ = c; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(utf16_in, char_count, append);
}
-inline size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+inline size_t CountModifiedUtf8BytesInUtf16(const uint16_t* chars, size_t char_count) {
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
size_t result = 0;
- const uint16_t *end = chars + char_count;
- while (chars < end) {
- const uint16_t ch = *chars++;
- if (LIKELY(ch != 0 && ch < 0x80)) {
- result++;
- continue;
- }
- if (ch < 0x800) {
- result += 2;
- continue;
- }
- if (ch >= 0xd800 && ch < 0xdc00) {
- if (chars < end) {
- const uint16_t ch2 = *chars;
- // If we find a properly paired surrogate, we emit it as a 4 byte
- // UTF sequence. If we find an unpaired leading or trailing surrogate,
- // we emit it as a 3 byte sequence like would have done earlier.
- if (ch2 >= 0xdc00 && ch2 < 0xe000) {
- chars++;
- result += 4;
- continue;
- }
- }
- }
- result += 3;
- }
+ auto append = [&](char c ATTRIBUTE_UNUSED) { ++result; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(chars, char_count, append);
return result;
}
diff --git a/test/update-rollback/Android.bp b/test/update-rollback/Android.bp
index 3713328..ad14988 100644
--- a/test/update-rollback/Android.bp
+++ b/test/update-rollback/Android.bp
@@ -23,5 +23,9 @@
libs: ["tradefed"],
static_libs: ["cts-install-lib-host"],
data: [":test_broken_com.android.art"],
- test_suites: ["general-tests"],
+ // Add this test to `device-tests` rather than `general-tests` to ensure
+ // that the type of ART APEX -- public (`com.android.art`) or internal
+ // (`com.google.android.art`) -- used in the test matches the one bundled
+ // with the Android platform used in the device-under-test.
+ test_suites: ["device-tests"],
}
diff --git a/test/utils/get-device-isa b/test/utils/get-device-isa
deleted file mode 100755
index 5f9c2a4..0000000
--- a/test/utils/get-device-isa
+++ /dev/null
@@ -1,77 +0,0 @@
-#! /bin/bash
-#
-# Copyright 2019 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.
-
-usage() {
- cat >&2 <<EOF
-Determine and print the 32- or 64-bit architecture of a device.
-
-Usage:
- $0 --32 Select the 32-bit architecture
- $0 --64 Select the 64-bit architecture
-EOF
- exit 1
-}
-
-check_32bit() {
- if ! adb shell test -e /system/bin/linker; then
- echo >&2 "Device does not have 32-bit support"
- exit 1
- fi
-}
-
-if [[ $# -ne 1 ]]; then
- usage
-fi
-
-uname_m="$(adb shell uname -m)"
-
-case "$1" in
- (--32)
- case $uname_m in
- (armv*)
- echo arm
- ;;
- (i?86)
- echo x86
- ;;
- (aarch64)
- check_32bit
- echo arm
- ;;
- (x86_64)
- check_32bit
- echo x86
- ;;
- (*)
- echo >&2 "Unknown ISA: $uname_m"
- exit 1
- esac
- ;;
- (--64)
- case $uname_m in
- (aarch64)
- echo arm64
- ;;
- (x86_64)
- echo x86_64
- ;;
- (*)
- echo >&2 "Unknown ISA: $uname_m"
- exit 1
- esac
- ;;
- (*) usage;;
-esac
diff --git a/test/utils/get-device-test-native-lib-path b/test/utils/get-device-test-native-lib-path
deleted file mode 100755
index 21ea98c..0000000
--- a/test/utils/get-device-test-native-lib-path
+++ /dev/null
@@ -1,47 +0,0 @@
-#! /bin/bash
-#
-# Copyright 2019 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.
-
-usage() {
- cat >&2 <<EOF
-Determine the 32- or 64-bit architecture of a device and print the path to
-native libraries installed on the device for testing purposes.
-
-Usage:
- $0 --32 Select the 32-bit architecture
- $0 --64 Select the 64-bit architecture
-EOF
- exit 1
-}
-
-if [[ $# -ne 1 ]]; then
- usage
-fi
-
-case "$1" in
- (--32) TEST_DIRECTORY="nativetest";;
- (--64) TEST_DIRECTORY="nativetest64";;
- (*) usage;;
-esac
-
-if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- echo 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
-fi
-
-bitness_flag=$1
-ISA=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-isa" "$bitness_flag")
-
-echo "/data/${TEST_DIRECTORY}/art/${ISA}"
diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files
index ebe2520..2ab9317 100755
--- a/test/utils/regen-test-files
+++ b/test/utils/regen-test-files
@@ -94,14 +94,12 @@
"1004-checker-volatile-ref-load",
"133-static-invoke-super",
"1338-gc-no-los",
- "151-OpenFileLimit",
"159-app-image-fields",
"160-read-barrier-stress",
"163-app-image-methods",
"165-lock-owner-proxy",
"168-vmstack-annotated",
"176-app-image-string",
- "2232-write-metrics-to-log",
"304-method-tracing",
"628-vdex",
"643-checker-bogus-ic",
@@ -197,17 +195,35 @@
# at Main.main(Main.java:20)
#
"821-many-args",
+ # 823-cha-inlining: Dependency on `libarttest`.
+ "823-cha-inlining",
# 826-infinite-loop: The test expects an argument passed to `Main.main` (the test library,
# usually `arttestd` or `arttest)`, but the ART run-test TradeFed test runner
# (`com.android.tradefed.testtype.ArtRunTest`) does not implement this yet.
"826-infinite-loop",
# 832-cha-recursive: Dependency on `libarttest`.
"832-cha-recursive",
+ # 837-deopt: Dependency on `libarttest`.
+ "837-deopt",
+ # 844-exception: Dependency on `libarttest`.
+ "844-exception",
+ # 844-exception2: Dependency on `libarttest`.
+ "844-exception2",
+ # 966-default-conflict: Dependency on `libarttest`.
+ "966-default-conflict",
+ # 993-breakpoints-non-debuggable: This test needs native code.
+ "993-breakpoints-non-debuggable",
+])
+
+known_failing_on_hwasan_tests = frozenset([
+ "BootImageProfileTest", # b/232012605
+ "CtsJdwpTestCases", # times out
])
# ART gtests that do not need root access to the device.
art_gtest_user_module_names = [
"art_libnativebridge_cts_tests",
+ "art_standalone_artd_tests",
"art_standalone_cmdline_tests",
"art_standalone_compiler_tests",
"art_standalone_dex2oat_tests",
@@ -222,7 +238,6 @@
"art_standalone_libprofile_tests",
"art_standalone_oatdump_tests",
"art_standalone_odrefresh_tests",
- "art_standalone_runtime_compiler_tests",
"art_standalone_runtime_tests",
"art_standalone_sigchain_tests",
"libnativeloader_test",
@@ -230,9 +245,9 @@
# ART gtests that need root access to the device.
art_gtest_eng_only_module_names = [
- "libnativeloader_e2e_tests",
"art_standalone_dexoptanalyzer_tests",
"art_standalone_profman_tests",
+ "libnativeloader_e2e_tests",
]
# All supported ART gtests.
@@ -240,57 +255,53 @@
# ART gtests supported in MTS that do not need root access to the device.
art_gtest_mts_user_module_names = copy.copy(art_gtest_user_module_names)
-# Temporarily disable `art_standalone_odrefresh_tests` in MTS,
-# as it is currently failing in Mainline testing
-# (b/206335809); a fix is in the works but may take some time
-# to land.
-#
-# TODO(b/206335809): Re-enable this test when the fix has landed.
-art_gtest_mts_user_module_names.remove("art_standalone_odrefresh_tests")
# ART gtests supported in Mainline presubmits.
art_gtests_mainline_presubmit_module_names = copy.copy(art_gtest_module_names)
-# Temporarily disable `art_standalone_odrefresh_tests` in Mainline
-# presubmits, as it is currently failing in Mainline testing
-# (b/206335809); a fix is in the works but may take some time to
-# land.
-#
-# TODO(b/206335809): Re-enable this test when the fix has landed.
-art_gtests_mainline_presubmit_module_names.remove("art_standalone_odrefresh_tests")
# Tests exhibiting a flaky behavior, currently exluded from MTS for
# the stake of stability / confidence (b/209958457).
-flaky_tests_excluded_from_mts = [
- ("CtsLibcoreFileIOTestCases" +
- " android.cts.FileChannelInterProcessLockTest#" + m) for m in [
+flaky_tests_excluded_from_mts = {
+ "CtsLibcoreFileIOTestCases": [
+ ("android.cts.FileChannelInterProcessLockTest#" + m) for m in [
"test_lockJJZ_Exclusive_asyncChannel",
"test_lockJJZ_Exclusive_syncChannel",
"test_lock_differentChannelTypes",
"test_lockJJZ_Shared_asyncChannel",
"test_lockJJZ_Shared_syncChannel",
+ ]
+ ],
+ "CtsLibcoreTestCases": [
+ ("com.android.org.conscrypt.javax.net.ssl.SSLSocketVersionCompatibilityTest#" + m + c)
+ for (m, c) in itertools.product(
+ [
+ "test_SSLSocket_interrupt_read_withoutAutoClose",
+ "test_SSLSocket_setSoWriteTimeout",
+ ],
+ [
+ "[0: TLSv1.2 client, TLSv1.2 server]",
+ "[1: TLSv1.2 client, TLSv1.3 server]",
+ "[2: TLSv1.3 client, TLSv1.2 server]",
+ "[3: TLSv1.3 client, TLSv1.3 server]",
+ ]
+ )
+ ] + [
+ ("libcore.dalvik.system.DelegateLastClassLoaderTest#" + m) for m in [
+ "testLookupOrderNodelegate_getResource",
+ "testLookupOrder_getResource",
+ ]
]
-] + [
- ("CtsLibcoreTestCases" +
- " com.android.org.conscrypt.javax.net.ssl.SSLSocketVersionCompatibilityTest#" + m + c)
- for (m, c) in itertools.product(
- [
- "test_SSLSocket_interrupt_read_withoutAutoClose",
- "test_SSLSocket_setSoWriteTimeout",
- ],
- [
- "[0: TLSv1.2 client, TLSv1.2 server]",
- "[1: TLSv1.2 client, TLSv1.3 server]",
- "[2: TLSv1.3 client, TLSv1.2 server]",
- "[3: TLSv1.3 client, TLSv1.3 server]",
- ]
- )
-] + [
- ("CtsLibcoreTestCases" +
- " libcore.dalvik.system.DelegateLastClassLoaderTest#" + m) for m in [
- "testLookupOrderNodelegate_getResource",
- "testLookupOrder_getResource",
- ]
-]
+}
+
+# Tests failing because of linking issues, currently exluded from MTS
+# and Mainline Presubmits to minimize noise in continuous runs while
+# we investigate.
+#
+# TODO(b/247108425): Address the linking issues and re-enable these
+# tests.
+failing_tests_excluded_from_mts_and_mainline_presubmits = {
+ "art_standalone_compiler_tests": ["JniCompilerTest*"],
+}
# Is `run_test` a Checker test (i.e. a test containing Checker
# assertions)?
@@ -316,37 +327,37 @@
for run_test in os.listdir(self.art_test_dir)
if re.match("^[0-9]{3,}-", run_test)])
- # Read build file (Bash script) and return a canonized version of it
- # (without comments, blank lines, "debugging" statements, etc.).
- def canonize_build_script(self, build_file):
+ # Return the metadata of a test, if any.
+ def get_test_metadata(self, run_test):
+ run_test_path = os.path.join(self.art_test_dir, run_test)
+ metadata_file = os.path.join(run_test_path, "test-metadata.json")
+ metadata = {}
+ if os.path.exists(metadata_file):
+ with open(metadata_file, "r") as f:
+ try:
+ metadata = json.load(f)
+ except json.decoder.JSONDecodeError:
+ logging.error(f"Unable to parse test metadata file `{metadata_file}`")
+ raise
+ return metadata
- def is_comment(line):
- return re.match("^\\s*#", line)
-
- def is_blank(line):
- return re.match("^\\s*$", line)
-
- # Is `line` a `set -e` statement?
- def is_set_e(line):
- return re.match("^\\s*set -e\\s*", line)
-
- # Should `line` be kept in the canonized build script?
- def keep_line(line):
- return not (is_comment(line) or is_blank(line) or is_set_e(line))
-
- with open(build_file, "r") as f:
- lines = f.readlines()
- return list(filter(keep_line, lines))
-
- # Can the build script in `build_file` be safely ignored?
- def can_ignore_build_script(self, build_file):
- build_script = self.canonize_build_script(build_file)
- if len(build_script) == 1:
- if build_script[0] == "./default-build \"$@\" --experimental var-handles\n":
- # Soong builds JARs with VarHandle support by default (i.e. by
- # using an API level greater or equal to 28), so we can ignore
- # build scripts that just request support for this feature.
- return True
+ # Can the build script of `run_test` be safely ignored?
+ def can_ignore_build_script(self, run_test):
+ # Check whether there are test metadata with build parameters
+ # enabling us to safely ignore the build script.
+ metadata = self.get_test_metadata(run_test)
+ build_param = metadata.get("build-param", {})
+ # Ignore build scripts that are just about preventing building for
+ # the JVM and/or using VarHandles (Soong builds JARs with
+ # VarHandle support by default (i.e. by using an API level greater
+ # or equal to 28), so we can ignore build scripts that just
+ # request support for this feature.)
+ experimental_var_handles = {"experimental": "var-handles"}
+ jvm_supported_false = {"jvm-supported": "false"}
+ if (build_param == experimental_var_handles or
+ build_param == jvm_supported_false or
+ build_param == experimental_var_handles | jvm_supported_false):
+ return True
return False
# Is building `run_test` supported?
@@ -356,8 +367,11 @@
# Skip tests with non-default build rules, unless these build
# rules can be safely ignored.
- if os.path.isfile(os.path.join(run_test_path, "build")):
- if not self.can_ignore_build_script(os.path.join(run_test_path, "build")):
+ if (os.path.isfile(os.path.join(run_test_path, "generate-sources")) or
+ os.path.isfile(os.path.join(run_test_path, "javac_post.sh"))):
+ return False
+ if os.path.isfile(os.path.join(run_test_path, "build.py")):
+ if not self.can_ignore_build_script(run_test):
return False
# Skip tests with sources outside the `src` directory.
for subdir in ["jasmin",
@@ -369,8 +383,7 @@
"src-bcpex",
"src-ex",
"src-ex2",
- "src-multidex",
- "src2"]:
+ "src-multidex"]:
if os.path.isdir(os.path.join(run_test_path, subdir)):
return False
# Skip tests that have both an `src` directory and an `src-art` directory.
@@ -390,19 +403,60 @@
# All other tests are considered buildable.
return True
- # Is (successfully) running `run_test` supported?
- # TODO(b/147812905): Add run-time support for more tests.
- def is_runnable(self, run_test):
- run_test_path = os.path.join(self.art_test_dir, run_test)
+ # Can the run script of `run_test` be safely ignored?
+ def can_ignore_run_script(self, run_test):
# Unconditionally consider some identified tests that have a
# (not-yet-handled) custom `run` script as runnable.
+ #
# TODO(rpl): Get rid of this exception mechanism by supporting
# these tests' `run` scripts properly.
if run_test in runnable_test_exceptions:
return True
- # Skip tests with a custom `run` script.
- if os.path.isfile(os.path.join(run_test_path, "run")):
- return False
+ # Check whether there are test metadata with run parameters
+ # enabling us to safely ignore the run script.
+ metadata = self.get_test_metadata(run_test)
+ run_param = metadata.get("run-param", {})
+ if run_param.get("default-run", ""):
+ return True
+ return False
+
+ def gen_libs_list_impl(self, library_type, libraries):
+ if len(libraries) == 0:
+ return ""
+ libraries_joined = """,
+ """.join(libraries)
+ return f"""
+ {library_type}: [
+ {libraries_joined}
+ ],"""
+
+ def gen_libs_list(self, libraries):
+ return self.gen_libs_list_impl("libs", libraries);
+
+ def gen_static_libs_list(self, libraries):
+ return self.gen_libs_list_impl("static_libs", libraries);
+
+ def gen_java_library_rule(self, name, src_dir, libraries):
+ return f"""\
+
+
+ // Library with {src_dir}/ sources for the test.
+ java_library {{
+ name: "{name}",
+ defaults: ["art-run-test-defaults"],{self.gen_libs_list(libraries)}
+ srcs: ["{src_dir}/**/*.java"],
+ }}"""
+
+ # Is (successfully) running `run_test` supported?
+ # TODO(b/147812905): Add run-time support for more tests.
+ def is_runnable(self, run_test):
+ run_test_path = os.path.join(self.art_test_dir, run_test)
+
+ # Skip tests with non-default run rules, unless these run rules
+ # can be safely ignored.
+ if os.path.isfile(os.path.join(run_test_path, "run.py")):
+ if not self.can_ignore_run_script(run_test):
+ return False
# Skip tests known to fail.
if run_test in known_failing_tests:
return False
@@ -430,11 +484,7 @@
bp_file = os.path.join(run_test_path, "Android.bp")
# Optional test metadata (JSON file).
- metadata_file = os.path.join(run_test_path, "test-metadata.json")
- metadata = {}
- if os.path.exists(metadata_file):
- with open(metadata_file, "r") as f:
- metadata = json.load(f)
+ metadata = self.get_test_metadata(run_test)
run_test_module_name = ART_RUN_TEST_MODULE_NAME_PREFIX + run_test
@@ -471,6 +521,16 @@
else:
source_dir = "src"
+ src_library_rules = []
+ test_libraries = []
+ if os.path.isdir(os.path.join(run_test_path, "src2")):
+ src_library_rules.append(self.gen_java_library_rule(
+ f"{run_test_module_name}-{source_dir}",
+ source_dir,
+ test_libraries))
+ test_libraries.append(f"\"{run_test_module_name}-src\"")
+ source_dir = "src2"
+
with open(bp_file, "w") as f:
logging.debug(f"Writing `{bp_file}`.")
f.write(textwrap.dedent(f"""\
@@ -485,14 +545,14 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
- }}
+ }}{''.join(src_library_rules)}
// Test's Dex code.
java_test {{
name: "{run_test_module_name}",
defaults: ["art-run-test-defaults"],
test_config_template: ":{test_config_template}",
- srcs: ["{source_dir}/**/*.java"],
+ srcs: ["{source_dir}/**/*.java"],{self.gen_static_libs_list(test_libraries)}
data: [
":{run_test_module_name}-expected-stdout",
":{run_test_module_name}-expected-stderr",
@@ -522,26 +582,36 @@
run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t for t in art_run_tests]
# Mainline presubmits.
+ mainline_presubmit_apex_suffix = "[com.google.android.art.apex]"
mainline_other_presubmit_tests = [
"ComposHostTestCases",
]
mainline_presubmit_tests = (mainline_other_presubmit_tests + run_test_module_names +
art_gtests_mainline_presubmit_module_names)
- mainline_presubmit_tests_with_apex = [t + "[com.google.android.art.apex]"
- for t
- in mainline_presubmit_tests]
- mainline_presubmit_tests_dict = [{"name": t} for t in mainline_presubmit_tests_with_apex]
+ mainline_presubmit_tests_dict = [
+ ({"name": t + mainline_presubmit_apex_suffix,
+ "options": [
+ {"exclude-filter": e}
+ for e in failing_tests_excluded_from_mts_and_mainline_presubmits[t]
+ ]}
+ if t in failing_tests_excluded_from_mts_and_mainline_presubmits
+ else {"name": t + mainline_presubmit_apex_suffix})
+ for t in mainline_presubmit_tests
+ ]
# Presubmits.
other_presubmit_tests = [
- "CtsJdwpTestCases",
- "BootImageProfileTest",
"ArtServiceTests",
+ "BootImageProfileTest",
"ComposHostTestCases",
+ "CtsJdwpTestCases",
+ "art-apex-update-rollback",
"art_standalone_dexpreopt_tests",
]
presubmit_tests = other_presubmit_tests + run_test_module_names + art_gtest_module_names
presubmit_tests_dict = [{"name": t} for t in presubmit_tests]
+ hwasan_presubmit_tests_dict = [{"name": t} for t in presubmit_tests
+ if t not in known_failing_on_hwasan_tests]
# Use an `OrderedDict` container to preserve the order in which items are inserted.
# Do not produce an entry for a test group if it is empty.
@@ -551,6 +621,7 @@
in [
("mainline-presubmit", mainline_presubmit_tests_dict),
("presubmit", presubmit_tests_dict),
+ ("hwasan-presubmit", hwasan_presubmit_tests_dict),
]
if test_group_dict
])
@@ -700,18 +771,25 @@
include.setAttribute("name", f"mts-art-tests-list-user-shard-{s:02}")
configuration.appendChild(include)
- # Excluded flaky tests.
- xml_comment = root.createComment(f" Excluded flaky tests (b/209958457). ")
- configuration.appendChild(xml_comment)
-
def append_test_exclusion(test):
option = root.createElement("option")
option.setAttribute("name", "compatibility:exclude-filter")
option.setAttribute("value", test)
configuration.appendChild(option)
- for t in flaky_tests_excluded_from_mts:
- append_test_exclusion(t)
+ # Excluded flaky tests.
+ xml_comment = root.createComment(" Excluded flaky tests (b/209958457). ")
+ configuration.appendChild(xml_comment)
+ for module in flaky_tests_excluded_from_mts:
+ for testcase in flaky_tests_excluded_from_mts[module]:
+ append_test_exclusion(f"{module} {testcase}")
+
+ # Excluded failing tests.
+ xml_comment = root.createComment(" Excluded failing tests (b/247108425). ")
+ configuration.appendChild(xml_comment)
+ for module in failing_tests_excluded_from_mts_and_mainline_presubmits:
+ for testcase in failing_tests_excluded_from_mts_and_mainline_presubmits[module]:
+ append_test_exclusion(f"{module} {testcase}")
xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
diff --git a/tools/Android.bp b/tools/Android.bp
index b7f5c1b..7ffa672 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -97,23 +97,3 @@
},
},
}
-
-python_binary_host {
- name: "art-run-test-checker",
- srcs: [
- "checker/**/*.py",
- ],
- main: "checker/checker.py",
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- },
- },
- test_suites: [
- "general-tests",
- "mts-art",
- ],
-}
diff --git a/tools/PresubmitJsonLinter.java b/tools/PresubmitJsonLinter.java
new file mode 100644
index 0000000..334d200
--- /dev/null
+++ b/tools/PresubmitJsonLinter.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 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 com.google.gson.stream.JsonReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Pre upload hook that ensures art-buildbot expectation files (files under //art/tools ending with
+ * "_failures.txt", e.g. //art/tools/libcore_failures.txt) are well-formed json files.
+ *
+ * It makes basic validation of the keys but does not cover all the cases. Parser structure is
+ * based on external/vogar/src/vogar/ExpectationStore.java.
+ *
+ * Hook is set up in //art/PREUPLOAD.cfg See also //tools/repohooks/README.md
+ */
+class PresubmitJsonLinter {
+
+ private static final int FLAGS = Pattern.MULTILINE | Pattern.DOTALL;
+ private static final Set<String> RESULTS = new HashSet<>();
+
+ static {
+ RESULTS.addAll(List.of(
+ "UNSUPPORTED",
+ "COMPILE_FAILED",
+ "EXEC_FAILED",
+ "EXEC_TIMEOUT",
+ "ERROR",
+ "SUCCESS"
+ ));
+ }
+
+ public static void main(String[] args) {
+ for (String arg : args) {
+ info("Checking " + arg);
+ checkExpectationFile(arg);
+ }
+ }
+
+ private static void info(String message) {
+ System.err.println(message);
+ }
+
+ private static void error(String message) {
+ System.err.println(message);
+ System.exit(1);
+ }
+
+ private static void checkExpectationFile(String arg) {
+ JsonReader reader;
+ try {
+ reader = new JsonReader(new FileReader(arg));
+ } catch (FileNotFoundException e) {
+ error("File '" + arg + "' is not found");
+ return;
+ }
+ reader.setLenient(true);
+ try {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ readExpectation(reader);
+ }
+ reader.endArray();
+ } catch (IOException e) {
+ error("Malformed json: " + reader);
+ }
+ }
+
+ private static void readExpectation(JsonReader reader) throws IOException {
+ Set<String> names = new LinkedHashSet<String>();
+ Set<String> tags = new LinkedHashSet<String>();
+ boolean readResult = false;
+ boolean readDescription = false;
+
+ reader.beginObject();
+ while (reader.hasNext()) {
+ String name = reader.nextName();
+ switch (name) {
+ case "result":
+ String result = reader.nextString();
+ if (!RESULTS.contains(result)) {
+ error("Invalid 'result' value: '" + result +
+ "'. Expected one of " + String.join(", ", RESULTS) +
+ ". " + reader);
+ }
+ readResult = true;
+ break;
+ case "substring": {
+ try {
+ Pattern.compile(
+ ".*" + Pattern.quote(reader.nextString()) + ".*", FLAGS);
+ } catch (PatternSyntaxException e) {
+ error("Malformed 'substring' value: " + reader);
+ }
+ }
+ case "pattern": {
+ try {
+ Pattern.compile(reader.nextString(), FLAGS);
+ } catch (PatternSyntaxException e) {
+ error("Malformed 'pattern' value: " + reader);
+ }
+ break;
+ }
+ case "failure":
+ names.add(reader.nextString());
+ break;
+ case "description":
+ reader.nextString();
+ readDescription = true;
+ break;
+ case "name":
+ names.add(reader.nextString());
+ break;
+ case "names":
+ readStrings(reader, names);
+ break;
+ case "tags":
+ readStrings(reader, tags);
+ break;
+ case "bug":
+ reader.nextLong();
+ break;
+ case "modes":
+ readModes(reader);
+ break;
+ case "modes_variants":
+ readModesAndVariants(reader);
+ break;
+ default:
+ error("Unknown key '" + name + "' in expectations file");
+ reader.skipValue();
+ break;
+ }
+ }
+ reader.endObject();
+
+ if (names.isEmpty()) {
+ error("Missing 'name' or 'failure' key in " + reader);
+ }
+ if (!readResult) {
+ error("Missing 'result' key in " + reader);
+ }
+ if (!readDescription) {
+ error("Missing 'description' key in " + reader);
+ }
+ }
+
+ private static void readStrings(JsonReader reader, Set<String> output) throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ output.add(reader.nextString());
+ }
+ reader.endArray();
+ }
+
+ private static void readModes(JsonReader reader) throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ reader.nextString();
+ }
+ reader.endArray();
+ }
+
+ /**
+ * Expected format: mode_variants: [["host", "X32"], ["host", "X64"]]
+ */
+ private static void readModesAndVariants(JsonReader reader) throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ reader.beginArray();
+ reader.nextString();
+ reader.nextString();
+ reader.endArray();
+ }
+ reader.endArray();
+ }
+}
\ No newline at end of file
diff --git a/tools/ahat/.clang-format b/tools/ahat/.clang-format
new file mode 120000
index 0000000..88ab38e
--- /dev/null
+++ b/tools/ahat/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-java-2
\ No newline at end of file
diff --git a/tools/ahat/Android.bp b/tools/ahat/Android.bp
index affa2e0..5f6ba81 100644
--- a/tools/ahat/Android.bp
+++ b/tools/ahat/Android.bp
@@ -27,7 +27,7 @@
visibility: [
"//libcore/metrictests/memory/host",
],
- wrapper: "ahat",
+ wrapper: "ahat.sh",
srcs: ["src/main/**/*.java"],
manifest: "etc/ahat.mf",
java_resources: ["etc/style.css"],
diff --git a/tools/ahat/ahat b/tools/ahat/ahat.sh
similarity index 100%
rename from tools/ahat/ahat
rename to tools/ahat/ahat.sh
diff --git a/tools/ahat/etc/style.css b/tools/ahat/etc/style.css
index 47fae1d..83e5b20 100644
--- a/tools/ahat/etc/style.css
+++ b/tools/ahat/etc/style.css
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+html {
+ /* Roboto has tabular numbers, use it if available, fallback to other sans. */
+ font-family: "Roboto", "Arial", "sans-serif";
+}
+
div.menu {
background-color: #eeffff;
}
@@ -39,3 +44,23 @@
padding-left: 8px;
padding-right: 8px;
}
+
+.sidebar {
+ position: fixed;
+ right: 0;
+ top: 48px;
+ padding-left: 12px;
+ padding-right: 24px;
+ font-size: small;
+ border-left: 4px solid #dcedc8;
+}
+
+.sidebar a {
+ text-decoration: none;
+ color: #4285f4;
+}
+
+.sidebar a:hover {
+ text-decoration: underline;
+ color: #073042;
+}
\ No newline at end of file
diff --git a/tools/ahat/src/main/com/android/ahat/DocString.java b/tools/ahat/src/main/com/android/ahat/DocString.java
index eda9b38..ca5dbf0 100644
--- a/tools/ahat/src/main/com/android/ahat/DocString.java
+++ b/tools/ahat/src/main/com/android/ahat/DocString.java
@@ -136,7 +136,7 @@
if (isPlaceHolder) {
string.append(DocString.removed("del"));
} else if (size != 0) {
- string.appendFormat("%,14d", size);
+ string.appendFormat("%,d", size);
}
return string;
}
@@ -162,13 +162,13 @@
public DocString appendDelta(boolean noCurrent, boolean noBaseline,
long current, long baseline) {
if (noCurrent) {
- append(removed(format("%+,14d", 0 - baseline)));
+ append(removed(format("%+,d", 0 - baseline)));
} else if (noBaseline) {
append(added("new"));
} else if (current > baseline) {
- append(added(format("%+,14d", current - baseline)));
+ append(added(format("%+,d", current - baseline)));
} else if (current < baseline) {
- append(removed(format("%+,14d", current - baseline)));
+ append(removed(format("%+,d", current - baseline)));
}
return this;
}
diff --git a/tools/ahat/src/main/com/android/ahat/HtmlDoc.java b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
index d5106dc..6c3ab2f 100644
--- a/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
+++ b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
@@ -18,6 +18,7 @@
import java.io.PrintStream;
import java.net.URI;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -26,6 +27,7 @@
class HtmlDoc implements Doc {
private PrintStream ps;
private Column[] mCurrentTableColumns;
+ private List<String> mSections;
/**
* Create an HtmlDoc that writes to the given print stream.
@@ -34,6 +36,7 @@
*/
public HtmlDoc(PrintStream ps, DocString title, URI style) {
this.ps = ps;
+ mSections = new ArrayList<>();
ps.println("<!DOCTYPE html>");
ps.println("<html>");
@@ -59,9 +62,10 @@
@Override
public void section(String title) {
- ps.print("<h2>");
+ ps.format("<h2 id=\"%d\">", mSections.size());
ps.print(DocString.text(title).html());
ps.println(":</h2>");
+ mSections.add(title);
}
@Override
@@ -182,8 +186,17 @@
mCurrentTableColumns = null;
}
+ private void sidebar() {
+ ps.println("<div class=\"sidebar\">");
+ for (int i = 0; i < mSections.size(); i++) {
+ ps.format("<p><a href=\"#%d\">%s</a></p>", i, mSections.get(i));
+ }
+ ps.println("</div>");
+ }
+
@Override
public void close() {
+ sidebar();
ps.println("</body>");
ps.println("</html>");
ps.close();
diff --git a/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java b/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
index 81611b6..4cdbaf4 100644
--- a/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
@@ -111,7 +111,7 @@
heapChoice.append(")");
doc.description(DocString.text("Heap"), heapChoice);
- doc.description(DocString.text("Count"), DocString.format("%,14d", insts.size()));
+ doc.description(DocString.text("Count"), DocString.format("%,d", insts.size()));
doc.end();
doc.println(DocString.text(""));
diff --git a/tools/ahat/src/main/com/android/ahat/OverviewHandler.java b/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
index 5f0b473..c6f4a54 100644
--- a/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
@@ -57,8 +57,6 @@
doc.section("Bytes Retained by Heap");
printHeapSizes(doc);
-
- doc.big(Menu.getMenu());
}
private void printHeapSizes(Doc doc) {
diff --git a/tools/ahat/src/main/com/android/ahat/SiteHandler.java b/tools/ahat/src/main/com/android/ahat/SiteHandler.java
index 5093f0d..671784e 100644
--- a/tools/ahat/src/main/com/android/ahat/SiteHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/SiteHandler.java
@@ -102,7 +102,7 @@
DocString.link(
DocString.formattedUri("objects?id=%d&heap=%s&class=%s",
site.getId(), info.heap.getName(), className),
- DocString.format("%,14d", info.numInstances)),
+ DocString.format("%,d", info.numInstances)),
DocString.delta(false, false, info.numInstances, baseinfo.numInstances),
DocString.text(info.heap.getName()),
Summarizer.summarize(info.classObj));
diff --git a/tools/ahat/src/test-dump/Main.java b/tools/ahat/src/test-dump/Main.java
index 2e29076..711d662 100644
--- a/tools/ahat/src/test-dump/Main.java
+++ b/tools/ahat/src/test-dump/Main.java
@@ -43,6 +43,10 @@
// Allocate the instance of DumpedStuff.
stuff = new DumpedStuff(baseline);
+ // Preemptively garbage collect to avoid an inopportune GC triggering
+ // after this.
+ Runtime.getRuntime().gc();
+
// Create a bunch of unreachable objects pointing to basicString for the
// reverseReferencesAreNotUnreachable test
for (int i = 0; i < 100; i++) {
diff --git a/tools/ahat/src/test/com/android/ahat/InstanceTest.java b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
index 376122b..1f29030 100644
--- a/tools/ahat/src/test/com/android/ahat/InstanceTest.java
+++ b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
@@ -224,6 +224,7 @@
@Test
public void reachability() throws IOException {
TestDump dump = TestDump.getTestDump();
+ // We were careful to avoid GC before dumping, so nothing here should be null.
AhatInstance strong1 = dump.getDumpedAhatInstance("reachabilityReferenceChain");
AhatInstance soft1 = strong1.getField("referent").asAhatInstance();
AhatInstance strong2 = soft1.getField("referent").asAhatInstance();
diff --git a/tools/build_linux_bionic.sh b/tools/build_linux_bionic.sh
index 8992512..bbe71b6 100755
--- a/tools/build_linux_bionic.sh
+++ b/tools/build_linux_bionic.sh
@@ -16,83 +16,38 @@
# This will build a target using linux_bionic. It can be called with normal make
# flags.
-#
-# TODO This runs a 'm clean' prior to building the targets in order to ensure
-# that obsolete kati files don't mess up the build.
-if [[ -z $ANDROID_BUILD_TOP ]]; then
- pushd .
-else
- pushd $ANDROID_BUILD_TOP
-fi
+set -e
if [ ! -d art ]; then
echo "Script needs to be run at the root of the android tree"
exit 1
fi
+export TARGET_PRODUCT=linux_bionic
+
+# Avoid Soong error about invalid dependencies on disabled libLLVM_android,
+# which we get due to the --soong-only mode. (Another variant is to set
+# SOONG_ALLOW_MISSING_DEPENDENCIES).
+export FORCE_BUILD_LLVM_COMPONENTS=true
+
# TODO(b/194433871): Set MODULE_BUILD_FROM_SOURCE to disable prebuilt modules,
# which Soong otherwise can create duplicate install rules for in --soong-only
# mode.
-soong_args="MODULE_BUILD_FROM_SOURCE=true"
+export MODULE_BUILD_FROM_SOURCE=true
# Switch the build system to unbundled mode in the reduced manifest branch.
if [ ! -d frameworks/base ]; then
- soong_args="$soong_args TARGET_BUILD_UNBUNDLED=true"
+ export TARGET_BUILD_UNBUNDLED=true
fi
-source build/envsetup.sh >&/dev/null # for get_build_var
-# Soong needs a bunch of variables set and will not run if they are missing.
-# The default values of these variables is only contained in make, so use
-# nothing to create the variables then remove all the other artifacts.
-# Lunch since it seems we cannot find the build-number otherwise.
-lunch aosp_x86-eng
-build/soong/soong_ui.bash --make-mode $soong_args nothing
+vars="$(build/soong/soong_ui.bash --dumpvars-mode --vars="OUT_DIR BUILD_NUMBER")"
+# Assign to a variable and eval that, since bash ignores any error status from
+# the command substitution if it's directly on the eval line.
+eval $vars
-if [ $? != 0 ]; then
- exit 1
-fi
+# This file is currently not created in --soong-only mode, but some build
+# targets depend on it.
+printf %s "${BUILD_NUMBER}" > ${OUT_DIR}/soong/build_number.txt
-out_dir=$(get_build_var OUT_DIR)
-host_out=$(get_build_var HOST_OUT)
-
-# TODO(b/31559095) Figure out a better way to do this.
-#
-# There is no good way to force soong to generate host-bionic builds currently
-# so this is a hacky workaround.
-tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
-tmp_build_number=$(cat ${out_dir}/soong/build_number.txt)
-
-cat $out_dir/soong/soong.variables > ${tmp_soong_var}
-
-# See comment above about b/123645297 for why we cannot just do m clean. Clear
-# out all files except for intermediates and installed files and dexpreopt.config.
-find $out_dir/ -maxdepth 1 -mindepth 1 \
- -not -name soong \
- -not -name host \
- -not -name target | xargs -I '{}' rm -rf '{}'
-find $out_dir/soong/ -maxdepth 1 -mindepth 1 \
- -not -name .intermediates \
- -not -name host \
- -not -name dexpreopt.config \
- -not -name target | xargs -I '{}' rm -rf '{}'
-
-python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
-import json
-import sys
-x = json.load(open(sys.argv[1]))
-x['Allow_missing_dependencies'] = True
-x['HostArch'] = 'x86_64'
-x['CrossHost'] = 'linux_bionic'
-x['CrossHostArch'] = 'x86_64'
-if 'CrossHostSecondaryArch' in x:
- del x['CrossHostSecondaryArch']
-json.dump(x, open(sys.argv[2], mode='w'))
-END
-
-rm $tmp_soong_var
-
-# Write a new build-number
-echo ${tmp_build_number}_SOONG_ONLY_BUILD > ${out_dir}/soong/build_number.txt
-
-build/soong/soong_ui.bash --make-mode --skip-config --soong-only $soong_args $@
+build/soong/soong_ui.bash --make-mode --soong-only "$@"
diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh
index 7379e9a..0470d6d 100755
--- a/tools/build_linux_bionic_tests.sh
+++ b/tools/build_linux_bionic_tests.sh
@@ -14,64 +14,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
-if [[ -z $ANDROID_BUILD_TOP ]]; then
- pushd .
-else
- pushd $ANDROID_BUILD_TOP
-fi
+set -e
if [ ! -d art ]; then
echo "Script needs to be run at the root of the android tree"
exit 1
fi
-soong_args=""
-
# Switch the build system to unbundled mode in the reduced manifest branch.
if [ ! -d frameworks/base ]; then
- soong_args="$soong_args TARGET_BUILD_UNBUNDLED=true"
+ export TARGET_BUILD_UNBUNDLED=true
fi
-source build/envsetup.sh >&/dev/null # for get_build_var
-
-out_dir=$(get_build_var OUT_DIR)
-host_out=$(get_build_var HOST_OUT)
-
-# TODO(b/31559095) Figure out a better way to do this.
-#
-# There is no good way to force soong to generate host-bionic builds currently
-# so this is a hacky workaround.
+vars="$(build/soong/soong_ui.bash --dumpvars-mode --vars="OUT_DIR HOST_OUT")"
+# Assign to a variable and eval that, since bash ignores any error status from
+# the command substitution if it's directly on the eval line.
+eval $vars
# First build all the targets still in .mk files (also build normal glibc host
# targets so we know what's needed to run the tests).
-build/soong/soong_ui.bash --make-mode $soong_args "$@" test-art-host-run-test-dependencies build-art-host-tests
-if [ $? != 0 ]; then
- exit 1
-fi
+build/soong/soong_ui.bash --make-mode "$@" test-art-host-run-test-dependencies build-art-host-tests
-tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
+# Next build the Linux host Bionic targets in --soong-only mode.
+export TARGET_PRODUCT=linux_bionic
-echo "Saving soong.variables to " $tmp_soong_var
-cat $out_dir/soong/soong.variables > ${tmp_soong_var}
-python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
-import json
-import sys
-x = json.load(open(sys.argv[1]))
-x['Allow_missing_dependencies'] = True
-x['HostArch'] = 'x86_64'
-x['CrossHost'] = 'linux_bionic'
-x['CrossHostArch'] = 'x86_64'
-if 'CrossHostSecondaryArch' in x:
- del x['CrossHostSecondaryArch']
-json.dump(x, open(sys.argv[2], mode='w'))
-END
-if [ $? != 0 ]; then
- mv $tmp_soong_var $out_dir/soong/soong.variables
- exit 2
-fi
+# Avoid Soong error about invalid dependencies on disabled libLLVM_android,
+# which we get due to the --soong-only mode. (Another variant is to set
+# SOONG_ALLOW_MISSING_DEPENDENCIES).
+export FORCE_BUILD_LLVM_COMPONENTS=true
-soong_out=$out_dir/soong/host/linux_bionic-x86
+soong_out=$OUT_DIR/soong/host/linux_bionic-x86
declare -a bionic_targets
# These are the binaries actually used in tests. Since some of the files are
# java targets or 32 bit we cannot just do the same find for the bin files.
@@ -89,24 +61,10 @@
$soong_out/bin/hprof-conv
$soong_out/bin/signal_dumper
$soong_out/lib64/libclang_rt.ubsan_standalone-x86_64-android.so
- $(find $host_out/apex/com.android.art.host.zipapex -type f | sed "s:$host_out:$soong_out:g")
- $(find $host_out/lib64 -type f | sed "s:$host_out:$soong_out:g")
- $(find $host_out/nativetest64 -type f | sed "s:$host_out:$soong_out:g"))
+ $(find $HOST_OUT/apex/com.android.art.host.zipapex -type f | sed "s:$HOST_OUT:$soong_out:g")
+ $(find $HOST_OUT/lib64 -type f | sed "s:$HOST_OUT:$soong_out:g")
+ $(find $HOST_OUT/nativetest64 -type f | sed "s:$HOST_OUT:$soong_out:g"))
echo building ${bionic_targets[*]}
-build/soong/soong_ui.bash --make-mode --skip-config --soong-only $soong_args "$@" ${bionic_targets[*]}
-ret=$?
-
-mv $tmp_soong_var $out_dir/soong/soong.variables
-
-# Having built with host-bionic confuses soong somewhat by making it think the
-# linux_bionic targets are needed for art phony targets like
-# test-art-host-run-test-dependencies. To work around this blow away all
-# ninja files in OUT_DIR. The build system is smart enough to not need to
-# rebuild stuff so this should be fine.
-rm -f $OUT_DIR/*.ninja $OUT_DIR/soong/*.ninja
-
-popd
-
-exit $ret
+build/soong/soong_ui.bash --make-mode --soong-only "$@" ${bionic_targets[*]}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 1622552..e55eb35 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -25,8 +25,6 @@
exit 1
fi
-TARGET_ARCH=$(source build/envsetup.sh > /dev/null; get_build_var TARGET_ARCH)
-
# Logic for setting out_dir from build/make/core/envsetup.mk:
if [[ -z $OUT_DIR ]]; then
if [[ -z $OUT_DIR_COMMON_BASE ]]; then
@@ -69,6 +67,9 @@
elif [[ "$1" == "--showcommands" ]]; then
showcommands="showcommands"
shift
+ elif [[ "$1" == "--dist" ]]; then
+ common_targets="$common_targets dist"
+ shift
elif [[ "$1" == "" ]]; then
break
else
@@ -84,7 +85,7 @@
fi
# Allow to build successfully in master-art.
-extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true"
+extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true BUILD_BROKEN_DISABLE_BAZEL=true"
# Switch the build system to unbundled mode in the reduced manifest branch.
if [ ! -d frameworks/base ]; then
@@ -120,13 +121,12 @@
# Indirect dependencies in the platform, e.g. through heapprofd_client_api.
# These are built to go into system/lib(64) to be part of the system linker
# namespace.
- make_command+=" libbacktrace libnetd_client-target libprocinfo libtombstoned_client libunwindstack"
+ make_command+=" libnetd_client-target libprocinfo libtombstoned_client libunwindstack"
# Stubs for other APEX SDKs, for use by vogar. Referenced from DEVICE_JARS in
# external/vogar/src/vogar/ModeId.java.
# Note these go into out/target/common/obj/JAVA_LIBRARIES which isn't removed
# by "m installclean".
make_command+=" i18n.module.public.api.stubs conscrypt.module.public.api.stubs"
- make_command+=" ${ANDROID_PRODUCT_OUT#"${ANDROID_BUILD_TOP}/"}/system/etc/public.libraries.txt"
# Targets required to generate a linker configuration for device within the
# chroot environment. The *.libraries.txt targets are required by
# the source linkerconfig but not included in the prebuilt one.
@@ -186,14 +186,19 @@
fi
done
- # Replace stub libraries with implemenation libraries: because we do chroot
+ # Replace stub libraries with implementation libraries: because we do chroot
# testing, we need to install an implementation of the libraries (and cannot
# rely on the one already installed on the device, if the device is post R and
# has it).
implementation_libs=(
"heapprofd_client_api.so"
+ "libandroid_runtime_lazy.so"
"libartpalette-system.so"
- "liblog.so"
+ "libbinder.so"
+ "libbinder_ndk.so"
+ "libcutils.so"
+ "libutils.so"
+ "libvndksupport.so"
)
if [ -d prebuilts/runtime/mainline/platform/impl ]; then
if [[ $TARGET_ARCH = arm* ]]; then
@@ -203,18 +208,22 @@
arch32=x86
arch64=x86_64
fi
- for so in ${implementation_libs[@]}; do
- if [ -d "$ANDROID_PRODUCT_OUT/system/lib" ]; then
- cmd="cp -p prebuilts/runtime/mainline/platform/impl/$arch32/$so $ANDROID_PRODUCT_OUT/system/lib/$so"
- msginfo "Executing" "$cmd"
- eval "$cmd"
- fi
- if [ -d "$ANDROID_PRODUCT_OUT/system/lib64" ]; then
- cmd="cp -p prebuilts/runtime/mainline/platform/impl/$arch64/$so $ANDROID_PRODUCT_OUT/system/lib64/$so"
- msginfo "Executing" "$cmd"
- eval "$cmd"
- fi
- done
+ if [ "$TARGET_ARCH" = riscv64 ]; then
+ true # no 32-bit arch for RISC-V
+ else
+ for so in ${implementation_libs[@]}; do
+ if [ -d "$ANDROID_PRODUCT_OUT/system/lib" ]; then
+ cmd="cp -p prebuilts/runtime/mainline/platform/impl/$arch32/$so $ANDROID_PRODUCT_OUT/system/lib/$so"
+ msginfo "Executing" "$cmd"
+ eval "$cmd"
+ fi
+ if [ -d "$ANDROID_PRODUCT_OUT/system/lib64" ]; then
+ cmd="cp -p prebuilts/runtime/mainline/platform/impl/$arch64/$so $ANDROID_PRODUCT_OUT/system/lib64/$so"
+ msginfo "Executing" "$cmd"
+ eval "$cmd"
+ fi
+ done
+ fi
fi
# Create canonical name -> file name symlink in the symbol directory for the
@@ -302,6 +311,11 @@
mkdir -p $linkerconfig_root/system
cp -r $ANDROID_PRODUCT_OUT/system/etc $linkerconfig_root/system
+ # Use our smaller public.libraries.txt that contains only the public libraries
+ # pushed to the chroot directory.
+ cp $ANDROID_BUILD_TOP/art/tools/public.libraries.buildbot.txt \
+ $linkerconfig_root/system/etc/public.libraries.txt
+
# For linkerconfig to pick up the APEXes correctly we need to make them
# available in $linkerconfig_root/apex.
mkdir -p $linkerconfig_root/apex
diff --git a/tools/buildbot-cleanup-device.sh b/tools/buildbot-cleanup-device.sh
index 7dee149..7fd57b4 100755
--- a/tools/buildbot-cleanup-device.sh
+++ b/tools/buildbot-cleanup-device.sh
@@ -16,7 +16,22 @@
. "$(dirname $0)/buildbot-utils.sh"
-# Setup as root, as device cleanup requires it.
+# Testing on a Linux VM requires special cleanup.
+if [[ -n "$ART_TEST_ON_VM" ]]; then
+ [[ -d "$ART_TEST_VM_DIR" ]] || { msgfatal "no VM found in $ART_TEST_VM_DIR"; }
+ $ART_SSH_CMD "true" || { msgfatal "VM not responding (tried \"$ART_SSH_CMD true\""; }
+ $ART_SSH_CMD "
+ sudo umount $ART_TEST_CHROOT/proc
+ sudo umount $ART_TEST_CHROOT/sys
+ sudo umount $ART_TEST_CHROOT/dev
+ sudo umount $ART_TEST_CHROOT/bin
+ sudo umount $ART_TEST_CHROOT/lib
+ rm -rf $ART_TEST_CHROOT
+ "
+ exit 0
+fi
+
+# Regular Android device. Setup as root, as device cleanup requires it.
adb root
adb wait-for-device
diff --git a/tools/buildbot-setup-device.sh b/tools/buildbot-setup-device.sh
index ad2c59c..90d680b 100755
--- a/tools/buildbot-setup-device.sh
+++ b/tools/buildbot-setup-device.sh
@@ -25,7 +25,39 @@
verbose=false
fi
-# Setup as root, as some actions performed here require it.
+# Testing on a Linux VM requires special setup.
+if [[ -n "$ART_TEST_ON_VM" ]]; then
+ [[ -d "$ART_TEST_VM_DIR" ]] || { msgfatal "no VM found in $ART_TEST_VM_DIR"; }
+ $ART_SSH_CMD "true" || { msgerror "no VM (tried \"$ART_SSH_CMD true\""; }
+ $ART_SSH_CMD "
+ mkdir $ART_TEST_CHROOT
+
+ mkdir $ART_TEST_CHROOT/apex
+ mkdir $ART_TEST_CHROOT/bin
+ mkdir $ART_TEST_CHROOT/data
+ mkdir $ART_TEST_CHROOT/data/local
+ mkdir $ART_TEST_CHROOT/data/local/tmp
+ mkdir $ART_TEST_CHROOT/dev
+ mkdir $ART_TEST_CHROOT/etc
+ mkdir $ART_TEST_CHROOT/lib
+ mkdir $ART_TEST_CHROOT/linkerconfig
+ mkdir $ART_TEST_CHROOT/proc
+ mkdir $ART_TEST_CHROOT/sys
+ mkdir $ART_TEST_CHROOT/system
+ mkdir $ART_TEST_CHROOT/tmp
+
+ sudo mount -t proc /proc art-test-chroot/proc
+ sudo mount -t sysfs /sys art-test-chroot/sys
+ sudo mount --bind /dev art-test-chroot/dev
+ sudo mount --bind /bin art-test-chroot/bin
+ sudo mount --bind /lib art-test-chroot/lib
+ $ART_CHROOT_CMD echo \"Hello from chroot! I am \$(uname -a).\"
+ "
+ exit 0
+fi
+
+# Regular Android device. Setup as root, as some actions performed here require it.
+adb version
adb root
adb wait-for-device
@@ -36,7 +68,11 @@
adb shell date
host_seconds_since_epoch=$(date -u +%s)
-device_seconds_since_epoch=$(adb shell date -u +%s)
+
+# Get the device time in seconds, but filter the output as some
+# devices emit CRLF at the end of the command which then breaks the
+# time comparisons in this script (Hammerhead, MRA59G 2457013).
+device_seconds_since_epoch=$(adb shell date -u +%s | tr -c -d '[:digit:]')
abs_time_difference_in_seconds=$(expr $host_seconds_since_epoch - $device_seconds_since_epoch)
if [ $abs_time_difference_in_seconds -lt 0 ]; then
@@ -173,6 +209,8 @@
|| adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev"
adb shell mount | grep -q "^devpts on $ART_TEST_CHROOT/dev/pts type devpts " \
|| adb shell mount -o bind /dev/pts "$ART_TEST_CHROOT/dev/pts"
+ adb shell mount | grep -q " on $ART_TEST_CHROOT/dev/cpuset type cgroup " \
+ || adb shell mount -o bind /dev/cpuset "$ART_TEST_CHROOT/dev/cpuset"
# Create /apex directory in chroot.
adb shell mkdir -p "$ART_TEST_CHROOT/apex"
diff --git a/tools/buildbot-sync.sh b/tools/buildbot-sync.sh
index ba49c61..ef9ec8b 100755
--- a/tools/buildbot-sync.sh
+++ b/tools/buildbot-sync.sh
@@ -20,27 +20,21 @@
. "$(dirname $0)/buildbot-utils.sh"
-# Setup as root, as some actions performed here require it.
-adb root
-adb wait-for-device
+if [[ -z "$ART_TEST_ON_VM" ]]; then
+ # Setup as root, as some actions performed here require it.
+ adb root
+ adb wait-for-device
+fi
if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- msgerror 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
-fi
-
-if [[ -z "$ANDROID_PRODUCT_OUT" ]]; then
- msgerror 'ANDROID_PRODUCT_OUT environment variable is empty; did you forget to run `lunch`?'
- exit 1
-fi
-
-if [[ -z "$ART_TEST_CHROOT" ]]; then
- msgerror 'ART_TEST_CHROOT environment variable is empty; ' \
+ msgfatal 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
+elif [[ -z "$ANDROID_PRODUCT_OUT" ]]; then
+ msgfatal 'ANDROID_PRODUCT_OUT environment variable is empty; did you forget to run `lunch`?'
+elif [[ -z "$ART_TEST_CHROOT" ]]; then
+ msgfatal 'ART_TEST_CHROOT environment variable is empty; ' \
'please set it before running this script.'
- exit 1
fi
-
# Sync relevant product directories
# ---------------------------------
@@ -53,23 +47,40 @@
continue
fi
msginfo "Syncing $dir directory..."
- adb shell mkdir -p "$ART_TEST_CHROOT/$dir"
- adb push $dir "$ART_TEST_CHROOT/$(dirname $dir)"
+ if [[ -n "$ART_TEST_ON_VM" ]]; then
+ $ART_RSYNC_CMD -R $dir "$ART_TEST_SSH_USER@$ART_TEST_SSH_HOST:$ART_TEST_CHROOT"
+ else
+ adb shell mkdir -p "$ART_TEST_CHROOT/$dir"
+ adb push $dir "$ART_TEST_CHROOT/$(dirname $dir)"
+ fi
done
)
# Overwrite the default public.libraries.txt file with a smaller one that
# contains only the public libraries pushed to the chroot directory.
-adb push "$ANDROID_BUILD_TOP/art/tools/public.libraries.buildbot.txt" \
- "$ART_TEST_CHROOT/system/etc/public.libraries.txt"
+if [[ -n "$ART_TEST_ON_VM" ]]; then
+ $ART_RSYNC_CMD "$ANDROID_BUILD_TOP/art/tools/public.libraries.buildbot.txt" \
+ "$ART_TEST_SSH_USER@$ART_TEST_SSH_HOST:$ART_TEST_CHROOT/system/etc/public.libraries.txt"
+else
+ adb push "$ANDROID_BUILD_TOP/art/tools/public.libraries.buildbot.txt" \
+ "$ART_TEST_CHROOT/system/etc/public.libraries.txt"
+fi
# Create the framework directory if it doesn't exist. Some gtests need it.
-adb shell mkdir -p "$ART_TEST_CHROOT/system/framework"
+if [[ -n "$ART_TEST_ON_VM" ]]; then
+ $ART_SSH_CMD "$ART_CHROOT_CMD mkdir -p $ART_TEST_CHROOT/system/framework"
+else
+ adb shell mkdir -p "$ART_TEST_CHROOT/system/framework"
+fi
# APEX packages activation.
# -------------------------
-adb shell mkdir -p "$ART_TEST_CHROOT/apex"
+if [[ -n "$ART_TEST_ON_VM" ]]; then
+ $ART_SSH_CMD "$ART_CHROOT_CMD mkdir -p $ART_TEST_CHROOT/apex"
+else
+ adb shell mkdir -p "$ART_TEST_CHROOT/apex"
+fi
# Manually "activate" the flattened APEX $1 by syncing it to /apex/$2 in the
# chroot. $2 defaults to $1.
@@ -101,8 +112,13 @@
fi
msginfo "Activating APEX ${src_apex} as ${dst_apex}..."
- adb shell rm -rf "$ART_TEST_CHROOT/apex/${dst_apex}"
- adb push $src_apex_path "$ART_TEST_CHROOT/apex/${dst_apex}"
+ if [[ -n "$ART_TEST_ON_VM" ]]; then
+ $ART_RSYNC_CMD $src_apex_path/* \
+ "$ART_TEST_SSH_USER@$ART_TEST_SSH_HOST:$ART_TEST_CHROOT/apex/${dst_apex}"
+ else
+ adb shell rm -rf "$ART_TEST_CHROOT/apex/${dst_apex}"
+ adb push $src_apex_path "$ART_TEST_CHROOT/apex/${dst_apex}"
+ fi
}
# "Activate" the required APEX modules.
@@ -113,19 +129,34 @@
activate_apex com.android.conscrypt
activate_apex com.android.os.statsd
-# Generate primary boot images on device for testing.
-for b in {32,64}; do
- basename="generate-boot-image$b"
- bin_on_host="$ANDROID_PRODUCT_OUT/system/bin/$basename"
- bin_on_device="/data/local/tmp/$basename"
- output_dir="/system/framework/art_boot_images"
- if [ -f $bin_on_host ]; then
- msginfo "Generating the primary boot image ($b-bit)..."
- adb push "$bin_on_host" "$ART_TEST_CHROOT$bin_on_device"
- adb shell mkdir -p "$ART_TEST_CHROOT$output_dir"
- # `compiler-filter=speed-profile` is required because OatDumpTest checks the compiled code in
- # the boot image.
- adb shell chroot "$ART_TEST_CHROOT" \
- "$bin_on_device" --output-dir=$output_dir --compiler-filter=speed-profile
- fi
-done
+if [[ "$TARGET_ARCH" = "riscv64" ]]; then
+ true # Skip boot image generation for RISC-V; it's not supported.
+else
+ # Generate primary boot images on device for testing.
+ for b in {32,64}; do
+ basename="generate-boot-image$b"
+ bin_on_host="$ANDROID_PRODUCT_OUT/system/bin/$basename"
+ bin_on_device="/data/local/tmp/$basename"
+ output_dir="/system/framework/art_boot_images"
+ if [ -f $bin_on_host ]; then
+ msginfo "Generating the primary boot image ($b-bit)..."
+ if [[ -n "$ART_TEST_ON_VM" ]]; then
+ $ART_RSYNC_CMD "$bin_on_host" \
+ "$ART_TEST_SSH_USER@$ART_TEST_SSH_HOST:$ART_TEST_CHROOT$bin_on_device"
+ $ART_SSH_CMD "mkdir -p $ART_TEST_CHROOT$output_dir"
+ else
+ adb push "$bin_on_host" "$ART_TEST_CHROOT$bin_on_device"
+ adb shell mkdir -p "$ART_TEST_CHROOT$output_dir"
+ fi
+ # `compiler-filter=speed-profile` is required because OatDumpTest checks the compiled code in
+ # the boot image.
+ if [[ -n "$ART_TEST_ON_VM" ]]; then
+ $ART_SSH_CMD \
+ "$ART_CHROOT_CMD $bin_on_device --output-dir=$output_dir --compiler-filter=speed-profile"
+ else
+ adb shell chroot "$ART_TEST_CHROOT" \
+ "$bin_on_device" --output-dir=$output_dir --compiler-filter=speed-profile
+ fi
+ fi
+ done
+fi
diff --git a/tools/buildbot-teardown-device.sh b/tools/buildbot-teardown-device.sh
index 927e3c5..e71dcbe 100755
--- a/tools/buildbot-teardown-device.sh
+++ b/tools/buildbot-teardown-device.sh
@@ -19,6 +19,8 @@
. "$(dirname $0)/buildbot-utils.sh"
+[[ -n "$ART_TEST_ON_VM" ]] && exit 0
+
# Setup as root, as some actions performed here require it.
adb root
adb wait-for-device
@@ -79,7 +81,7 @@
local remove_dir=$3
local dir="$ART_TEST_CHROOT/$dir_in_chroot"
adb shell test -d "$dir" \
- && adb shell mount | grep -q "^$fstype on $dir type $fstype " \
+ && adb shell mount | grep -q " on $dir type $fstype " \
&& if adb shell umount "$dir"; then
$remove_dir && adb shell rmdir "$dir"
else
@@ -95,6 +97,7 @@
adb shell rm -rf "$ART_TEST_CHROOT/apex"
# Remove /dev from chroot.
+ remove_filesystem_from_chroot dev/cpuset cgroup false
remove_filesystem_from_chroot dev/pts devpts false
remove_filesystem_from_chroot dev tmpfs true
diff --git a/tools/buildbot-utils.sh b/tools/buildbot-utils.sh
index 32ed234..5bc58af 100755
--- a/tools/buildbot-utils.sh
+++ b/tools/buildbot-utils.sh
@@ -53,7 +53,43 @@
echo -e "${boldred}Error: ${nc}${message}"
}
+function msgfatal() {
+ local message="$*"
+ echo -e "${boldred}Fatal: ${nc}${message}"
+ exit 1
+}
+
function msgnote() {
local message="$*"
echo -e "${boldcyan}Note: ${nc}${message}"
}
+
+export TARGET_ARCH=$(build/soong/soong_ui.bash --dumpvar-mode TARGET_ARCH)
+
+# Do some checks and prepare environment for tests that run on Linux (not on Android).
+if [[ -n "$ART_TEST_ON_VM" ]]; then
+ if [[ -z $ANDROID_BUILD_TOP ]]; then
+ msgfatal "ANDROID_BUILD_TOP is not set"
+ elif [[ -z "$ART_TEST_SSH_USER" ]]; then
+ msgfatal "ART_TEST_SSH_USER not set"
+ elif [[ -z "$ART_TEST_SSH_HOST" ]]; then
+ msgfatal "ART_TEST_SSH_HOST not set"
+ elif [[ -z "$ART_TEST_SSH_PORT" ]]; then
+ msgfatal "ART_TEST_SSH_PORT not set"
+ fi
+
+ export ART_TEST_CHROOT="/home/$ART_TEST_SSH_USER/art-test-chroot"
+ export ART_CHROOT_CMD="unshare --user --map-root-user chroot art-test-chroot"
+ export ART_SSH_CMD="ssh -q -p $ART_TEST_SSH_PORT $ART_TEST_SSH_USER@$ART_TEST_SSH_HOST"
+ export ART_SCP_CMD="scp -P $ART_TEST_SSH_PORT -p -r"
+ export ART_RSYNC_CMD="rsync -az"
+ export RSYNC_RSH="ssh -p $ART_TEST_SSH_PORT" # don't prefix with "ART_", rsync expects this name
+
+ if [[ "$TARGET_ARCH" =~ ^(arm64|riscv64)$ ]]; then
+ export ART_TEST_VM_IMG="ubuntu-22.04-server-cloudimg-$TARGET_ARCH.img"
+ export ART_TEST_VM_DIR="$ANDROID_BUILD_TOP/vm/$TARGET_ARCH"
+ export ART_TEST_VM="$ART_TEST_VM_DIR/$ART_TEST_VM_IMG"
+ else
+ msgfatal "unexpected TARGET_ARCH=$TARGET_ARCH; expected one of {arm64,riscv64}"
+ fi
+fi
diff --git a/tools/buildbot-vm.sh b/tools/buildbot-vm.sh
new file mode 100755
index 0000000..524a57b
--- /dev/null
+++ b/tools/buildbot-vm.sh
@@ -0,0 +1,128 @@
+#! /bin/bash
+#
+# Copyright (C) 2023 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.
+
+set -e
+
+ART_TEST_ON_VM=true . "$(dirname $0)/buildbot-utils.sh"
+
+known_actions="create|boot|setup-ssh|connect|quit"
+
+if [[ -z $ANDROID_BUILD_TOP ]]; then
+ msgfatal "ANDROID_BUILD_TOP is not set"
+elif [[ ( $# -ne 1 ) || ! ( "$1" =~ ^($known_actions)$ ) ]]; then
+ msgfatal "usage: $0 <$known_actions>"
+fi
+
+action="$1"
+
+get_stable_binary() {
+ mkdir tmp && cd tmp
+ wget "http://security.ubuntu.com/ubuntu/pool/main/$1"
+ 7z x "$(basename $1)" && zstd -d data.tar.zst && tar -xf data.tar
+ mv "$2" ..
+ cd .. && rm -rf tmp
+}
+
+if [[ $action = create ]]; then
+(
+ rm -rf "$ART_TEST_VM_DIR"
+ mkdir -p "$ART_TEST_VM_DIR"
+ cd "$ART_TEST_VM_DIR"
+
+ # sudo apt install qemu-system-<arch> qemu-efi cloud-image-utils
+
+ # Get the cloud image for Ubunty 22.04 (Jammy)
+ wget "http://cloud-images.ubuntu.com/releases/22.04/release/$ART_TEST_VM_IMG"
+
+ if [[ "$TARGET_ARCH" = "riscv64" ]]; then
+ # Get U-Boot for Ubuntu 22.04 (Jammy)
+ get_stable_binary \
+ u/u-boot/u-boot-qemu_2022.01+dfsg-2ubuntu2.3_all.deb \
+ usr/lib/u-boot/qemu-riscv64_smode/uboot.elf
+
+ # Get OpenSBI for Ubuntu 22.04 (Jammy)
+ get_stable_binary \
+ o/opensbi/opensbi_1.1-0ubuntu0.22.04.1_all.deb \
+ usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf
+
+ elif [[ "$TARGET_ARCH" = "arm64" ]]; then
+ # Get EFI (ARM64) for Ubuntu 22.04 (Jammy)
+ get_stable_binary \
+ e/edk2/qemu-efi-aarch64_2022.02-3ubuntu0.22.04.1_all.deb \
+ usr/share/qemu-efi-aarch64/QEMU_EFI.fd
+
+ dd if=/dev/zero of=flash0.img bs=1M count=64
+ dd if=QEMU_EFI.fd of=flash0.img conv=notrunc
+ dd if=/dev/zero of=flash1.img bs=1M count=64
+ fi
+
+ qemu-img resize "$ART_TEST_VM_IMG" +128G
+
+ # https://help.ubuntu.com/community/CloudInit
+ cat >user-data <<EOF
+#cloud-config
+ssh_pwauth: true
+chpasswd:
+ expire: false
+ list:
+ - $ART_TEST_SSH_USER:ubuntu
+EOF
+ cloud-localds user-data.img user-data
+)
+elif [[ $action = boot ]]; then
+(
+ cd "$ART_TEST_VM_DIR"
+ if [[ "$TARGET_ARCH" = "riscv64" ]]; then
+ qemu-system-riscv64 \
+ -m 16G \
+ -smp 8 \
+ -M virt \
+ -nographic \
+ -bios fw_jump.elf \
+ -kernel uboot.elf \
+ -drive file="$ART_TEST_VM_IMG",if=virtio \
+ -drive file=user-data.img,format=raw,if=virtio \
+ -device virtio-net-device,netdev=usernet \
+ -netdev user,id=usernet,hostfwd=tcp::$ART_TEST_SSH_PORT-:22
+ elif [[ "$TARGET_ARCH" = "arm64" ]]; then
+ qemu-system-aarch64 \
+ -m 16G \
+ -smp 8 \
+ -cpu cortex-a57 \
+ -M virt \
+ -nographic \
+ -drive if=none,file="$ART_TEST_VM_IMG",id=hd0 \
+ -pflash flash0.img \
+ -pflash flash1.img \
+ -drive file=user-data.img,format=raw,id=cloud \
+ -device virtio-blk-device,drive=hd0 \
+ -device virtio-net-device,netdev=usernet \
+ -netdev user,id=usernet,hostfwd=tcp::$ART_TEST_SSH_PORT-:22
+ fi
+
+)
+elif [[ $action = setup-ssh ]]; then
+ # Clean up mentions of this VM from known_hosts
+ sed -i -E "/\[$ART_TEST_SSH_HOST.*\]:$ART_TEST_SSH_PORT .*/d" $HOME/.ssh/known_hosts
+ ssh-copy-id -p "$ART_TEST_SSH_PORT" "$ART_TEST_SSH_USER@$ART_TEST_SSH_HOST"
+
+elif [[ $action = connect ]]; then
+ ssh -p "$ART_TEST_SSH_PORT" "$ART_TEST_SSH_USER@$ART_TEST_SSH_HOST"
+
+elif [[ $action = quit ]]; then
+ ssh -p "$ART_TEST_SSH_PORT" "$ART_TEST_SSH_USER@$ART_TEST_SSH_HOST" "sudo poweroff"
+
+fi
diff --git a/tools/check_presubmit_json_expectations.sh b/tools/check_presubmit_json_expectations.sh
new file mode 100755
index 0000000..ecb1e3e
--- /dev/null
+++ b/tools/check_presubmit_json_expectations.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e
+
+REPO_ROOT="$1"
+
+FILES_TO_CHECK=()
+for i in "${@:2}"; do
+ if [[ $i == *_failures.txt ]]; then
+ FILES_TO_CHECK+=($i)
+ fi
+done
+
+# if no libcore_*_failures.txt files were changed
+if [ ${#FILES_TO_CHECK[@]} -eq 0 ]; then
+ exit 0
+fi
+
+TMP_DIR=`mktemp -d`
+# check if tmp dir was created
+if [[ ! "$TMP_DIR" || ! -d "$TMP_DIR" ]]; then
+ echo "Could not create temp dir"
+ exit 1
+fi
+
+function cleanup {
+ rm -rf "$TMP_DIR"
+}
+
+# register the cleanup function to be called on the EXIT signal
+trap cleanup EXIT
+
+GSON_JAR="${REPO_ROOT}/external/caliper/lib/gson-2.2.2.jar"
+
+javac --class-path "$GSON_JAR" "${REPO_ROOT}/art/tools/PresubmitJsonLinter.java" -d "$TMP_DIR"
+java --class-path "$TMP_DIR:$GSON_JAR" PresubmitJsonLinter "${FILES_TO_CHECK[@]}"
diff --git a/tools/checker/Android.bp b/tools/checker/Android.bp
new file mode 100644
index 0000000..db2c597
--- /dev/null
+++ b/tools/checker/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2022 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+python_binary_host {
+ name: "art-run-test-checker",
+ srcs: [
+ "**/*.py",
+ ],
+ main: "checker.py",
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+ test_suites: [
+ "general-tests",
+ "mts-art",
+ ],
+}
diff --git a/tools/cpp-define-generator/globals.def b/tools/cpp-define-generator/globals.def
index 2572ea6..459e5a8 100644
--- a/tools/cpp-define-generator/globals.def
+++ b/tools/cpp-define-generator/globals.def
@@ -28,6 +28,7 @@
#include "mirror/object_reference.h"
#include "runtime_globals.h"
#include "stack.h"
+#include "entrypoints/quick/callee_save_frame.h"
#endif
ASM_DEFINE(ACCESS_FLAGS_METHOD_IS_NATIVE,
@@ -82,3 +83,11 @@
std::memory_order_relaxed)
ASM_DEFINE(STACK_OVERFLOW_RESERVED_BYTES,
GetStackOverflowReservedBytes(art::kRuntimeISA))
+ASM_DEFINE(CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS,
+ art::POPCOUNT(art::RuntimeCalleeSaveFrame::GetCoreSpills(
+ art::CalleeSaveType::kSaveEverything)))
+ASM_DEFINE(TAGGED_JNI_SP_MASK, art::ManagedStack::kTaggedJniSpMask)
+ASM_DEFINE(TAGGED_JNI_SP_MASK_TOGGLED32,
+ ~static_cast<uint32_t>(art::ManagedStack::kTaggedJniSpMask))
+ASM_DEFINE(TAGGED_JNI_SP_MASK_TOGGLED64,
+ ~static_cast<uint64_t>(art::ManagedStack::kTaggedJniSpMask))
diff --git a/tools/cpp-define-generator/lockword.def b/tools/cpp-define-generator/lockword.def
index a170c15..5494d59 100644
--- a/tools/cpp-define-generator/lockword.def
+++ b/tools/cpp-define-generator/lockword.def
@@ -30,10 +30,8 @@
art::LockWord::kMarkBitStateMaskShifted)
ASM_DEFINE(LOCK_WORD_MARK_BIT_SHIFT,
art::LockWord::kMarkBitStateShift)
-ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_MASK,
+ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_MASK_SHIFTED,
art::LockWord::kReadBarrierStateMaskShifted)
-ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED,
- art::LockWord::kReadBarrierStateMaskShiftedToggled)
ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_SHIFT,
art::LockWord::kReadBarrierStateShift)
ASM_DEFINE(LOCK_WORD_STATE_FORWARDING_ADDRESS,
diff --git a/tools/cpp-define-generator/mirror_class.def b/tools/cpp-define-generator/mirror_class.def
index 8cfd54e..062a7aa 100644
--- a/tools/cpp-define-generator/mirror_class.def
+++ b/tools/cpp-define-generator/mirror_class.def
@@ -16,6 +16,7 @@
#if ASM_DEFINE_INCLUDE_DEPENDENCIES
#include "mirror/class.h"
+#include "subtype_check.h"
#endif
ASM_DEFINE(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
@@ -49,3 +50,17 @@
ASM_DEFINE(MIRROR_CLASS_IS_INTERFACE_FLAG, art::kAccInterface)
ASM_DEFINE(MIRROR_CLASS_IS_INTERFACE_FLAG_BIT,
art::WhichPowerOf2(art::kAccInterface))
+ASM_DEFINE(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET,
+ art::mirror::Class::StatusOffset().SizeValue() +
+ (art::SubtypeCheckBits::BitStructSizeOf() / art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE,
+ art::enum_cast<uint32_t>(art::ClassStatus::kVisiblyInitialized) <<
+ (art::SubtypeCheckBits::BitStructSizeOf() % art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_IS_INITIALIZING_VALUE,
+ art::enum_cast<uint32_t>(art::ClassStatus::kInitializing) <<
+ (art::SubtypeCheckBits::BitStructSizeOf() % art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_IS_INITIALIZED_VALUE,
+ art::enum_cast<uint32_t>(art::ClassStatus::kInitialized) <<
+ (art::SubtypeCheckBits::BitStructSizeOf() % art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET,
+ art::mirror::Class::ClinitThreadIdOffset().Int32Value())
diff --git a/tools/cpp-define-generator/runtime.def b/tools/cpp-define-generator/runtime.def
index 2a2e303..fd6567d 100644
--- a/tools/cpp-define-generator/runtime.def
+++ b/tools/cpp-define-generator/runtime.def
@@ -30,3 +30,7 @@
art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsAndArgs))
ASM_DEFINE(RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET,
art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsOnly))
+ASM_DEFINE(RUNTIME_INSTRUMENTATION_OFFSET, art::Runtime::GetInstrumentationOffset().Int32Value())
+ASM_DEFINE(RUN_EXIT_HOOKS_OFFSET_FROM_RUNTIME_INSTANCE,
+ art::Runtime::GetInstrumentationOffset().Int32Value() +
+ art::instrumentation::Instrumentation::RunExitHooksOffset().Int32Value())
diff --git a/tools/cpp-define-generator/thread.def b/tools/cpp-define-generator/thread.def
index bae9200..97033fc 100644
--- a/tools/cpp-define-generator/thread.def
+++ b/tools/cpp-define-generator/thread.def
@@ -37,6 +37,8 @@
(art::WhichPowerOf2(sizeof(art::InterpreterCache::Entry)) - 2))
ASM_DEFINE(THREAD_IS_GC_MARKING_OFFSET,
art::Thread::IsGcMarkingOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_DEOPT_CHECK_REQUIRED_OFFSET,
+ art::Thread::DeoptCheckRequiredOffset<art::kRuntimePointerSize>().Int32Value())
ASM_DEFINE(THREAD_LOCAL_ALLOC_STACK_END_OFFSET,
art::Thread::ThreadLocalAllocStackEndOffset<art::kRuntimePointerSize>().Int32Value())
ASM_DEFINE(THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET,
@@ -69,3 +71,5 @@
art::Thread::ReadBarrierMarkEntryPointsOffset<art::kRuntimePointerSize>(0))
ASM_DEFINE(THREAD_SHARED_METHOD_HOTNESS_OFFSET,
art::Thread::SharedMethodHotnessOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_TID_OFFSET,
+ art::Thread::TidOffset<art::kRuntimePointerSize>().Int32Value())
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index b124f43..384ab37 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -459,6 +459,7 @@
}
// Count uses of top 16n.
std::vector<size_t> uses;
+ uses.reserve(types_accessed.size());
for (auto&& p : types_accessed) {
uses.push_back(p.second);
}
diff --git a/tools/dexanalyze/dexanalyze_test.cc b/tools/dexanalyze/dexanalyze_test.cc
index 9e6ed6d..474615d 100644
--- a/tools/dexanalyze/dexanalyze_test.cc
+++ b/tools/dexanalyze/dexanalyze_test.cc
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
#include "exec_utils.h"
namespace art {
-class DexAnalyzeTest : public CommonRuntimeTest {
+class DexAnalyzeTest : public CommonArtTest {
public:
std::string GetDexAnalyzePath() {
return GetArtBinDir() + "/dexanalyze";
diff --git a/tools/dexfuzz/.clang-format b/tools/dexfuzz/.clang-format
new file mode 120000
index 0000000..88ab38e
--- /dev/null
+++ b/tools/dexfuzz/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-java-2
\ No newline at end of file
diff --git a/tools/dexfuzz/Android.bp b/tools/dexfuzz/Android.bp
index 02bda0e..083ecd7 100644
--- a/tools/dexfuzz/Android.bp
+++ b/tools/dexfuzz/Android.bp
@@ -33,6 +33,6 @@
// --- dexfuzz script ----------------
sh_binary_host {
name: "dexfuzz-script",
- src: "dexfuzz",
- filename_from_src: true,
+ src: "dexfuzz.sh",
+ filename: "dexfuzz",
}
diff --git a/tools/dexfuzz/dexfuzz b/tools/dexfuzz/dexfuzz.sh
similarity index 100%
rename from tools/dexfuzz/dexfuzz
rename to tools/dexfuzz/dexfuzz.sh
diff --git a/tools/dist_linux_bionic.sh b/tools/dist_linux_bionic.sh
index 4c7ba1c..f710310 100755
--- a/tools/dist_linux_bionic.sh
+++ b/tools/dist_linux_bionic.sh
@@ -19,12 +19,6 @@
# Builds the given targets using linux-bionic and moves the output files to the
# DIST_DIR. Takes normal make arguments.
-if [[ -z $ANDROID_BUILD_TOP ]]; then
- pushd .
-else
- pushd $ANDROID_BUILD_TOP
-fi
-
if [[ -z $DIST_DIR ]]; then
echo "DIST_DIR must be set!"
exit 1
@@ -35,10 +29,12 @@
exit 1
fi
-source build/envsetup.sh >&/dev/null # for get_build_var
-out_dir=$(get_build_var OUT_DIR)
+vars="$(build/soong/soong_ui.bash --dumpvars-mode --vars="OUT_DIR")"
+# Assign to a variable and eval that, since bash ignores any error status from
+# the command substitution if it's directly on the eval line.
+eval $vars
-./art/tools/build_linux_bionic.sh $@
+./art/tools/build_linux_bionic.sh "$@"
mkdir -p $DIST_DIR
-cp -R ${out_dir}/soong/host/* $DIST_DIR/
+cp -R ${OUT_DIR}/soong/host/* $DIST_DIR/
diff --git a/tools/dmtracedump/createtesttrace.cc b/tools/dmtracedump/createtesttrace.cc
index 444cce4..d55b3e4 100644
--- a/tools/dmtracedump/createtesttrace.cc
+++ b/tools/dmtracedump/createtesttrace.cc
@@ -22,6 +22,7 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <memory>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -143,7 +144,7 @@
/* Add space for a sentinel record at the end */
numRecords += 1;
records = new dataRecord[numRecords];
- stack* callStack = new stack[numThreads];
+ std::unique_ptr<stack[]> callStack(new stack[numThreads]);
for (int32_t ii = 0; ii < numThreads; ++ii) {
callStack[ii].frames = nullptr;
callStack[ii].indentLevel = 0;
diff --git a/tools/dmtracedump/tracedump.cc b/tools/dmtracedump/tracedump.cc
index 3cb7374..ecae6c1 100644
--- a/tools/dmtracedump/tracedump.cc
+++ b/tools/dmtracedump/tracedump.cc
@@ -1490,10 +1490,8 @@
char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
char anchor_buf[80];
- const char* anchor_close = "";
anchor_buf[0] = 0;
if (gOptions.outputHtml) {
- anchor_close = "</a>";
printf("<a name=\"inclusive\"></a>\n");
printf("<hr>\n");
outputNavigationBar();
diff --git a/tools/generate_cmake_lists.py b/tools/generate_cmake_lists.py
index b19c292..3fda003 100755
--- a/tools/generate_cmake_lists.py
+++ b/tools/generate_cmake_lists.py
@@ -32,6 +32,7 @@
(Also, exclude projects that you don't bother about. This will make
the indexing faster).
"""
+from __future__ import print_function
import sys
import os
@@ -47,7 +48,7 @@
path_to_top = os.path.realpath(path_to_top)
if not os.path.exists(os.path.join(path_to_top, 'build/envsetup.sh')):
- print path_to_top
+ print(path_to_top)
raise AssertionError("geneate_cmake_lists.py must be located inside an android source tree")
return path_to_top
diff --git a/tools/generate_operator_out.py b/tools/generate_operator_out.py
index f1491d8..f3de61c 100755
--- a/tools/generate_operator_out.py
+++ b/tools/generate_operator_out.py
@@ -65,7 +65,7 @@
continue
# Is this the start or end of a namespace?
- m = re.search(r'^namespace (\S+) \{', raw_line)
+ m = re.search(r'^namespace (\S+) (HIDDEN |EXPORT )?\{', raw_line)
if m:
namespaces.append(m.group(1))
continue
diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh
index d8ec58b..1ebd7c9 100755
--- a/tools/golem/build-target.sh
+++ b/tools/golem/build-target.sh
@@ -269,6 +269,8 @@
execute lunch "$lunch_target"
# Golem uses master-art repository which is missing a lot of other libraries.
setenv SOONG_ALLOW_MISSING_DEPENDENCIES true
+ # master-art cannot build with Bazel.
+ setenv BUILD_BROKEN_DISABLE_BAZEL true
# Let the build system know we're not aiming to do a full platform build.
if [ ! -d frameworks/base ]; then
setenv TARGET_BUILD_UNBUNDLED true
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 3a0e625..f408c66 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -143,7 +143,7 @@
std::map<std::string, std::string> flags;
for (std::string line; std::getline(ifs, line);) {
- std::size_t comma = line.find(",");
+ std::size_t comma = line.find(',');
if (comma == std::string::npos) {
flags.emplace(line, "");
} else {
diff --git a/tools/jvmti-agents/chain-agents/chainagents.cc b/tools/jvmti-agents/chain-agents/chainagents.cc
index 1242409..d272fc1 100644
--- a/tools/jvmti-agents/chain-agents/chainagents.cc
+++ b/tools/jvmti-agents/chain-agents/chainagents.cc
@@ -53,7 +53,7 @@
OnLoad,
};
-static std::pair<std::string, std::string> Split(std::string source, char delim) {
+static std::pair<std::string, std::string> Split(const std::string& source, char delim) {
std::string first(source.substr(0, source.find(delim)));
if (source.find(delim) == std::string::npos) {
return std::pair(first, "");
diff --git a/tools/jvmti-agents/field-counts/fieldcount.cc b/tools/jvmti-agents/field-counts/fieldcount.cc
index c31a973..5a4b00e 100644
--- a/tools/jvmti-agents/field-counts/fieldcount.cc
+++ b/tools/jvmti-agents/field-counts/fieldcount.cc
@@ -182,7 +182,7 @@
<< "\t" << "<ALL_TYPES>"
<< "\t" << obj_len
<< "\t" << total_size;
- for (auto sz : class_sizes) {
+ for (const std::pair<std::string, size_t> sz : class_sizes) {
size_t count = class_counts[sz.first];
LOG(INFO) << "\t" << field_class_name << "." << field_name << ":" << field_sig
<< "\t" << sz.first
diff --git a/tools/jvmti-agents/simple-force-redefine/forceredefine.cc b/tools/jvmti-agents/simple-force-redefine/forceredefine.cc
index 055fb8a..3474238 100644
--- a/tools/jvmti-agents/simple-force-redefine/forceredefine.cc
+++ b/tools/jvmti-agents/simple-force-redefine/forceredefine.cc
@@ -94,7 +94,7 @@
jvmtiEnv* jvmti_;
};
-static void Transform(std::shared_ptr<ir::DexFile> ir) {
+static void Transform(const std::shared_ptr<ir::DexFile>& ir) {
std::unique_ptr<ir::Builder> builder;
for (auto& method : ir->encoded_methods) {
// Do not look into abstract/bridge/native/synthetic methods.
diff --git a/tools/jvmti-agents/simple-profile/simple_profile.cc b/tools/jvmti-agents/simple-profile/simple_profile.cc
index 5ead97e..7161142 100644
--- a/tools/jvmti-agents/simple-profile/simple_profile.cc
+++ b/tools/jvmti-agents/simple-profile/simple_profile.cc
@@ -26,6 +26,7 @@
#include <sstream>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "android-base/unique_fd.h"
@@ -55,7 +56,7 @@
SimpleProfileData(
jvmtiEnv* env, std::string out_fd_name, int fd, bool dump_on_shutdown, bool dump_on_main_stop)
: dump_id_(0),
- out_fd_name_(out_fd_name),
+ out_fd_name_(std::move(out_fd_name)),
out_fd_(fd),
shutdown_(false),
dump_on_shutdown_(dump_on_shutdown || dump_on_main_stop),
@@ -79,7 +80,7 @@
void Shutdown(jvmtiEnv* jvmti, JNIEnv* jni);
private:
- void DoDump(jvmtiEnv* jvmti, JNIEnv* jni, std::unordered_map<jmethodID, uint64_t> copy);
+ void DoDump(jvmtiEnv* jvmti, JNIEnv* jni, const std::unordered_map<jmethodID, uint64_t>& copy);
jlong dump_id_;
jrawMonitorID mon_;
@@ -320,7 +321,7 @@
void SimpleProfileData::DoDump(jvmtiEnv* jvmti,
JNIEnv* jni,
- std::unordered_map<jmethodID, uint64_t> copy) {
+ const std::unordered_map<jmethodID, uint64_t>& copy) {
std::ostringstream oss;
oss << "[";
bool is_first = true;
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 6e6ccb8..b5c220e 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -330,5 +330,11 @@
bug: 228441328,
names: ["tck.java.time",
"test.java.time"]
+},
+{
+ description: "Timing out after ojluni tests were enabled",
+ result: ERROR,
+ bug: 231439593,
+ names: ["org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime"]
}
]
diff --git a/tools/libcore_fugu_failures.txt b/tools/libcore_fugu_failures.txt
index 0fff814..60b43d0 100644
--- a/tools/libcore_fugu_failures.txt
+++ b/tools/libcore_fugu_failures.txt
@@ -25,6 +25,8 @@
names: [
"libcore.java.math.BigIntegerTest#test_Constructor_IILjava_util_Random",
"libcore.java.math.BigIntegerTest#test_probablePrime",
+ "libcore.java.util.UUIDTest#testJava11Implementation_invalidInputs",
+ "libcore.java.util.UUIDTest#testJava8Implementation_allowsLongInputs",
"libcore.javax.crypto.CipherInputStreamTest#testDecryptCorruptGCM",
"libcore.javax.crypto.CipherOutputStreamTest#testDecryptCorruptGCM",
"libcore.libcore.timezone.TelephonyLookupTest#createInstanceWithFallback",
@@ -112,7 +114,6 @@
"org.apache.harmony.crypto.tests.javax.crypto.func.CipherRSATest#test_RSANoPadding",
"org.apache.harmony.crypto.tests.javax.crypto.func.CipherRSATest#test_RSAShortKey",
"org.apache.harmony.crypto.tests.javax.crypto.func.KeyGeneratorFunctionalTest#test_",
- "org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime",
"org.apache.harmony.tests.java.math.BigIntegerTest#test_isProbablePrimeI",
"org.apache.harmony.tests.java.math.OldBigIntegerTest#test_ConstructorIILjava_util_Random",
"org.apache.harmony.tests.java.math.OldBigIntegerTest#test_isProbablePrimeI",
@@ -127,5 +128,109 @@
"org.apache.harmony.tests.javax.security.OldSHA1PRNGSecureRandomTest#testNextBytesbyteArray03",
"org.apache.harmony.tests.javax.security.OldSHA1PRNGSecureRandomTest#testSetSeedbyteArray02"
]
+},
+{
+ description: "Test using the getrandom() syscall, only available from Linux 3.17.",
+ result: ERROR,
+ bug: 141230711,
+ modes: [device],
+ names: [
+ "test.java.awt",
+ "test.java.io.ByteArrayInputStream",
+ "test.java.io.ByteArrayOutputStream",
+ "test.java.io.FileReader",
+ "test.java.io.FileWriter",
+ "test.java.io.InputStream",
+ "test.java.io.OutputStream",
+ "test.java.io.PrintStream",
+ "test.java.io.PrintWriter",
+ "test.java.io.Reader",
+ "test.java.io.Writer",
+ "test.java.lang.Boolean",
+ "test.java.lang.ClassLoader",
+ "test.java.lang.Double",
+ "test.java.lang.Float",
+ "test.java.lang.Integer",
+ "test.java.lang.Long",
+ "test.java.lang.StackWalker#main",
+ "test.java.lang.StrictMath.CubeRootTests",
+ "test.java.lang.StrictMath.ExactArithTests",
+ "test.java.lang.StrictMath.Expm1Tests",
+ "test.java.lang.StrictMath.ExpTests",
+ "test.java.lang.StrictMath.HyperbolicTests",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard1",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard2",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard3",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard4",
+ "test.java.lang.StrictMath.HypotTests#testHypot",
+ "test.java.lang.StrictMath.Log1pTests",
+ "test.java.lang.StrictMath.Log10Tests",
+ "test.java.lang.StrictMath.MultiplicationTests",
+ "test.java.lang.StrictMath.PowTests",
+ "test.java.lang.String",
+ "test.java.lang.Thread",
+ "test.java.lang.invoke",
+ "test.java.lang.ref.SoftReference",
+ "test.java.lang.ref.BasicTest",
+ "test.java.lang.ref.EnqueueNullRefTest",
+ "test.java.lang.ref.EnqueuePollRaceTest",
+ "test.java.lang.ref.ReferenceCloneTest",
+ "test.java.lang.ref.ReferenceEnqueuePendingTest",
+ "test.java.math.BigDecimal",
+ "test.java.math.BigInteger#testArithmetic",
+ "test.java.math.BigInteger#testBitCount",
+ "test.java.math.BigInteger#testBitLength",
+ "test.java.math.BigInteger#testbitOps",
+ "test.java.math.BigInteger#testBitwise",
+ "test.java.math.BigInteger#testByteArrayConv",
+ "test.java.math.BigInteger#testConstructor",
+ "test.java.math.BigInteger#testDivideAndReminder",
+ "test.java.math.BigInteger#testDivideLarge",
+ "test.java.math.BigInteger#testModExp",
+ "test.java.math.BigInteger#testMultiplyLarge",
+ "test.java.math.BigInteger#testNextProbablePrime",
+ "test.java.math.BigInteger#testPow",
+ "test.java.math.BigInteger#testSerialize",
+ "test.java.math.BigInteger#testShift",
+ "test.java.math.BigInteger#testSquare",
+ "test.java.math.BigInteger#testSquareLarge",
+ "test.java.math.BigInteger#testSquareRootAndReminder",
+ "test.java.math.BigInteger#testStringConv_generic",
+ "test.java.math.RoundingMode",
+ "test.java.net.DatagramSocket",
+ "test.java.net.Socket",
+ "test.java.net.SocketOptions",
+ "test.java.net.URLDecoder",
+ "test.java.net.URLEncoder",
+ "test.java.nio.channels.Channels",
+ "test.java.nio.channels.SelectionKey",
+ "test.java.nio.channels.Selector",
+ "test.java.nio.file",
+ "test.java.security.cert",
+ "test.java.security.KeyAgreement.KeyAgreementTest",
+ "test.java.security.KeyAgreement.KeySizeTest#testECDHKeySize",
+ "test.java.security.KeyAgreement.KeySpecTest",
+ "test.java.security.KeyAgreement.MultiThreadTest",
+ "test.java.security.KeyAgreement.NegativeTest",
+ "test.java.security.KeyStore",
+ "test.java.security.Provider",
+ "test.java.util.Arrays",
+ "test.java.util.Collection",
+ "test.java.util.Collections",
+ "test.java.util.Date",
+ "test.java.util.EnumMap",
+ "test.java.util.EnumSet",
+ "test.java.util.GregorianCalendar",
+ "test.java.util.LinkedHashMap",
+ "test.java.util.LinkedHashSet",
+ "test.java.util.List",
+ "test.java.util.Map",
+ "test.java.util.Optional",
+ "test.java.util.TimeZone",
+ "test.java.util.concurrent",
+ "test.java.util.function",
+ "test.java.util.stream",
+ "test.java.util.zip.ZipFile"
+ ]
}
]
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 2193189..c931610 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -50,7 +50,6 @@
"org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testConsequentProxyConnection",
"org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_removeJ",
"org.apache.harmony.tests.java.lang.ProcessManagerTest#testSleep",
- "org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime",
"org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
"org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"
]
@@ -71,5 +70,11 @@
names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
"jsr166.CompletableFutureTest#testDelayedExecutor"
]
+},
+{
+ description: "SocketTimeout test gcstress and debug.",
+ result: EXEC_FAILED,
+ bug: 259530489,
+ names: ["org.apache.harmony.luni.tests.java.net.URLConnectionTest#test_setReadTimeoutI_SocketTimeoutException"]
}
]
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index 55bba72..81d6ca0 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -36,7 +36,6 @@
"libcore.java.util.stream.CollectorsTest#counting_largeStream",
"org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_remove",
"org.apache.harmony.tests.java.lang.String2Test#test_getBytes",
- "org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime",
"org.apache.harmony.tests.java.text.DateFormatTest#test_getAvailableLocales",
"org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
"org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"]
diff --git a/tools/luci/config/generated/cr-buildbucket.cfg b/tools/luci/config/generated/cr-buildbucket.cfg
index e4a5923..1da398c 100644
--- a/tools/luci/config/generated/cr-buildbucket.cfg
+++ b/tools/luci/config/generated/cr-buildbucket.cfg
@@ -17,7 +17,8 @@
builders {
name: "angler-armv7-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv7-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -35,13 +36,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv7-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv7-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -59,13 +61,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv7-non-gen-cc"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv7-non-gen-cc"
+ dimensions: "device_type:oriole"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -83,13 +86,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv8-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv8-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -107,13 +111,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv8-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv8-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -131,13 +136,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv8-non-gen-cc"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv8-non-gen-cc"
+ dimensions: "device_type:oriole"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -155,13 +161,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "bullhead-armv7-gcstress-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:bullhead-armv7-gcstress-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -179,13 +186,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "bullhead-armv8-gcstress-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:bullhead-armv8-gcstress-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -203,13 +211,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "bullhead-armv8-gcstress-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:bullhead-armv8-gcstress-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -227,13 +236,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "fugu-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:fugu-debug"
+ dimensions: "device_type:fugu"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -251,13 +261,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "fugu-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:fugu-ndebug"
+ dimensions: "device_type:fugu"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -275,13 +286,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-cms"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-cms"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -299,13 +310,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -323,13 +334,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-gcstress-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-gcstress-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -347,13 +358,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-ndebug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -371,13 +382,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-poison-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -395,13 +406,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-cdex-fast"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-cdex-fast"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -419,13 +430,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-cms"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-cms"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -443,13 +454,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -467,13 +478,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-ndebug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -491,13 +502,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-non-gen-cc"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-non-gen-cc"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -515,13 +526,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-poison-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -539,13 +550,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "walleye-armv7-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:walleye-armv7-poison-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -563,13 +575,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "walleye-armv8-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:walleye-armv8-poison-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -587,13 +600,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "walleye-armv8-poison-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:walleye-armv8-poison-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -611,7 +625,7 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
}
diff --git a/tools/luci/config/generated/luci-scheduler.cfg b/tools/luci/config/generated/luci-scheduler.cfg
index d6c6ab5..ba7bcee 100644
--- a/tools/luci/config/generated/luci-scheduler.cfg
+++ b/tools/luci/config/generated/luci-scheduler.cfg
@@ -356,6 +356,40 @@
refs: "regexp:refs/heads/master-art"
}
}
+trigger {
+ id: "vogar"
+ realm: "ci"
+ acl_sets: "ci"
+ triggers: "angler-armv7-debug"
+ triggers: "angler-armv7-ndebug"
+ triggers: "angler-armv7-non-gen-cc"
+ triggers: "angler-armv8-debug"
+ triggers: "angler-armv8-ndebug"
+ triggers: "angler-armv8-non-gen-cc"
+ triggers: "bullhead-armv7-gcstress-ndebug"
+ triggers: "bullhead-armv8-gcstress-debug"
+ triggers: "bullhead-armv8-gcstress-ndebug"
+ triggers: "fugu-debug"
+ triggers: "fugu-ndebug"
+ triggers: "host-x86-cms"
+ triggers: "host-x86-debug"
+ triggers: "host-x86-gcstress-debug"
+ triggers: "host-x86-ndebug"
+ triggers: "host-x86-poison-debug"
+ triggers: "host-x86_64-cdex-fast"
+ triggers: "host-x86_64-cms"
+ triggers: "host-x86_64-debug"
+ triggers: "host-x86_64-ndebug"
+ triggers: "host-x86_64-non-gen-cc"
+ triggers: "host-x86_64-poison-debug"
+ triggers: "walleye-armv7-poison-debug"
+ triggers: "walleye-armv8-poison-debug"
+ triggers: "walleye-armv8-poison-ndebug"
+ gitiles {
+ repo: "https://android.googlesource.com/platform/external/vogar"
+ refs: "regexp:refs/heads/master"
+ }
+}
acl_sets {
name: "ci"
acls {
diff --git a/tools/luci/config/generated/project.cfg b/tools/luci/config/generated/project.cfg
index 4844ab4..f9f7877 100644
--- a/tools/luci/config/generated/project.cfg
+++ b/tools/luci/config/generated/project.cfg
@@ -7,7 +7,7 @@
name: "art"
access: "group:all"
lucicfg {
- version: "1.30.11"
+ version: "1.33.7"
package_dir: ".."
config_dir: "generated"
entry_point: "main.star"
diff --git a/tools/luci/config/main.star b/tools/luci/config/main.star
index f4ad488..b1ecde1 100755
--- a/tools/luci/config/main.star
+++ b/tools/luci/config/main.star
@@ -23,7 +23,7 @@
lucicfg.check_version("1.30.9", "Please update depot_tools")
luci.builder.defaults.experiments.set({
- "luci.recipes.use_python3": 10,
+ "luci.recipes.use_python3": 100,
})
# Use LUCI Scheduler BBv2 names and add Scheduler realms configs.
@@ -141,13 +141,20 @@
)
luci.gitiles_poller(
+ name = "vogar",
+ bucket = "ci",
+ repo = "https://android.googlesource.com/platform/external/vogar",
+ refs = ["refs/heads/master"],
+)
+
+luci.gitiles_poller(
name = "manifest",
bucket = "ci",
repo = "https://android.googlesource.com/platform/manifest",
refs = ["refs/heads/master-art"],
)
-def ci_builder(name, category, short_name):
+def ci_builder(name, category, short_name, dimensions):
luci.builder(
name = name,
bucket = "ci",
@@ -156,11 +163,8 @@
cipd_version = "refs/heads/main",
name = "art",
),
- dimensions = {
+ dimensions = dimensions | {
"pool": "luci.art.ci",
-
- # Some builders require specific hardware, so we make the assignment in bots.cfg
- "builder": name,
},
service_account = "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com",
@@ -185,6 +189,7 @@
"art",
"libcore",
"manifest",
+ "vogar",
],
)
luci.console_view_entry(
@@ -194,28 +199,37 @@
short_name = short_name,
)
-ci_builder("angler-armv7-debug", "angler|armv7", "dbg")
-ci_builder("angler-armv7-non-gen-cc", "angler|armv7", "ngen")
-ci_builder("angler-armv7-ndebug", "angler|armv7", "ndbg")
-ci_builder("angler-armv8-debug", "angler|armv8", "dbg")
-ci_builder("angler-armv8-non-gen-cc", "angler|armv8", "ngen")
-ci_builder("angler-armv8-ndebug", "angler|armv8", "ndbg")
-ci_builder("bullhead-armv7-gcstress-ndebug", "bullhead|armv7|gcstress", "dbg")
-ci_builder("bullhead-armv8-gcstress-debug", "bullhead|armv8|gcstress", "dbg")
-ci_builder("bullhead-armv8-gcstress-ndebug", "bullhead|armv8|gcstress", "ndbg")
-ci_builder("fugu-debug", "fugu", "dbg")
-ci_builder("fugu-ndebug", "fugu", "ndbg")
-ci_builder("host-x86-cms", "host|x86", "cms")
-ci_builder("host-x86-debug", "host|x86", "dbg")
-ci_builder("host-x86-ndebug", "host|x86", "ndbg")
-ci_builder("host-x86-gcstress-debug", "host|x86", "gcs")
-ci_builder("host-x86-poison-debug", "host|x86", "psn")
-ci_builder("host-x86_64-cdex-fast", "host|x64", "cdx")
-ci_builder("host-x86_64-cms", "host|x64", "cms")
-ci_builder("host-x86_64-debug", "host|x64", "dbg")
-ci_builder("host-x86_64-non-gen-cc", "host|x64", "ngen")
-ci_builder("host-x86_64-ndebug", "host|x64", "ndbg")
-ci_builder("host-x86_64-poison-debug", "host|x64", "psn")
-ci_builder("walleye-armv7-poison-debug", "walleye|armv7|poison", "dbg")
-ci_builder("walleye-armv8-poison-debug", "walleye|armv8|poison", "dbg")
-ci_builder("walleye-armv8-poison-ndebug", "walleye|armv8|poison", "ndbg")
+# Dimensions specify which bots we can run on.
+host_dims = {"os": "Linux"}
+target_dims = {"os": "Android"}
+arm_target_dims = target_dims | {"device_type": "bonito|oriole|walleye"}
+x86_target_dims = target_dims | {"device_type": "fugu"}
+
+# userfault-GC configurations must be run on Pixel 6.
+userfault_gc_target_dims = target_dims | {"device_type": "oriole"}
+
+ci_builder("angler-armv7-debug", "angler|armv7", "dbg", arm_target_dims)
+ci_builder("angler-armv7-non-gen-cc", "angler|armv7", "ngen", userfault_gc_target_dims)
+ci_builder("angler-armv7-ndebug", "angler|armv7", "ndbg", arm_target_dims)
+ci_builder("angler-armv8-debug", "angler|armv8", "dbg", arm_target_dims)
+ci_builder("angler-armv8-non-gen-cc", "angler|armv8", "ngen", userfault_gc_target_dims)
+ci_builder("angler-armv8-ndebug", "angler|armv8", "ndbg", arm_target_dims)
+ci_builder("bullhead-armv7-gcstress-ndebug", "bullhead|armv7|gcstress", "dbg", arm_target_dims)
+ci_builder("bullhead-armv8-gcstress-debug", "bullhead|armv8|gcstress", "dbg", arm_target_dims)
+ci_builder("bullhead-armv8-gcstress-ndebug", "bullhead|armv8|gcstress", "ndbg", arm_target_dims)
+ci_builder("fugu-debug", "fugu", "dbg", x86_target_dims)
+ci_builder("fugu-ndebug", "fugu", "ndbg", x86_target_dims)
+ci_builder("host-x86-cms", "host|x86", "cms", host_dims)
+ci_builder("host-x86-debug", "host|x86", "dbg", host_dims)
+ci_builder("host-x86-ndebug", "host|x86", "ndbg", host_dims)
+ci_builder("host-x86-gcstress-debug", "host|x86", "gcs", host_dims)
+ci_builder("host-x86-poison-debug", "host|x86", "psn", host_dims)
+ci_builder("host-x86_64-cdex-fast", "host|x64", "cdx", host_dims)
+ci_builder("host-x86_64-cms", "host|x64", "cms", host_dims)
+ci_builder("host-x86_64-debug", "host|x64", "dbg", host_dims)
+ci_builder("host-x86_64-non-gen-cc", "host|x64", "ngen", host_dims)
+ci_builder("host-x86_64-ndebug", "host|x64", "ndbg", host_dims)
+ci_builder("host-x86_64-poison-debug", "host|x64", "psn", host_dims)
+ci_builder("walleye-armv7-poison-debug", "walleye|armv7|poison", "dbg", arm_target_dims)
+ci_builder("walleye-armv8-poison-debug", "walleye|armv8|poison", "dbg", arm_target_dims)
+ci_builder("walleye-armv8-poison-ndebug", "walleye|armv8|poison", "ndbg", arm_target_dims)
diff --git a/tools/prebuilt_libjdwp_art_failures.txt b/tools/prebuilt_libjdwp_art_failures.txt
index ee59315..4ded7d5 100644
--- a/tools/prebuilt_libjdwp_art_failures.txt
+++ b/tools/prebuilt_libjdwp_art_failures.txt
@@ -106,5 +106,5 @@
result: EXEC_FAILED,
bug: 69169846,
name: "org.apache.harmony.jpda.tests.jdwp.DDM_DDMTest#testChunk001"
-},
+}
]
diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt
index e23cf2c..9b0dc68 100644
--- a/tools/public.libraries.buildbot.txt
+++ b/tools/public.libraries.buildbot.txt
@@ -1,6 +1,6 @@
-libbacktrace.so
libc.so
libc++.so
libdl.so
libm.so
libnativehelper.so
+libunwindstack.so
diff --git a/tools/run-gtests.sh b/tools/run-gtests.sh
index 21064c1..0f333f6 100755
--- a/tools/run-gtests.sh
+++ b/tools/run-gtests.sh
@@ -59,9 +59,17 @@
options="$@"
+run_in_chroot() {
+ if [ -n $ART_TEST_ON_VM ]; then
+ $ART_SSH_CMD $ART_CHROOT_CMD $@
+ else
+ "$adb" shell chroot "$ART_TEST_CHROOT" $@
+ fi
+}
+
if [[ ${#tests[@]} -eq 0 ]]; then
# Search for executables under the `bin/art` directory of the ART APEX.
- readarray -t tests <<<$("$adb" shell chroot "$ART_TEST_CHROOT" \
+ readarray -t tests <<<$(run_in_chroot \
find "$android_art_root/bin/art" -type f -perm /ugo+x | sort)
fi
@@ -69,7 +77,7 @@
for t in ${tests[@]}; do
echo "$t"
- "$adb" shell chroot "$ART_TEST_CHROOT" \
+ run_in_chroot \
env ANDROID_ART_ROOT="$android_art_root" \
ANDROID_I18N_ROOT="$android_i18n_root" \
ANDROID_TZDATA_ROOT="$android_tzdata_root" \
diff --git a/tools/run-libcore-tests.py b/tools/run-libcore-tests.py
index 1e6070a..c6958f1 100755
--- a/tools/run-libcore-tests.py
+++ b/tools/run-libcore-tests.py
@@ -42,6 +42,7 @@
help='Enable GC stress configuration (device|host only).')
parser.add_argument('tests', nargs="*",
help='Name(s) of the test(s) to run')
+ parser.add_argument('--verbose', action='store_true', help='Print verbose output from vogar.')
return parser.parse_args()
ART_TEST_ANDROID_ROOT = os.environ.get("ART_TEST_ANDROID_ROOT", "/system")
@@ -91,7 +92,10 @@
"libcore.sun.util",
"libcore.xml",
"org.apache.harmony.annotation",
- "org.apache.harmony.luni",
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnection",
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnection",
+ "org.apache.harmony.luni.tests.java.io",
+ "org.apache.harmony.luni.tests.java.net",
"org.apache.harmony.nio",
"org.apache.harmony.regex",
"org.apache.harmony.testframework",
@@ -122,7 +126,8 @@
"test.java.lang.Long",
# Sharded test.java.lang.StrictMath
"test.java.lang.StrictMath.CubeRootTests",
- "test.java.lang.StrictMath.ExactArithTests",
+ # TODO: disable the test until b/248208762 is fixed.
+ # "test.java.lang.StrictMath.ExactArithTests",
"test.java.lang.StrictMath.Expm1Tests",
"test.java.lang.StrictMath.ExpTests",
"test.java.lang.StrictMath.HyperbolicTests",
@@ -159,21 +164,15 @@
"test.java.math.BigInteger#testDivideAndReminder",
"test.java.math.BigInteger#testDivideLarge",
"test.java.math.BigInteger#testModExp",
- "test.java.math.BigInteger#testModInv",
"test.java.math.BigInteger#testMultiplyLarge",
"test.java.math.BigInteger#testNextProbablePrime",
"test.java.math.BigInteger#testPow",
- "test.java.math.BigInteger#testPrime",
"test.java.math.BigInteger#testSerialize",
"test.java.math.BigInteger#testShift",
"test.java.math.BigInteger#testSquare",
"test.java.math.BigInteger#testSquareLarge",
- "test.java.math.BigInteger#testSquareRoot",
"test.java.math.BigInteger#testSquareRootAndReminder",
"test.java.math.BigInteger#testStringConv_generic",
- "test.java.math.BigInteger#testStringConv_schoenhage_threshold_pow0",
- "test.java.math.BigInteger#testStringConv_schoenhage_threshold_pow1",
- "test.java.math.BigInteger#testStringConv_schoenhage_threshold_pow2",
"test.java.math.RoundingMode",
# test.java.net
"test.java.net.DatagramSocket",
@@ -190,7 +189,6 @@
"test.java.security.cert",
# Sharded test.java.security.KeyAgreement
"test.java.security.KeyAgreement.KeyAgreementTest",
- "test.java.security.KeyAgreement.KeySizeTest#testDHKeySize",
"test.java.security.KeyAgreement.KeySizeTest#testECDHKeySize",
"test.java.security.KeyAgreement.KeySpecTest",
"test.java.security.KeyAgreement.MultiThreadTest",
@@ -242,6 +240,130 @@
CLASSPATH = ["core-tests", "core-ojtests", "jsr166-tests", "mockito-target"]
+SLOW_OJLUNI_TESTS = {
+ "test.java.awt",
+ "test.java.lang.String",
+ "test.java.lang.invoke",
+ "test.java.nio.channels.Selector",
+ "test.java.time",
+ "test.java.util.Arrays",
+ "test.java.util.Map",
+ "test.java.util.concurrent",
+ "test.java.util.stream",
+ "test.java.util.zip.ZipFile",
+ "tck.java.time",
+}
+
+# Disabled to unblock art-buildbot
+# These tests fail with "java.io.IOException: Stream closed", tracked in
+# http://b/235566533 and http://b/208639267
+DISABLED_GCSTRESS_DEBUG_TESTS = {
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard1",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard2",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard3",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard4",
+ "test.java.math.BigDecimal",
+ "test.java.math.BigInteger#testConstructor",
+}
+
+DISABLED_FUGU_TESTS = {
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnection",
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnection",
+ "test.java.awt",
+ "test.java.io.ByteArrayInputStream",
+ "test.java.io.ByteArrayOutputStream",
+ "test.java.io.InputStream",
+ "test.java.io.OutputStream",
+ "test.java.io.PrintStream",
+ "test.java.io.PrintWriter",
+ "test.java.io.Reader",
+ "test.java.io.Writer",
+ "test.java.lang.Boolean",
+ "test.java.lang.ClassLoader",
+ "test.java.lang.Double",
+ "test.java.lang.Float",
+ "test.java.lang.Integer",
+ "test.java.lang.Long",
+ "test.java.lang.StrictMath.CubeRootTests",
+ "test.java.lang.StrictMath.Expm1Tests",
+ "test.java.lang.StrictMath.ExpTests",
+ "test.java.lang.StrictMath.HyperbolicTests",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard1",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard2",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard3",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard4",
+ "test.java.lang.StrictMath.HypotTests#testHypot",
+ "test.java.lang.StrictMath.Log1pTests",
+ "test.java.lang.StrictMath.Log10Tests",
+ "test.java.lang.StrictMath.MultiplicationTests",
+ "test.java.lang.StrictMath.PowTests",
+ "test.java.lang.String",
+ "test.java.lang.Thread",
+ "test.java.lang.invoke",
+ "test.java.lang.ref.SoftReference",
+ "test.java.lang.ref.BasicTest",
+ "test.java.lang.ref.EnqueueNullRefTest",
+ "test.java.lang.ref.EnqueuePollRaceTest",
+ "test.java.lang.ref.ReferenceCloneTest",
+ "test.java.lang.ref.ReferenceEnqueuePendingTest",
+ "test.java.math.BigDecimal",
+ "test.java.math.BigInteger#testArithmetic",
+ "test.java.math.BigInteger#testBitCount",
+ "test.java.math.BigInteger#testBitLength",
+ "test.java.math.BigInteger#testbitOps",
+ "test.java.math.BigInteger#testBitwise",
+ "test.java.math.BigInteger#testByteArrayConv",
+ "test.java.math.BigInteger#testConstructor",
+ "test.java.math.BigInteger#testDivideAndReminder",
+ "test.java.math.BigInteger#testDivideLarge",
+ "test.java.math.BigInteger#testModExp",
+ "test.java.math.BigInteger#testMultiplyLarge",
+ "test.java.math.BigInteger#testNextProbablePrime",
+ "test.java.math.BigInteger#testPow",
+ "test.java.math.BigInteger#testSerialize",
+ "test.java.math.BigInteger#testShift",
+ "test.java.math.BigInteger#testSquare",
+ "test.java.math.BigInteger#testSquareLarge",
+ "test.java.math.BigInteger#testSquareRootAndReminder",
+ "test.java.math.BigInteger#testStringConv_generic",
+ "test.java.math.RoundingMode",
+ "test.java.net.DatagramSocket",
+ "test.java.net.Socket",
+ "test.java.net.SocketOptions",
+ "test.java.net.URLDecoder",
+ "test.java.net.URLEncoder",
+ "test.java.nio.channels.Channels",
+ "test.java.nio.channels.SelectionKey",
+ "test.java.nio.channels.Selector",
+ "test.java.nio.file",
+ "test.java.security.cert",
+ "test.java.security.KeyAgreement.KeyAgreementTest",
+ "test.java.security.KeyAgreement.KeySizeTest#testECDHKeySize",
+ "test.java.security.KeyAgreement.KeySpecTest",
+ "test.java.security.KeyAgreement.MultiThreadTest",
+ "test.java.security.KeyAgreement.NegativeTest",
+ "test.java.security.KeyStore",
+ "test.java.security.Provider",
+ "test.java.time",
+ "test.java.util.Arrays",
+ "test.java.util.Collection",
+ "test.java.util.Collections",
+ "test.java.util.Date",
+ "test.java.util.EnumMap",
+ "test.java.util.EnumSet",
+ "test.java.util.GregorianCalendar",
+ "test.java.util.LinkedHashMap",
+ "test.java.util.LinkedHashSet",
+ "test.java.util.List",
+ "test.java.util.Map",
+ "test.java.util.Optional",
+ "test.java.util.TestFormatter",
+ "test.java.util.TimeZone",
+ "test.java.util.function",
+ "test.java.util.stream",
+ "tck.java.time",
+}
+
def get_jar_filename(classpath):
base_path = (ANDROID_PRODUCT_OUT + "/../..") if ANDROID_PRODUCT_OUT else "out/target"
base_path = os.path.normpath(base_path) # Normalize ".." components for readability.
@@ -275,6 +397,13 @@
# See b/78228743 and b/178351808.
if args.gcstress or args.debug or args.mode == "jvm":
test_names = list(t for t in test_names if not t.startswith("libcore.highmemorytest"))
+ test_names = list(filter(lambda x: x not in SLOW_OJLUNI_TESTS, test_names))
+ if args.gcstress and args.debug:
+ test_names = list(filter(lambda x: x not in DISABLED_GCSTRESS_DEBUG_TESTS, test_names))
+ if not args.getrandom:
+ # Disable libcore.highmemorytest due to limited ram on fugu. http://b/258173036
+ test_names = list(filter(lambda x: x not in DISABLED_FUGU_TESTS and
+ not x.startswith("libcore.highmemorytest"), test_names))
return test_names
def get_vogar_command(test_name):
@@ -282,6 +411,7 @@
if args.mode == "device":
cmd.append("--mode=device --vm-arg -Ximage:/system/framework/art_boot_images/boot.art")
cmd.append("--vm-arg -Xbootclasspath:" + ":".join(BOOT_CLASSPATH))
+
if args.mode == "host":
# We explicitly give a wrong path for the image, to ensure vogar
# will create a boot image with the default compiler. Note that
@@ -298,11 +428,16 @@
if args.debug:
cmd.append("--vm-arg -XXlib:libartd.so --vm-arg -XX:SlowDebug=true")
+ # The only device in go/art-buildbot without getrandom is fugu. We limit the amount of memory
+ # per runtime for fugu to avoid low memory killer, fugu has 4-cores 1GB RAM (b/258171768).
+ if not args.getrandom:
+ cmd.append("--vm-arg -Xmx128M")
+
if args.mode == "device":
if ART_TEST_CHROOT:
cmd.append(f"--chroot {ART_TEST_CHROOT} --device-dir=/tmp/vogar/test-{test_name}")
else:
- cmd.append("--device-dir=/data/local/tmp/vogar/test-{test_name}")
+ cmd.append(f"--device-dir=/data/local/tmp/vogar/test-{test_name}")
cmd.append(f"--vm-command={ART_TEST_ANDROID_ROOT}/bin/art")
else:
cmd.append(f"--device-dir=/tmp/vogar/test-{test_name}")
@@ -314,6 +449,9 @@
cmd.append("--vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken")
cmd.append("--vm-arg -Xusejit:{}".format(str(args.jit).lower()))
+ if args.verbose:
+ cmd.append("--verbose")
+
# Suppress color codes if not attached to a terminal
if not sys.stdout.isatty():
cmd.append("--no-color")
@@ -370,7 +508,7 @@
print(f"Running {len(futures)} tasks on {args.jobs} core(s)...\n")
for i, future in enumerate(concurrent.futures.as_completed(futures)):
test_name, cmd, stdout, exit_code = future.result()
- if exit_code != 0 or args.dry_run:
+ if exit_code != 0 or args.dry_run or args.verbose:
print(cmd)
print(stdout.strip())
else:
diff --git a/tools/signal_dumper/Android.bp b/tools/signal_dumper/Android.bp
index 33450d1..00948b8 100644
--- a/tools/signal_dumper/Android.bp
+++ b/tools/signal_dumper/Android.bp
@@ -48,18 +48,6 @@
},
}
-cc_defaults {
- name: "signal_dumper_libbacktrace_static_deps",
- defaults: [
- "signal_dumper_libbase_static_deps",
- "signal_dumper_libunwindstack_static_deps",
- ],
- static_libs: [
- "libbase",
- "libunwindstack",
- ],
-}
-
art_cc_binary {
name: "signal_dumper",
@@ -73,14 +61,14 @@
defaults: [
"art_defaults",
- "signal_dumper_libbacktrace_static_deps",
"signal_dumper_libbase_static_deps",
+ "signal_dumper_libunwindstack_static_deps",
],
srcs: ["signal_dumper.cc"],
static_libs: [
- "libbacktrace",
"libbase",
+ "libunwindstack",
],
}
diff --git a/tools/signal_dumper/signal_dumper.cc b/tools/signal_dumper/signal_dumper.cc
index e9a589e..e88ac19 100644
--- a/tools/signal_dumper/signal_dumper.cc
+++ b/tools/signal_dumper/signal_dumper.cc
@@ -15,6 +15,7 @@
*/
#include <dirent.h>
+#include <inttypes.h>
#include <poll.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
@@ -38,8 +39,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
+#include <unwindstack/AndroidUnwinder.h>
namespace art {
namespace {
@@ -492,11 +492,10 @@
constexpr bool kIs64Bit = false;
#endif
-void DumpThread(pid_t pid,
+void DumpThread(unwindstack::AndroidRemoteUnwinder& unwinder, pid_t pid,
pid_t tid,
const std::string* addr2line_path,
- const char* prefix,
- BacktraceMap* map) {
+ const char* prefix) {
LOG(ERROR) << std::endl << "=== pid: " << pid << " tid: " << tid << " ===" << std::endl;
constexpr uint32_t kMaxWaitMicros = 1000 * 1000; // 1s.
@@ -504,50 +503,41 @@
LOG(ERROR) << "Failed to wait for sigstop on " << tid;
}
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
- if (backtrace == nullptr) {
- LOG(ERROR) << prefix << "(failed to create Backtrace for thread " << tid << ")";
- return;
- }
- backtrace->SetSkipFrames(false);
- if (!backtrace->Unwind(0, nullptr)) {
- LOG(ERROR) << prefix << "(backtrace::Unwind failed for thread " << tid
- << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")";
- return;
- }
- if (backtrace->NumFrames() == 0) {
- LOG(ERROR) << prefix << "(no native stack frames for thread " << tid << ")";
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(tid, data)) {
+ LOG(ERROR) << prefix << "(Unwind failed for thread " << tid << ": "
+ << data.GetErrorString() << ")";
return;
}
std::unique_ptr<addr2line::Addr2linePipe> addr2line_state;
-
- for (Backtrace::const_iterator it = backtrace->begin();
- it != backtrace->end(); ++it) {
+ data.DemangleFunctionNames();
+ for (const unwindstack::FrameData& frame : data.frames) {
std::ostringstream oss;
- oss << prefix << StringPrintf("#%02zu pc ", it->num);
+ oss << prefix << StringPrintf("#%02zu pc ", frame.num);
bool try_addr2line = false;
- if (!BacktraceMap::IsValid(it->map)) {
- oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " ???" : "%08" PRIx64 " ???", it->pc);
+ if (frame.map_info == nullptr) {
+ oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " ???" : "%08" PRIx64 " ???", frame.pc);
} else {
- oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " " : "%08" PRIx64 " ", it->rel_pc);
- if (it->map.name.empty()) {
- oss << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start);
+ oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " " : "%08" PRIx64 " ", frame.rel_pc);
+ if (frame.map_info->name().empty()) {
+ oss << StringPrintf("<anonymous:%" PRIx64 ">", frame.map_info->start());
} else {
- oss << it->map.name;
+ oss << frame.map_info->name().c_str();
}
- if (it->map.offset != 0) {
- oss << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
+ if (frame.map_info->offset() != 0) {
+ oss << StringPrintf(" (offset %" PRIx64 ")", frame.map_info->offset());
}
oss << " (";
- if (!it->func_name.empty()) {
- oss << it->func_name;
- if (it->func_offset != 0) {
- oss << "+" << it->func_offset;
+ const std::string& function_name = frame.function_name;
+ if (!function_name.empty()) {
+ oss << function_name;
+ if (frame.function_offset != 0) {
+ oss << "+" << frame.function_offset;
}
// Functions found using the gdb jit interface will be in an empty
// map that cannot be found using addr2line.
- if (!it->map.name.empty()) {
+ if (!frame.map_info->name().empty()) {
try_addr2line = true;
}
} else {
@@ -558,8 +548,8 @@
LOG(ERROR) << oss.str() << std::endl;
if (try_addr2line && addr2line_path != nullptr) {
addr2line::Addr2line(*addr2line_path,
- it->map.name,
- it->rel_pc,
+ frame.map_info->name(),
+ frame.rel_pc,
LOG_STREAM(ERROR),
prefix,
&addr2line_state);
@@ -593,14 +583,9 @@
LOG(ERROR) << "Did not receive SIGSTOP for pid " << forked_pid;
}
- std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(forked_pid));
- if (backtrace_map == nullptr) {
- LOG(ERROR) << "Could not create BacktraceMap";
- return;
- }
-
+ unwindstack::AndroidRemoteUnwinder unwinder(forked_pid);
for (pid_t tid : tids) {
- DumpThread(forked_pid, tid, addr2line_path.get(), " ", backtrace_map.get());
+ DumpThread(unwinder, forked_pid, tid, addr2line_path.get(), " ");
}
}
diff --git a/tools/tracefast-plugin/tracefast.cc b/tools/tracefast-plugin/tracefast.cc
index 618742d..734dce0 100644
--- a/tools/tracefast-plugin/tracefast.cc
+++ b/tools/tracefast-plugin/tracefast.cc
@@ -60,7 +60,6 @@
override REQUIRES_SHARED(art::Locks::mutator_lock_) { }
void MethodUnwind(art::Thread* thread ATTRIBUTE_UNUSED,
- art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
art::ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED)
override REQUIRES_SHARED(art::Locks::mutator_lock_) { }
diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk
index f7c8d50..9ea9b3a 100644
--- a/tools/veridex/Android.mk
+++ b/tools/veridex/Android.mk
@@ -23,14 +23,14 @@
system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex
$(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
-$(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX)
+$(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(D8)
$(transform-classes.jar-to-dex)
$(call declare-1p-target,$(system_stub_dex),art)
oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex
$(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
-$(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX)
+$(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(D8)
$(transform-classes.jar-to-dex)
$(call declare-1p-target,$(oahl_stub_dex),art)
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
index 2a8b8a0..6a4d351 100644
--- a/tools/veridex/flow_analysis.cc
+++ b/tools/veridex/flow_analysis.cc
@@ -540,7 +540,7 @@
case Instruction::FILLED_NEW_ARRAY: {
dex::TypeIndex type_index(instruction.VRegB_35c());
VeriClass* cls = resolver_->GetVeriClass(type_index);
- UpdateRegister(instruction.VRegA_22c(), cls);
+ UpdateRegister(instruction.VRegA_35c(), cls);
break;
}
@@ -602,7 +602,7 @@
if (VeriClass::sdkInt_ != nullptr && resolver_->GetField(field_index) == VeriClass::sdkInt_) {
UpdateRegister(dest_reg, gTargetSdkVersion, VeriClass::integer_);
} else {
- UpdateRegister(dest_reg, GetFieldType(instruction.VRegC_22c()));
+ UpdateRegister(dest_reg, GetFieldType(field_index));
}
break;
}